mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
Enhance supplier management and reconciliation functionalities
Adds new fields to the supplier schema (email, address, automatic reconciliation) and updates the UI to display these. Refactors the BL reconciliation page to use tabs for manual and potentially future automatic reconciliation, and updates data fetching logic. Replit-Commit-Author: Agent Replit-Commit-Session-Id: b163d4c0-de5e-4f4e-a9c0-aed4c7049718 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/b163d4c0-de5e-4f4e-a9c0-aed4c7049718/odrk4d7
This commit is contained in:
1 parent
42d46ac0e2
commit
f7891cda01
4 files changed
+1579
-954
No files matched your search
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+108
-18
@@ -20,7 +20,8 @@ import {
|
||||
Package,
|
||||
Truck,
|
||||
CheckCircle,
|
||||
XCircle
|
||||
XCircle,
|
||||
Clock
|
||||
} from "lucide-react";
|
||||
import type { Supplier } from "@shared/schema";
|
||||
|
||||
@@ -37,7 +38,10 @@ export default function Suppliers() {
|
||||
name: "",
|
||||
contact: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
address: "",
|
||||
hasDlc: false,
|
||||
automaticReconciliation: false,
|
||||
});
|
||||
|
||||
const { data: suppliers = [], isLoading } = useQuery<Supplier[]>({
|
||||
@@ -66,7 +70,15 @@ export default function Suppliers() {
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ['/api/suppliers'] });
|
||||
setShowCreateModal(false);
|
||||
setFormData({ name: "", contact: "", phone: "", hasDlc: false });
|
||||
setFormData({
|
||||
name: "",
|
||||
contact: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
address: "",
|
||||
hasDlc: false,
|
||||
automaticReconciliation: false
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
if (isUnauthorizedError(error)) {
|
||||
@@ -137,7 +149,15 @@ export default function Suppliers() {
|
||||
|
||||
setShowEditModal(false);
|
||||
setSelectedSupplier(null);
|
||||
setFormData({ name: "", contact: "", phone: "", hasDlc: false });
|
||||
setFormData({
|
||||
name: "",
|
||||
contact: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
address: "",
|
||||
hasDlc: false,
|
||||
automaticReconciliation: false
|
||||
});
|
||||
},
|
||||
onError: (error, variables, context) => {
|
||||
// Rollback optimiste en cas d'erreur
|
||||
@@ -214,7 +234,15 @@ export default function Suppliers() {
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
setFormData({ name: "", contact: "", phone: "", hasDlc: false });
|
||||
setFormData({
|
||||
name: "",
|
||||
contact: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
address: "",
|
||||
hasDlc: false,
|
||||
automaticReconciliation: false
|
||||
});
|
||||
setShowCreateModal(true);
|
||||
};
|
||||
|
||||
@@ -224,7 +252,10 @@ export default function Suppliers() {
|
||||
name: supplier.name,
|
||||
contact: supplier.contact || "",
|
||||
phone: supplier.phone || "",
|
||||
email: supplier.email || "",
|
||||
address: supplier.address || "",
|
||||
hasDlc: supplier.hasDlc || false,
|
||||
automaticReconciliation: supplier.automaticReconciliation || false,
|
||||
});
|
||||
setShowEditModal(true);
|
||||
};
|
||||
@@ -400,19 +431,30 @@ export default function Suppliers() {
|
||||
{supplier.phone}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center text-sm">
|
||||
{supplier.hasDlc ? (
|
||||
<div className="flex items-center text-green-600">
|
||||
<CheckCircle className="w-4 h-4 mr-2" />
|
||||
DLC activé
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center text-gray-400">
|
||||
<XCircle className="w-4 h-4 mr-2" />
|
||||
DLC désactivé
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center justify-between text-sm">
|
||||
<div className="flex items-center space-x-2">
|
||||
{supplier.hasDlc && (
|
||||
<div className="flex items-center text-green-600 bg-green-50 px-2 py-1 rounded-full">
|
||||
<Clock className="w-3 h-3 mr-1" />
|
||||
<span className="text-xs font-medium">DLC</span>
|
||||
</div>
|
||||
)}
|
||||
{supplier.automaticReconciliation && (
|
||||
<div className="flex items-center text-blue-600 bg-blue-50 px-2 py-1 rounded-full">
|
||||
<CheckCircle className="w-3 h-3 mr-1" />
|
||||
<span className="text-xs font-medium">Auto-rapprochement</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!supplier.hasDlc && !supplier.automaticReconciliation && (
|
||||
<div className="text-xs text-gray-500 mt-2">
|
||||
Aucune option activée
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Statistics section follows */}
|
||||
</div>
|
||||
|
||||
<div className="border-t pt-4">
|
||||
@@ -453,7 +495,15 @@ export default function Suppliers() {
|
||||
setShowCreateModal(false);
|
||||
setShowEditModal(false);
|
||||
setSelectedSupplier(null);
|
||||
setFormData({ name: "", contact: "", phone: "", hasDlc: false });
|
||||
setFormData({
|
||||
name: "",
|
||||
contact: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
address: "",
|
||||
hasDlc: false,
|
||||
automaticReconciliation: false
|
||||
});
|
||||
}}>
|
||||
<DialogContent className="sm:max-w-md" aria-describedby="supplier-modal-description">
|
||||
<DialogHeader>
|
||||
@@ -497,6 +547,27 @@ export default function Suppliers() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) => handleChange('email', e.target.value)}
|
||||
placeholder="Adresse email"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Label htmlFor="address">Adresse</Label>
|
||||
<Input
|
||||
id="address"
|
||||
value={formData.address}
|
||||
onChange={(e) => handleChange('address', e.target.value)}
|
||||
placeholder="Adresse complète"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="hasDlc"
|
||||
@@ -508,6 +579,17 @@ export default function Suppliers() {
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="automaticReconciliation"
|
||||
checked={formData.automaticReconciliation}
|
||||
onCheckedChange={(checked) => handleChange('automaticReconciliation', checked === true)}
|
||||
/>
|
||||
<Label htmlFor="automaticReconciliation" className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
Rapprochement automatique BL/Factures
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3 pt-4">
|
||||
<Button
|
||||
type="button"
|
||||
@@ -516,7 +598,15 @@ export default function Suppliers() {
|
||||
setShowCreateModal(false);
|
||||
setShowEditModal(false);
|
||||
setSelectedSupplier(null);
|
||||
setFormData({ name: "", contact: "", phone: "", hasDlc: false });
|
||||
setFormData({
|
||||
name: "",
|
||||
contact: "",
|
||||
phone: "",
|
||||
email: "",
|
||||
address: "",
|
||||
hasDlc: false,
|
||||
automaticReconciliation: false
|
||||
});
|
||||
}}
|
||||
>
|
||||
Annuler
|
||||
|
||||
@@ -74,7 +74,10 @@ export const suppliers = pgTable("suppliers", {
|
||||
name: varchar("name").notNull(),
|
||||
contact: varchar("contact"),
|
||||
phone: varchar("phone"),
|
||||
email: varchar("email"),
|
||||
address: text("address"),
|
||||
hasDlc: boolean("has_dlc").default(false), // Coche DLC pour la gestion DLC
|
||||
automaticReconciliation: boolean("automatic_reconciliation").default(false), // Rapprochement automatique BL/Factures
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
updatedAt: timestamp("updated_at").defaultNow(),
|
||||
});
|
||||
@@ -470,6 +473,70 @@ export const insertTaskSchema = createInsertSchema(tasks).omit({
|
||||
dueDate: z.coerce.date().optional().nullable(), // Convertit automatiquement les chaînes en Date
|
||||
});
|
||||
|
||||
// Types
|
||||
export type User = typeof users.$inferSelect;
|
||||
export type UpsertUser = z.infer<typeof insertUserSchema>;
|
||||
export type Group = typeof groups.$inferSelect;
|
||||
export type InsertGroup = z.infer<typeof insertGroupSchema>;
|
||||
export type Supplier = typeof suppliers.$inferSelect;
|
||||
export type InsertSupplier = z.infer<typeof insertSupplierSchema>;
|
||||
export type Order = typeof orders.$inferSelect;
|
||||
export type InsertOrder = z.infer<typeof insertOrderSchema>;
|
||||
export type Delivery = typeof deliveries.$inferSelect;
|
||||
export type InsertDelivery = z.infer<typeof insertDeliverySchema>;
|
||||
export type UserGroup = typeof userGroups.$inferSelect;
|
||||
export type InsertUserGroup = z.infer<typeof insertUserGroupSchema>;
|
||||
export type Publicity = typeof publicities.$inferSelect;
|
||||
export type InsertPublicity = z.infer<typeof insertPublicitySchema>;
|
||||
export type PublicityParticipation = typeof publicityParticipations.$inferSelect;
|
||||
export type InsertPublicityParticipation = z.infer<typeof insertPublicityParticipationSchema>;
|
||||
export type NocodbConfig = typeof nocodbConfig.$inferSelect;
|
||||
export type InsertNocodbConfig = z.infer<typeof insertNocodbConfigSchema>;
|
||||
export type CustomerOrder = typeof customerOrders.$inferSelect;
|
||||
export type InsertCustomerOrder = z.infer<typeof insertCustomerOrderSchema>;
|
||||
export type DlcProduct = typeof dlcProducts.$inferSelect;
|
||||
export type InsertDlcProduct = z.infer<typeof insertDlcProductSchema>;
|
||||
export type DlcProductFrontend = Omit<DlcProduct, 'expiryDate'> & { dlcDate: Date };
|
||||
export type InsertDlcProductFrontend = z.infer<typeof insertDlcProductFrontendSchema>;
|
||||
export type Task = typeof tasks.$inferSelect;
|
||||
export type InsertTask = z.infer<typeof insertTaskSchema>;
|
||||
|
||||
// Complex types with relations
|
||||
export type UserWithGroups = User & {
|
||||
groups: Group[];
|
||||
};
|
||||
|
||||
export type OrderWithRelations = Order & {
|
||||
supplier: Supplier;
|
||||
group: Group;
|
||||
creator: User;
|
||||
};
|
||||
|
||||
export type DeliveryWithRelations = Delivery & {
|
||||
supplier: Supplier;
|
||||
group: Group;
|
||||
creator: User;
|
||||
order?: Order;
|
||||
};
|
||||
|
||||
export type PublicityWithRelations = Publicity & {
|
||||
creator: User;
|
||||
participations: (PublicityParticipation & { group: Group })[];
|
||||
};
|
||||
|
||||
export type CustomerOrderWithRelations = CustomerOrder & {
|
||||
supplier: Supplier;
|
||||
group: Group;
|
||||
creator: User;
|
||||
};
|
||||
|
||||
export type DlcProductWithRelations = DlcProduct & {
|
||||
supplier: Supplier;
|
||||
group: Group;
|
||||
creator: User;
|
||||
validator?: User;
|
||||
};
|
||||
|
||||
// Types
|
||||
export type UpsertUser = z.infer<typeof insertUserSchema>;
|
||||
export type User = typeof users.$inferSelect;
|
||||
|
||||
Reference in new issue
Block a user