mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-12 01:37:40 +02:00
Add functionality to mark and unmark products as processed
This commit introduces new mutations and UI logic to the DLC page for marking products as processed or unmarking them. It also includes updates to icons and database schema for announcements and avoirs. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 1e4433b5-47ab-464c-b663-fea2e53367dd Replit-Commit-Checkpoint-Type: intermediate_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/1e4433b5-47ab-464c-b663-fea2e53367dd/px42AkO
This commit is contained in:
1 parent
6a715955c0
commit
f1f2cf5fc4
6 files changed
+624
-262
No files matched your search
@@ -2051,6 +2051,91 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
});
|
||||
|
||||
// Route pour marquer un produit DLC comme traité temporairement (expire bientôt) - accessible à tous
|
||||
app.put('/api/dlc-products/:id/mark-processed', isAuthenticated, async (req: any, res) => {
|
||||
try {
|
||||
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" });
|
||||
}
|
||||
|
||||
const id = parseInt(req.params.id);
|
||||
const dlcProduct = await storage.getDlcProduct(id);
|
||||
|
||||
if (!dlcProduct) {
|
||||
return res.status(404).json({ message: "DLC Product not found" });
|
||||
}
|
||||
|
||||
// For non-admin users, check if they have access to the product's group
|
||||
if (user.role !== 'admin') {
|
||||
const userGroupIds = user.userGroups.map(ug => ug.groupId);
|
||||
if (!userGroupIds.includes(dlcProduct.groupId)) {
|
||||
return res.status(403).json({ message: "Access denied to this group's DLC products" });
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🔍 DLC Product mark as processed attempt:', {
|
||||
userId: user.id,
|
||||
userRole: user.role,
|
||||
dlcProductId: id,
|
||||
dlcGroupId: dlcProduct.groupId
|
||||
});
|
||||
|
||||
const processedProduct = await storage.markDlcProductAsProcessed(id, user.id);
|
||||
console.log('✅ DLC Product marked as processed by:', user.role, user.id);
|
||||
|
||||
res.json(processedProduct);
|
||||
} catch (error) {
|
||||
console.error("Error marking DLC product as processed:", error);
|
||||
res.status(500).json({ message: "Failed to mark DLC product as processed" });
|
||||
}
|
||||
});
|
||||
|
||||
// Route pour annuler le traitement temporaire d'un produit DLC - réservé aux admins, directeurs et managers
|
||||
app.put('/api/dlc-products/:id/unmark-processed', isAuthenticated, async (req: any, res) => {
|
||||
try {
|
||||
const user = await storage.getUserWithGroups(req.user.claims ? req.user.claims.sub : req.user.id);
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: "User not found" });
|
||||
}
|
||||
|
||||
// Check if user has permission to unmark processed (admin, directeur, manager)
|
||||
if (!['admin', 'directeur', 'manager'].includes(user.role)) {
|
||||
return res.status(403).json({ message: "Insufficient permissions to unmark DLC product as processed" });
|
||||
}
|
||||
|
||||
const id = parseInt(req.params.id);
|
||||
const dlcProduct = await storage.getDlcProduct(id);
|
||||
|
||||
if (!dlcProduct) {
|
||||
return res.status(404).json({ message: "DLC Product not found" });
|
||||
}
|
||||
|
||||
// For non-admin users, check if they have access to the product's group
|
||||
if (user.role !== 'admin') {
|
||||
const userGroupIds = user.userGroups.map(ug => ug.groupId);
|
||||
if (!userGroupIds.includes(dlcProduct.groupId)) {
|
||||
return res.status(403).json({ message: "Access denied to this group's DLC products" });
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🔍 DLC Product unmark processed attempt:', {
|
||||
userId: user.id,
|
||||
userRole: user.role,
|
||||
dlcProductId: id,
|
||||
dlcGroupId: dlcProduct.groupId
|
||||
});
|
||||
|
||||
const unprocessedProduct = await storage.unmarkDlcProductAsProcessed(id);
|
||||
console.log('✅ DLC Product unmarked as processed by:', user.role, user.id);
|
||||
|
||||
res.json(unprocessedProduct);
|
||||
} catch (error) {
|
||||
console.error("Error unmarking DLC product as processed:", error);
|
||||
res.status(500).json({ message: "Failed to unmark DLC product as processed" });
|
||||
}
|
||||
});
|
||||
|
||||
// Tasks routes
|
||||
app.get('/api/tasks', isAuthenticated, async (req: any, res) => {
|
||||
try {
|
||||
|
||||
+77
-4
@@ -193,6 +193,8 @@ export interface IStorage {
|
||||
validateDlcProduct(id: number, validatedBy: string): Promise<DlcProductFrontend>;
|
||||
markDlcProductStockEpuise(id: number, markedBy: string): Promise<DlcProductFrontend>;
|
||||
restoreDlcProductStock(id: number): Promise<DlcProductFrontend>;
|
||||
markDlcProductAsProcessed(id: number, processedBy: string): Promise<DlcProductFrontend>;
|
||||
unmarkDlcProductAsProcessed(id: number): Promise<DlcProductFrontend>;
|
||||
getDlcStats(groupIds?: number[]): Promise<{ active: number; expiringSoon: number; expired: number; }>;
|
||||
|
||||
// Task operations
|
||||
@@ -1990,6 +1992,34 @@ export class DatabaseStorage implements IStorage {
|
||||
return dlcProduct as DlcProductFrontend;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
async getDlcStats(groupIds?: number[]): Promise<{ active: number; expiringSoon: number; expired: number; }> {
|
||||
const today = new Date();
|
||||
const alertDate = new Date();
|
||||
@@ -2003,8 +2033,14 @@ export class DatabaseStorage implements IStorage {
|
||||
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)`
|
||||
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);
|
||||
@@ -4096,6 +4132,36 @@ export class MemStorage implements IStorage {
|
||||
return { ...updatedProduct, dlcDate: new Date(updatedProduct.expiryDate) } as DlcProductFrontend;
|
||||
}
|
||||
|
||||
async markDlcProductAsProcessed(id: number, processedBy: string): Promise<DlcProductFrontend> {
|
||||
const existingProduct = this.dlcProducts.get(id);
|
||||
if (!existingProduct) throw new Error('DLC Product not found');
|
||||
|
||||
const updatedProduct = {
|
||||
...existingProduct,
|
||||
processedAt: new Date(),
|
||||
processedBy: processedBy,
|
||||
processedUntilExpiry: true,
|
||||
updatedAt: new Date()
|
||||
};
|
||||
this.dlcProducts.set(id, updatedProduct);
|
||||
return { ...updatedProduct, dlcDate: new Date(updatedProduct.expiryDate) } as DlcProductFrontend;
|
||||
}
|
||||
|
||||
async unmarkDlcProductAsProcessed(id: number): Promise<DlcProductFrontend> {
|
||||
const existingProduct = this.dlcProducts.get(id);
|
||||
if (!existingProduct) throw new Error('DLC Product not found');
|
||||
|
||||
const updatedProduct = {
|
||||
...existingProduct,
|
||||
processedAt: null,
|
||||
processedBy: null,
|
||||
processedUntilExpiry: false,
|
||||
updatedAt: new Date()
|
||||
};
|
||||
this.dlcProducts.set(id, updatedProduct);
|
||||
return { ...updatedProduct, dlcDate: new Date(updatedProduct.expiryDate) } as DlcProductFrontend;
|
||||
}
|
||||
|
||||
async getDlcStats(groupIds?: number[]): Promise<{ active: number; expiringSoon: number; expired: number; }> {
|
||||
const today = new Date();
|
||||
const alertDate = new Date();
|
||||
@@ -4111,9 +4177,16 @@ export class MemStorage implements IStorage {
|
||||
active: products.filter(p => new Date(p.expiryDate) > today).length,
|
||||
expiringSoon: products.filter(p => {
|
||||
const expiry = new Date(p.expiryDate);
|
||||
return expiry >= today && expiry <= alertDate;
|
||||
const isExpiringSoon = expiry >= today && expiry <= alertDate;
|
||||
const isProcessed = p.processedUntilExpiry && expiry > today;
|
||||
return isExpiringSoon && !isProcessed;
|
||||
}).length,
|
||||
expired: products.filter(p => new Date(p.expiryDate) <= today).length
|
||||
expired: products.filter(p => {
|
||||
const expiry = new Date(p.expiryDate);
|
||||
const isExpired = expiry <= today;
|
||||
const isProcessed = p.processedUntilExpiry && expiry > today;
|
||||
return isExpired && !isProcessed;
|
||||
}).length
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user