mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
5 files changed
+794
-544
No files matched your search
@@ -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"
|
||||
|
||||
|
||||
+337
-249
File diff suppressed because it is too large.
Load diff
+338
-250
File diff suppressed because it is too large.
Load diff
@@ -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
@@ -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[]> {
|
||||
|
||||
Reference in new issue
Block a user