feat: add item-level order confirmation/decline support

Per Letzshop API, each inventory unit must be confirmed/declined individually.
This enables partial confirmation (some items confirmed, others declined).

Admin API endpoints:
- POST /vendors/{id}/orders/{id}/confirm - confirm all items
- POST /vendors/{id}/orders/{id}/reject - decline all items
- POST /vendors/{id}/orders/{id}/items/{id}/confirm - confirm single item
- POST /vendors/{id}/orders/{id}/items/{id}/decline - decline single item

Order detail modal now shows:
- Product name, EAN, SKU, MPN, price per item
- Per-item state badge (unconfirmed/confirmed/declined)
- Per-item confirm/decline buttons for pending items
- Bulk confirm/decline all buttons

Order status logic:
- If all items declined -> order is "declined"
- If any item confirmed -> order is "confirmed"
- Partial confirmation supported

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-18 21:27:56 +01:00
parent 12018fc088
commit 3f1f6ad852
4 changed files with 450 additions and 11 deletions

View File

@@ -579,6 +579,90 @@ function adminMarketplaceLetzshop() {
this.showOrderModal = true;
},
/**
* Confirm a single inventory unit
*/
async confirmInventoryUnit(order, unit, index) {
if (!this.selectedVendor) return;
try {
await apiClient.post(
`/admin/letzshop/vendors/${this.selectedVendor.id}/orders/${order.id}/items/${unit.id}/confirm`
);
// Update local state
this.selectedOrder.inventory_units[index].state = 'confirmed_available';
this.successMessage = 'Item confirmed';
// Reload orders to get updated status
await this.loadOrders();
} catch (error) {
marketplaceLetzshopLog.error('Failed to confirm item:', error);
this.error = error.message || 'Failed to confirm item';
}
},
/**
* Decline a single inventory unit
*/
async declineInventoryUnit(order, unit, index) {
if (!this.selectedVendor) return;
try {
await apiClient.post(
`/admin/letzshop/vendors/${this.selectedVendor.id}/orders/${order.id}/items/${unit.id}/decline`
);
// Update local state
this.selectedOrder.inventory_units[index].state = 'confirmed_unavailable';
this.successMessage = 'Item declined';
// Reload orders to get updated status
await this.loadOrders();
} catch (error) {
marketplaceLetzshopLog.error('Failed to decline item:', error);
this.error = error.message || 'Failed to decline item';
}
},
/**
* Confirm all items in an order
*/
async confirmAllItems(order) {
if (!this.selectedVendor) return;
if (!confirm('Are you sure you want to confirm all items in this order?')) return;
try {
await apiClient.post(
`/admin/letzshop/vendors/${this.selectedVendor.id}/orders/${order.id}/confirm`
);
this.successMessage = 'All items confirmed';
this.showOrderModal = false;
await this.loadOrders();
} catch (error) {
marketplaceLetzshopLog.error('Failed to confirm all items:', error);
this.error = error.message || 'Failed to confirm all items';
}
},
/**
* Decline all items in an order
*/
async declineAllItems(order) {
if (!this.selectedVendor) return;
if (!confirm('Are you sure you want to decline all items in this order?')) return;
try {
await apiClient.post(
`/admin/letzshop/vendors/${this.selectedVendor.id}/orders/${order.id}/reject`
);
this.successMessage = 'All items declined';
this.showOrderModal = false;
await this.loadOrders();
} catch (error) {
marketplaceLetzshopLog.error('Failed to decline all items:', error);
this.error = error.message || 'Failed to decline all items';
}
},
// ═══════════════════════════════════════════════════════════════
// SETTINGS TAB
// ═══════════════════════════════════════════════════════════════