feat: Add functionality to move inventory items to pantry and enhance pantry management
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
- Implemented moveInventoryItemToPantry method in InventoryRepository to facilitate moving items from inventory to pantry. - Enhanced InventoryScreen with a new header section providing context about the inventory. - Added a button in SwipeableInventoryTile to move items to pantry with appropriate error handling. - Introduced movePantryItemToInventory method in PantryRepository to support moving items back to inventory. - Refactored PantryScreen to rename _addToInventory to _moveToInventory for clarity and updated UI to reflect changes. - Added AdminPantryItem model to represent pantry items in the admin panel. - Created AdminPantryPanel for managing pantry items, including moving items to inventory and listing users. - Developed AdminPrivateProductsPanel for managing private products, allowing promotion to global products.
This commit is contained in:
@@ -79,6 +79,12 @@ export class ProductsController {
|
||||
return this.productsService.findPending();
|
||||
}
|
||||
|
||||
@Roles('admin')
|
||||
@Get('private')
|
||||
findPrivate() {
|
||||
return this.productsService.findPrivate();
|
||||
}
|
||||
|
||||
@Roles('admin')
|
||||
@Post('ai-categorize-bulk')
|
||||
@Throttle({ default: { ttl: 60_000, limit: 5 } })
|
||||
@@ -202,6 +208,15 @@ export class ProductsController {
|
||||
return this.productsService.update(id, body);
|
||||
}
|
||||
|
||||
@Roles('admin')
|
||||
@Post('private/:id/promote')
|
||||
promotePrivateToGlobal(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Request() req: { user: { id: number } },
|
||||
) {
|
||||
return this.productsService.promotePrivateToGlobal(id, req.user.id);
|
||||
}
|
||||
|
||||
@Roles('admin')
|
||||
@Delete(':id/permanent')
|
||||
permanentDelete(@Param('id', ParseIntPipe) id: number) {
|
||||
|
||||
@@ -435,6 +435,17 @@ export class ProductsService {
|
||||
});
|
||||
}
|
||||
|
||||
async findPrivate() {
|
||||
return this.prisma.product.findMany({
|
||||
where: { isPrivate: true, isActive: true },
|
||||
include: {
|
||||
categoryRef: { include: { parent: true } },
|
||||
owner: { select: { id: true, username: true } },
|
||||
},
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
}
|
||||
|
||||
async createPending(data: CreateProductDto, userId: number) {
|
||||
const name = data.name.trim();
|
||||
const normalizedName = normalizeName(name);
|
||||
@@ -470,6 +481,67 @@ export class ProductsService {
|
||||
return this.prisma.product.update({ where: { id }, data: { status } });
|
||||
}
|
||||
|
||||
async promotePrivateToGlobal(productId: number, adminUserId: number) {
|
||||
const source = await this.prisma.product.findUnique({
|
||||
where: { id: productId },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
canonicalName: true,
|
||||
normalizedName: true,
|
||||
categoryId: true,
|
||||
isPrivate: true,
|
||||
isActive: true,
|
||||
ownerId: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!source) {
|
||||
throw new NotFoundException(`Product with id ${productId} not found`);
|
||||
}
|
||||
|
||||
if (!source.isPrivate) {
|
||||
throw new ForbiddenException('Endast privata produkter kan promoveras till global produkt');
|
||||
}
|
||||
|
||||
const name = (source.canonicalName ?? source.name).trim();
|
||||
const normalizedName = normalizeName(name);
|
||||
|
||||
const existingGlobal = await this.prisma.product.findUnique({
|
||||
where: { normalizedName },
|
||||
});
|
||||
|
||||
if (existingGlobal && existingGlobal.id !== source.id) {
|
||||
if (!existingGlobal.isActive) {
|
||||
return this.prisma.product.update({
|
||||
where: { id: existingGlobal.id },
|
||||
data: {
|
||||
isActive: true,
|
||||
deletedAt: null,
|
||||
name,
|
||||
canonicalName: name,
|
||||
categoryId: source.categoryId ?? existingGlobal.categoryId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return existingGlobal;
|
||||
}
|
||||
|
||||
return this.prisma.product.create({
|
||||
data: {
|
||||
name,
|
||||
normalizedName,
|
||||
canonicalName: name,
|
||||
isActive: true,
|
||||
isPrivate: false,
|
||||
ownerId: adminUserId,
|
||||
deletedAt: null,
|
||||
...(source.categoryId != null ? { categoryId: source.categoryId } : {}),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// ── Privata produkter (användare kan hantera sina egna) ──────────────────────
|
||||
// Hjälpfunktioner för att undvika kodduplicering mellan admin och user-scope
|
||||
|
||||
|
||||
Reference in New Issue
Block a user