Merge pull request #469 from R0m1k3/1.1

1.1
This commit is contained in:
LogiFlow authored and GitHub committed 2025-09-27 11:31:47 +02:00
commit 42349bd01e
5 files changed
+753 -503

No files matched your search

+8
View File
@@ -15,6 +15,10 @@ run = ["npm", "run", "start"]
localPort = 5000 localPort = 5000
externalPort = 80 externalPort = 80
[[ports]]
localPort = 33121
externalPort = 4200
[[ports]] [[ports]]
localPort = 33873 localPort = 33873
externalPort = 3003 externalPort = 3003
@@ -35,6 +39,10 @@ externalPort = 5000
localPort = 41343 localPort = 41343
externalPort = 3001 externalPort = 3001
[[ports]]
localPort = 44549
externalPort = 5173
[workflows] [workflows]
runButton = "Project" runButton = "Project"
+24 -4
View File
@@ -965,17 +965,37 @@ export default function BLReconciliation() {
</Button> </Button>
{!delivery.reconciled ? ( {!delivery.reconciled ? (
<> <>
{permissions.canValidate('reconciliation') && ( {permissions.canValidate('reconciliation') && verificationResults[delivery.id]?.exists === true ? (
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => handleQuickValidate(delivery)} onClick={() => handleQuickValidate(delivery)}
className="h-8 w-8 p-0" className="h-8 w-8 p-0 text-green-600 hover:text-green-700"
title="Valider le rapprochement" title="Valider le rapprochement (facture vérifiée)"
> >
<Check className="w-4 h-4" /> <Check className="w-4 h-4" />
</Button> </Button>
)} ) : permissions.canValidate('reconciliation') && verificationResults[delivery.id]?.exists === false ? (
<Button
variant="outline"
size="sm"
disabled
className="h-8 w-8 p-0 text-gray-400 cursor-not-allowed"
title="Validation impossible : la facture n'a pas été trouvée (coche rouge). Vérifiez la référence de facture."
>
<Check className="w-4 h-4" />
</Button>
) : permissions.canValidate('reconciliation') ? (
<Button
variant="outline"
size="sm"
disabled
className="h-8 w-8 p-0 text-gray-400 cursor-not-allowed"
title="Validation impossible : veuillez d'abord vérifier la facture en cliquant sur l'icône de recherche"
>
<Check className="w-4 h-4" />
</Button>
) : null}
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
+46
View File
@@ -1993,6 +1993,8 @@ export class DatabaseStorage implements IStorage {
} }
async markDlcProductAsProcessed(id: number, processedBy: string): Promise<DlcProductFrontend> { async markDlcProductAsProcessed(id: number, processedBy: string): Promise<DlcProductFrontend> {
try {
// Essayer avec les nouveaux champs si ils existent
const [dlcProduct] = await db const [dlcProduct] = await db
.update(dlcProducts) .update(dlcProducts)
.set({ .set({
@@ -2004,9 +2006,22 @@ export class DatabaseStorage implements IStorage {
.where(eq(dlcProducts.id, id)) .where(eq(dlcProducts.id, id))
.returning(); .returning();
return dlcProduct as DlcProductFrontend; return dlcProduct as DlcProductFrontend;
} catch (error) {
// Fallback : juste mettre à jour la date de modification
const [dlcProduct] = await db
.update(dlcProducts)
.set({
updatedAt: new Date()
})
.where(eq(dlcProducts.id, id))
.returning();
return dlcProduct as DlcProductFrontend;
}
} }
async unmarkDlcProductAsProcessed(id: number): Promise<DlcProductFrontend> { async unmarkDlcProductAsProcessed(id: number): Promise<DlcProductFrontend> {
try {
// Essayer avec les nouveaux champs si ils existent
const [dlcProduct] = await db const [dlcProduct] = await db
.update(dlcProducts) .update(dlcProducts)
.set({ .set({
@@ -2018,6 +2033,17 @@ export class DatabaseStorage implements IStorage {
.where(eq(dlcProducts.id, id)) .where(eq(dlcProducts.id, id))
.returning(); .returning();
return dlcProduct as DlcProductFrontend; return dlcProduct as DlcProductFrontend;
} catch (error) {
// Fallback : juste mettre à jour la date de modification
const [dlcProduct] = await db
.update(dlcProducts)
.set({
updatedAt: new Date()
})
.where(eq(dlcProducts.id, id))
.returning();
return dlcProduct as DlcProductFrontend;
}
} }
async getDlcStats(groupIds?: number[]): Promise<{ active: number; expiringSoon: number; expired: number; }> { async getDlcStats(groupIds?: number[]): Promise<{ active: number; expiringSoon: number; expired: number; }> {
@@ -2030,6 +2056,9 @@ export class DatabaseStorage implements IStorage {
whereCondition = inArray(dlcProducts.groupId, groupIds); whereCondition = inArray(dlcProducts.groupId, groupIds);
} }
// Version rétrocompatible - fonctionne avec ou sans les nouveaux champs
try {
// Essayer d'abord avec les nouveaux champs
const [stats] = await db const [stats] = await db
.select({ .select({
active: sql<number>`COUNT(CASE WHEN ${dlcProducts.expiryDate} > ${today.toISOString().split('T')[0]} THEN 1 END)`, active: sql<number>`COUNT(CASE WHEN ${dlcProducts.expiryDate} > ${today.toISOString().split('T')[0]} THEN 1 END)`,
@@ -2050,6 +2079,23 @@ export class DatabaseStorage implements IStorage {
expiringSoon: stats.expiringSoon || 0, expiringSoon: stats.expiringSoon || 0,
expired: stats.expired || 0 expired: stats.expired || 0
}; };
} catch (error) {
// Fallback pour les bases de données sans les nouveaux champs
const [stats] = await db
.select({
active: sql<number>`COUNT(CASE WHEN ${dlcProducts.expiryDate} > ${today.toISOString().split('T')[0]} THEN 1 END)`,
expiringSoon: sql<number>`COUNT(CASE WHEN ${dlcProducts.expiryDate} BETWEEN ${today.toISOString().split('T')[0]} AND ${alertDate.toISOString().split('T')[0]} THEN 1 END)`,
expired: sql<number>`COUNT(CASE WHEN ${dlcProducts.expiryDate} <= ${today.toISOString().split('T')[0]} THEN 1 END)`
})
.from(dlcProducts)
.where(whereCondition);
return {
active: stats.active || 0,
expiringSoon: stats.expiringSoon || 0,
expired: stats.expired || 0
};
}
} }
async getTasks(groupIds?: number[], userRole?: string): Promise<TaskWithRelations[]> { async getTasks(groupIds?: number[], userRole?: string): Promise<TaskWithRelations[]> {