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:
michaelschal committed 2025-08-12 10:08:57 +00:00
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
View File
@@ -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
+67
View File
@@ -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;