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
+794 -544

No files matched your search

+8
View File
@@ -15,6 +15,10 @@ run = ["npm", "run", "start"]
localPort = 5000
externalPort = 80
[[ports]]
localPort = 33121
externalPort = 4200
[[ports]]
localPort = 33873
externalPort = 3003
@@ -35,6 +39,10 @@ externalPort = 5000
localPort = 41343
externalPort = 3001
[[ports]]
localPort = 44549
externalPort = 5173
[workflows]
runButton = "Project"
+24 -4
View File
@@ -965,17 +965,37 @@ export default function BLReconciliation() {
</Button>
{!delivery.reconciled ? (
<>
{permissions.canValidate('reconciliation') && (
{permissions.canValidate('reconciliation') && verificationResults[delivery.id]?.exists === true ? (
<Button
variant="outline"
size="sm"
onClick={() => handleQuickValidate(delivery)}
className="h-8 w-8 p-0"
title="Valider le rapprochement"
className="h-8 w-8 p-0 text-green-600 hover:text-green-700"
title="Valider le rapprochement (facture vérifiée)"
>
<Check className="w-4 h-4" />
</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
variant="outline"
size="sm"
+87 -41
View File
@@ -1993,31 +1993,57 @@ export class DatabaseStorage implements IStorage {
}
async markDlcProductAsProcessed(id: number, processedBy: string): Promise<DlcProductFrontend> {
const [dlcProduct] = await db
.update(dlcProducts)
.set({
processedAt: new Date(),
processedBy: processedBy,
processedUntilExpiry: true,
updatedAt: new Date()
})
.where(eq(dlcProducts.id, id))
.returning();
return dlcProduct as DlcProductFrontend;
try {
// Essayer avec les nouveaux champs si ils existent
const [dlcProduct] = await db
.update(dlcProducts)
.set({
processedAt: new Date(),
processedBy: processedBy,
processedUntilExpiry: true,
updatedAt: new Date()
})
.where(eq(dlcProducts.id, id))
.returning();
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> {
const [dlcProduct] = await db
.update(dlcProducts)
.set({
processedAt: null,
processedBy: null,
processedUntilExpiry: false,
updatedAt: new Date()
})
.where(eq(dlcProducts.id, id))
.returning();
return dlcProduct as DlcProductFrontend;
try {
// Essayer avec les nouveaux champs si ils existent
const [dlcProduct] = await db
.update(dlcProducts)
.set({
processedAt: null,
processedBy: null,
processedUntilExpiry: false,
updatedAt: new Date()
})
.where(eq(dlcProducts.id, id))
.returning();
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; }> {
@@ -2030,26 +2056,46 @@ export class DatabaseStorage implements IStorage {
whereCondition = inArray(dlcProducts.groupId, groupIds);
}
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]}
AND (${dlcProducts.processedUntilExpiry} IS NULL OR ${dlcProducts.processedUntilExpiry} = false)
THEN 1 END)`,
expired: sql<number>`COUNT(CASE
WHEN ${dlcProducts.expiryDate} <= ${today.toISOString().split('T')[0]}
AND (${dlcProducts.processedUntilExpiry} IS NULL OR ${dlcProducts.processedUntilExpiry} = false OR ${dlcProducts.expiryDate} < ${today.toISOString().split('T')[0]})
THEN 1 END)`
})
.from(dlcProducts)
.where(whereCondition);
// Version rétrocompatible - fonctionne avec ou sans les nouveaux champs
try {
// Essayer d'abord avec 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]}
AND (${dlcProducts.processedUntilExpiry} IS NULL OR ${dlcProducts.processedUntilExpiry} = false)
THEN 1 END)`,
expired: sql<number>`COUNT(CASE
WHEN ${dlcProducts.expiryDate} <= ${today.toISOString().split('T')[0]}
AND (${dlcProducts.processedUntilExpiry} IS NULL OR ${dlcProducts.processedUntilExpiry} = false OR ${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
};
return {
active: stats.active || 0,
expiringSoon: stats.expiringSoon || 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[]> {