mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-23 06:58:19 +02:00
5 files changed
+794
-544
No files matched your search
@@ -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"
|
||||||
|
|
||||||
|
|||||||
+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>
|
</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"
|
||||||
|
|||||||
+87
-41
@@ -1993,31 +1993,57 @@ export class DatabaseStorage implements IStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async markDlcProductAsProcessed(id: number, processedBy: string): Promise<DlcProductFrontend> {
|
async markDlcProductAsProcessed(id: number, processedBy: string): Promise<DlcProductFrontend> {
|
||||||
const [dlcProduct] = await db
|
try {
|
||||||
.update(dlcProducts)
|
// Essayer avec les nouveaux champs si ils existent
|
||||||
.set({
|
const [dlcProduct] = await db
|
||||||
processedAt: new Date(),
|
.update(dlcProducts)
|
||||||
processedBy: processedBy,
|
.set({
|
||||||
processedUntilExpiry: true,
|
processedAt: new Date(),
|
||||||
updatedAt: new Date()
|
processedBy: processedBy,
|
||||||
})
|
processedUntilExpiry: true,
|
||||||
.where(eq(dlcProducts.id, id))
|
updatedAt: new Date()
|
||||||
.returning();
|
})
|
||||||
return dlcProduct as DlcProductFrontend;
|
.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> {
|
async unmarkDlcProductAsProcessed(id: number): Promise<DlcProductFrontend> {
|
||||||
const [dlcProduct] = await db
|
try {
|
||||||
.update(dlcProducts)
|
// Essayer avec les nouveaux champs si ils existent
|
||||||
.set({
|
const [dlcProduct] = await db
|
||||||
processedAt: null,
|
.update(dlcProducts)
|
||||||
processedBy: null,
|
.set({
|
||||||
processedUntilExpiry: false,
|
processedAt: null,
|
||||||
updatedAt: new Date()
|
processedBy: null,
|
||||||
})
|
processedUntilExpiry: false,
|
||||||
.where(eq(dlcProducts.id, id))
|
updatedAt: new Date()
|
||||||
.returning();
|
})
|
||||||
return dlcProduct as DlcProductFrontend;
|
.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; }> {
|
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);
|
whereCondition = inArray(dlcProducts.groupId, groupIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
const [stats] = await db
|
// Version rétrocompatible - fonctionne avec ou sans les nouveaux champs
|
||||||
.select({
|
try {
|
||||||
active: sql<number>`COUNT(CASE WHEN ${dlcProducts.expiryDate} > ${today.toISOString().split('T')[0]} THEN 1 END)`,
|
// Essayer d'abord avec les nouveaux champs
|
||||||
expiringSoon: sql<number>`COUNT(CASE
|
const [stats] = await db
|
||||||
WHEN ${dlcProducts.expiryDate} BETWEEN ${today.toISOString().split('T')[0]} AND ${alertDate.toISOString().split('T')[0]}
|
.select({
|
||||||
AND (${dlcProducts.processedUntilExpiry} IS NULL OR ${dlcProducts.processedUntilExpiry} = false)
|
active: sql<number>`COUNT(CASE WHEN ${dlcProducts.expiryDate} > ${today.toISOString().split('T')[0]} THEN 1 END)`,
|
||||||
THEN 1 END)`,
|
expiringSoon: sql<number>`COUNT(CASE
|
||||||
expired: sql<number>`COUNT(CASE
|
WHEN ${dlcProducts.expiryDate} BETWEEN ${today.toISOString().split('T')[0]} AND ${alertDate.toISOString().split('T')[0]}
|
||||||
WHEN ${dlcProducts.expiryDate} <= ${today.toISOString().split('T')[0]}
|
AND (${dlcProducts.processedUntilExpiry} IS NULL OR ${dlcProducts.processedUntilExpiry} = false)
|
||||||
AND (${dlcProducts.processedUntilExpiry} IS NULL OR ${dlcProducts.processedUntilExpiry} = false OR ${dlcProducts.expiryDate} < ${today.toISOString().split('T')[0]})
|
THEN 1 END)`,
|
||||||
THEN 1 END)`
|
expired: sql<number>`COUNT(CASE
|
||||||
})
|
WHEN ${dlcProducts.expiryDate} <= ${today.toISOString().split('T')[0]}
|
||||||
.from(dlcProducts)
|
AND (${dlcProducts.processedUntilExpiry} IS NULL OR ${dlcProducts.processedUntilExpiry} = false OR ${dlcProducts.expiryDate} < ${today.toISOString().split('T')[0]})
|
||||||
.where(whereCondition);
|
THEN 1 END)`
|
||||||
|
})
|
||||||
|
.from(dlcProducts)
|
||||||
|
.where(whereCondition);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
active: stats.active || 0,
|
active: stats.active || 0,
|
||||||
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[]> {
|
||||||
|
|||||||
Reference in new issue
Block a user