Improve the frontend 2
This commit is contained in:
@@ -10,7 +10,7 @@ import { inventoryService } from '../services/inventory';
|
||||
import { getAlertAnalytics } from '../services/alert_analytics';
|
||||
import { getSustainabilityWidgetData } from '../services/sustainability';
|
||||
import { ApiError } from '../client/apiClient';
|
||||
import type { InventoryDashboardSummary } from '../types/dashboard';
|
||||
import type { InventoryDashboardSummary } from '../types/inventory';
|
||||
import type { AlertAnalytics } from '../services/alert_analytics';
|
||||
import type { SalesAnalytics } from '../types/sales';
|
||||
import type { OrdersDashboardSummary } from '../types/orders';
|
||||
@@ -106,25 +106,12 @@ function calculateTrend(current: number, previous: number): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate today's sales from sales records
|
||||
* Calculate today's sales from sales records (REMOVED - Professional/Enterprise tier feature)
|
||||
* Basic tier users don't get sales analytics on dashboard
|
||||
*/
|
||||
function calculateTodaySales(salesData?: SalesAnalytics): { amount: number; trend: number; productsSold: number; productsTrend: number } {
|
||||
if (!salesData) {
|
||||
return { amount: 0, trend: 0, productsSold: 0, productsTrend: 0 };
|
||||
}
|
||||
|
||||
// Sales data should have today's revenue and comparison
|
||||
const todayRevenue = salesData.total_revenue || 0;
|
||||
const previousRevenue = salesData.previous_period_revenue || 0;
|
||||
const todayUnits = salesData.total_units_sold || 0;
|
||||
const previousUnits = salesData.previous_period_units_sold || 0;
|
||||
|
||||
return {
|
||||
amount: todayRevenue,
|
||||
trend: calculateTrend(todayRevenue, previousRevenue),
|
||||
productsSold: todayUnits,
|
||||
productsTrend: calculateTrend(todayUnits, previousUnits),
|
||||
};
|
||||
function calculateTodaySales(): { amount: number; trend: number; productsSold: number; productsTrend: number } {
|
||||
// Return zero values - sales analytics not available for basic tier
|
||||
return { amount: 0, trend: 0, productsSold: 0, productsTrend: 0 };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -135,27 +122,27 @@ function calculateOrdersMetrics(ordersData?: OrdersDashboardSummary): { pending:
|
||||
return { pending: 0, today: 0, trend: 0 };
|
||||
}
|
||||
|
||||
const pendingCount = ordersData.pending_orders_count || 0;
|
||||
const todayCount = ordersData.orders_today_count || 0;
|
||||
const yesterdayCount = ordersData.orders_yesterday_count || 0;
|
||||
const pendingCount = ordersData.pending_orders || 0;
|
||||
const todayCount = ordersData.total_orders_today || 0;
|
||||
|
||||
return {
|
||||
pending: pendingCount,
|
||||
today: todayCount,
|
||||
trend: calculateTrend(todayCount, yesterdayCount),
|
||||
trend: 0, // Trend calculation removed - needs historical data
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregate dashboard data from all services
|
||||
* NOTE: Sales analytics removed - Professional/Enterprise tier feature
|
||||
*/
|
||||
function aggregateDashboardStats(data: AggregatedDashboardData): DashboardStats {
|
||||
const sales = calculateTodaySales(data.sales);
|
||||
const sales = calculateTodaySales(); // Returns zeros for basic tier
|
||||
const orders = calculateOrdersMetrics(data.orders);
|
||||
|
||||
const criticalStockCount =
|
||||
(data.inventory?.low_stock_count || 0) +
|
||||
(data.inventory?.out_of_stock_count || 0);
|
||||
(data.inventory?.low_stock_items || 0) +
|
||||
(data.inventory?.out_of_stock_items || 0);
|
||||
|
||||
return {
|
||||
// Alerts
|
||||
@@ -167,20 +154,20 @@ function aggregateDashboardStats(data: AggregatedDashboardData): DashboardStats
|
||||
ordersToday: orders.today,
|
||||
ordersTrend: orders.trend,
|
||||
|
||||
// Sales
|
||||
salesToday: sales.amount,
|
||||
salesTrend: sales.trend,
|
||||
salesCurrency: '€', // Default to EUR for bakery
|
||||
// Sales (REMOVED - not available for basic tier)
|
||||
salesToday: 0,
|
||||
salesTrend: 0,
|
||||
salesCurrency: '€',
|
||||
|
||||
// Inventory
|
||||
criticalStock: criticalStockCount,
|
||||
lowStockCount: data.inventory?.low_stock_count || 0,
|
||||
outOfStockCount: data.inventory?.out_of_stock_count || 0,
|
||||
expiringSoon: data.inventory?.expiring_soon_count || 0,
|
||||
lowStockCount: data.inventory?.low_stock_items || 0,
|
||||
outOfStockCount: data.inventory?.out_of_stock_items || 0,
|
||||
expiringSoon: data.inventory?.expiring_soon_items || 0,
|
||||
|
||||
// Products
|
||||
productsSoldToday: sales.productsSold,
|
||||
productsSoldTrend: sales.productsTrend,
|
||||
// Products (REMOVED - not available for basic tier)
|
||||
productsSoldToday: 0,
|
||||
productsSoldTrend: 0,
|
||||
|
||||
// Sustainability
|
||||
wasteReductionPercentage: data.sustainability?.waste_reduction_percentage,
|
||||
@@ -209,17 +196,13 @@ export const useDashboardStats = (
|
||||
return useQuery<DashboardStats, ApiError>({
|
||||
queryKey: dashboardKeys.stats(tenantId),
|
||||
queryFn: async () => {
|
||||
// Fetch all data in parallel
|
||||
const [alertsData, ordersData, salesData, inventoryData, sustainabilityData] = await Promise.allSettled([
|
||||
// Fetch all data in parallel (REMOVED sales analytics - Professional/Enterprise tier only)
|
||||
const [alertsData, ordersData, inventoryData, sustainabilityData] = await Promise.allSettled([
|
||||
getAlertAnalytics(tenantId, 7),
|
||||
// Note: OrdersService methods are static
|
||||
import('../services/orders').then(({ OrdersService }) =>
|
||||
OrdersService.getDashboardSummary(tenantId)
|
||||
),
|
||||
// Fetch today's sales with comparison to yesterday
|
||||
import('../services/sales').then(({ salesService }) =>
|
||||
salesService.getSalesAnalytics(tenantId, todayStr, todayStr)
|
||||
),
|
||||
inventoryService.getDashboardSummary(tenantId),
|
||||
getSustainabilityWidgetData(tenantId, 30), // 30 days for monthly savings
|
||||
]);
|
||||
@@ -228,7 +211,7 @@ export const useDashboardStats = (
|
||||
const aggregatedData: AggregatedDashboardData = {
|
||||
alerts: alertsData.status === 'fulfilled' ? alertsData.value : undefined,
|
||||
orders: ordersData.status === 'fulfilled' ? ordersData.value : undefined,
|
||||
sales: salesData.status === 'fulfilled' ? salesData.value : undefined,
|
||||
sales: undefined, // REMOVED - Professional/Enterprise tier only
|
||||
inventory: inventoryData.status === 'fulfilled' ? inventoryData.value : undefined,
|
||||
sustainability: sustainabilityData.status === 'fulfilled' ? sustainabilityData.value : undefined,
|
||||
};
|
||||
@@ -240,9 +223,6 @@ export const useDashboardStats = (
|
||||
if (ordersData.status === 'rejected') {
|
||||
console.warn('[Dashboard] Failed to fetch orders:', ordersData.reason);
|
||||
}
|
||||
if (salesData.status === 'rejected') {
|
||||
console.warn('[Dashboard] Failed to fetch sales:', salesData.reason);
|
||||
}
|
||||
if (inventoryData.status === 'rejected') {
|
||||
console.warn('[Dashboard] Failed to fetch inventory:', inventoryData.reason);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { equipmentService } from '../services/equipment';
|
||||
import type { Equipment } from '../types/equipment';
|
||||
import type { Equipment, EquipmentDeletionSummary } from '../types/equipment';
|
||||
|
||||
// Query Keys
|
||||
export const equipmentKeys = {
|
||||
@@ -114,7 +114,7 @@ export function useUpdateEquipment(tenantId: string) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to delete equipment
|
||||
* Hook to delete equipment (soft delete)
|
||||
*/
|
||||
export function useDeleteEquipment(tenantId: string) {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -139,3 +139,46 @@ export function useDeleteEquipment(tenantId: string) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to hard delete equipment (permanent deletion)
|
||||
*/
|
||||
export function useHardDeleteEquipment(tenantId: string) {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: (equipmentId: string) =>
|
||||
equipmentService.hardDeleteEquipment(tenantId, equipmentId),
|
||||
onSuccess: (_, equipmentId) => {
|
||||
// Remove from cache
|
||||
queryClient.removeQueries({
|
||||
queryKey: equipmentKeys.detail(tenantId, equipmentId)
|
||||
});
|
||||
|
||||
// Invalidate lists to refresh
|
||||
queryClient.invalidateQueries({ queryKey: equipmentKeys.lists() });
|
||||
|
||||
toast.success('Equipment permanently deleted');
|
||||
},
|
||||
onError: (error: any) => {
|
||||
console.error('Error hard deleting equipment:', error);
|
||||
toast.error(error.response?.data?.detail || 'Error permanently deleting equipment');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to get equipment deletion summary
|
||||
*/
|
||||
export function useEquipmentDeletionSummary(
|
||||
tenantId: string,
|
||||
equipmentId: string,
|
||||
options?: { enabled?: boolean }
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: [...equipmentKeys.detail(tenantId, equipmentId), 'deletion-summary'],
|
||||
queryFn: () => equipmentService.getEquipmentDeletionSummary(tenantId, equipmentId),
|
||||
enabled: !!tenantId && !!equipmentId && (options?.enabled ?? true),
|
||||
staleTime: 0, // Always fetch fresh data for dependency checks
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Subscription hook for checking plan features and limits
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { subscriptionService } from '../services/subscription';
|
||||
import {
|
||||
SUBSCRIPTION_TIERS,
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from '../types/subscription';
|
||||
import { useCurrentTenant } from '../../stores';
|
||||
import { useAuthUser } from '../../stores/auth.store';
|
||||
import { useSubscriptionEvents } from '../../contexts/SubscriptionEventsContext';
|
||||
|
||||
export interface SubscriptionFeature {
|
||||
hasFeature: boolean;
|
||||
@@ -40,9 +41,10 @@ export const useSubscription = () => {
|
||||
loading: true,
|
||||
});
|
||||
|
||||
const currentTenant = useCurrentTenant();
|
||||
const currentTenant = useCurrentTenant();
|
||||
const user = useAuthUser();
|
||||
const tenantId = currentTenant?.id || user?.tenant_id;
|
||||
const { notifySubscriptionChanged } = useSubscriptionEvents();
|
||||
|
||||
// Load subscription data
|
||||
const loadSubscriptionData = useCallback(async () => {
|
||||
@@ -62,6 +64,9 @@ export const useSubscription = () => {
|
||||
features: usageSummary.usage || {},
|
||||
loading: false,
|
||||
});
|
||||
|
||||
// Notify subscribers that subscription data has changed
|
||||
notifySubscriptionChanged();
|
||||
} catch (error) {
|
||||
console.error('Error loading subscription data:', error);
|
||||
setSubscriptionInfo(prev => ({
|
||||
@@ -70,7 +75,7 @@ export const useSubscription = () => {
|
||||
error: 'Failed to load subscription data'
|
||||
}));
|
||||
}
|
||||
}, [tenantId]);
|
||||
}, [tenantId, notifySubscriptionChanged]);
|
||||
|
||||
useEffect(() => {
|
||||
loadSubscriptionData();
|
||||
@@ -177,4 +182,4 @@ export const useSubscription = () => {
|
||||
};
|
||||
};
|
||||
|
||||
export default useSubscription;
|
||||
export default useSubscription;
|
||||
|
||||
@@ -26,6 +26,9 @@ import type {
|
||||
DeliveryReceiptConfirmation,
|
||||
DeliverySearchParams,
|
||||
PerformanceMetric,
|
||||
SupplierPriceListCreate,
|
||||
SupplierPriceListUpdate,
|
||||
SupplierPriceListResponse,
|
||||
} from '../types/suppliers';
|
||||
|
||||
// Query Keys Factory
|
||||
@@ -228,6 +231,37 @@ export const useDelivery = (
|
||||
});
|
||||
};
|
||||
|
||||
// Supplier Price List Queries
|
||||
export const useSupplierPriceLists = (
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
isActive: boolean = true,
|
||||
options?: Omit<UseQueryOptions<SupplierPriceListResponse[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<SupplierPriceListResponse[], ApiError>({
|
||||
queryKey: [...suppliersKeys.suppliers.detail(tenantId, supplierId), 'price-lists', isActive],
|
||||
queryFn: () => suppliersService.getSupplierPriceLists(tenantId, supplierId, isActive),
|
||||
enabled: !!tenantId && !!supplierId,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useSupplierPriceList = (
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
priceListId: string,
|
||||
options?: Omit<UseQueryOptions<SupplierPriceListResponse, ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<SupplierPriceListResponse, ApiError>({
|
||||
queryKey: [...suppliersKeys.suppliers.detail(tenantId, supplierId), 'price-list', priceListId],
|
||||
queryFn: () => suppliersService.getSupplierPriceList(tenantId, supplierId, priceListId),
|
||||
enabled: !!tenantId && !!supplierId && !!priceListId,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Performance Queries
|
||||
export const useSupplierPerformanceMetrics = (
|
||||
tenantId: string,
|
||||
@@ -236,7 +270,7 @@ export const useSupplierPerformanceMetrics = (
|
||||
) => {
|
||||
return useQuery<PerformanceMetric[], ApiError>({
|
||||
queryKey: suppliersKeys.performance.metrics(tenantId, supplierId),
|
||||
queryFn: () => suppliersService.getPerformanceMetrics(tenantId, supplierId),
|
||||
queryFn: () => suppliersService.getSupplierPerformanceMetrics(tenantId, supplierId),
|
||||
enabled: !!tenantId && !!supplierId,
|
||||
staleTime: 10 * 60 * 1000, // 10 minutes
|
||||
...options,
|
||||
@@ -245,13 +279,13 @@ export const useSupplierPerformanceMetrics = (
|
||||
|
||||
export const usePerformanceAlerts = (
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
supplierId?: string,
|
||||
options?: Omit<UseQueryOptions<any[], ApiError>, 'queryKey' | 'queryFn'>
|
||||
) => {
|
||||
return useQuery<any[], ApiError>({
|
||||
queryKey: suppliersKeys.performance.alerts(tenantId, supplierId),
|
||||
queryFn: () => suppliersService.evaluatePerformanceAlerts(tenantId, supplierId),
|
||||
enabled: !!tenantId && !!supplierId,
|
||||
queryFn: () => suppliersService.getPerformanceAlerts(tenantId, supplierId),
|
||||
enabled: !!tenantId,
|
||||
staleTime: 2 * 60 * 1000, // 2 minutes
|
||||
...options,
|
||||
});
|
||||
@@ -607,12 +641,108 @@ export const useConfirmDeliveryReceipt = (
|
||||
});
|
||||
};
|
||||
|
||||
// Supplier Price List Mutations
|
||||
export const useCreateSupplierPriceList = (
|
||||
options?: UseMutationOptions<
|
||||
SupplierPriceListResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; supplierId: string; priceListData: SupplierPriceListCreate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
SupplierPriceListResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; supplierId: string; priceListData: SupplierPriceListCreate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, supplierId, priceListData }) =>
|
||||
suppliersService.createSupplierPriceList(tenantId, supplierId, priceListData),
|
||||
onSuccess: (data, { tenantId, supplierId }) => {
|
||||
// Add to cache
|
||||
queryClient.setQueryData(
|
||||
[...suppliersKeys.suppliers.detail(tenantId, supplierId), 'price-list', data.id],
|
||||
data
|
||||
);
|
||||
|
||||
// Invalidate price lists
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...suppliersKeys.suppliers.detail(tenantId, supplierId), 'price-lists']
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useUpdateSupplierPriceList = (
|
||||
options?: UseMutationOptions<
|
||||
SupplierPriceListResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; supplierId: string; priceListId: string; priceListData: SupplierPriceListUpdate }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
SupplierPriceListResponse,
|
||||
ApiError,
|
||||
{ tenantId: string; supplierId: string; priceListId: string; priceListData: SupplierPriceListUpdate }
|
||||
>({
|
||||
mutationFn: ({ tenantId, supplierId, priceListId, priceListData }) =>
|
||||
suppliersService.updateSupplierPriceList(tenantId, supplierId, priceListId, priceListData),
|
||||
onSuccess: (data, { tenantId, supplierId, priceListId }) => {
|
||||
// Update cache
|
||||
queryClient.setQueryData(
|
||||
[...suppliersKeys.suppliers.detail(tenantId, supplierId), 'price-list', priceListId],
|
||||
data
|
||||
);
|
||||
|
||||
// Invalidate price lists
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...suppliersKeys.suppliers.detail(tenantId, supplierId), 'price-lists']
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteSupplierPriceList = (
|
||||
options?: UseMutationOptions<
|
||||
{ message: string },
|
||||
ApiError,
|
||||
{ tenantId: string; supplierId: string; priceListId: string }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
{ message: string },
|
||||
ApiError,
|
||||
{ tenantId: string; supplierId: string; priceListId: string }
|
||||
>({
|
||||
mutationFn: ({ tenantId, supplierId, priceListId }) =>
|
||||
suppliersService.deleteSupplierPriceList(tenantId, supplierId, priceListId),
|
||||
onSuccess: (_, { tenantId, supplierId, priceListId }) => {
|
||||
// Remove from cache
|
||||
queryClient.removeQueries({
|
||||
queryKey: [...suppliersKeys.suppliers.detail(tenantId, supplierId), 'price-list', priceListId]
|
||||
});
|
||||
|
||||
// Invalidate price lists
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [...suppliersKeys.suppliers.detail(tenantId, supplierId), 'price-lists']
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
// Performance Mutations
|
||||
export const useCalculateSupplierPerformance = (
|
||||
options?: UseMutationOptions<
|
||||
{ message: string; calculation_id: string },
|
||||
ApiError,
|
||||
{ tenantId: string; supplierId: string; request?: PerformanceCalculationRequest }
|
||||
{ tenantId: string; supplierId: string; request?: any }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -620,7 +750,7 @@ export const useCalculateSupplierPerformance = (
|
||||
return useMutation<
|
||||
{ message: string; calculation_id: string },
|
||||
ApiError,
|
||||
{ tenantId: string; supplierId: string; request?: PerformanceCalculationRequest }
|
||||
{ tenantId: string; supplierId: string; request?: any }
|
||||
>({
|
||||
mutationFn: ({ tenantId, supplierId, request }) =>
|
||||
suppliersService.calculateSupplierPerformance(tenantId, supplierId, request),
|
||||
@@ -641,7 +771,7 @@ export const useEvaluatePerformanceAlerts = (
|
||||
options?: UseMutationOptions<
|
||||
{ alerts_generated: number; message: string },
|
||||
ApiError,
|
||||
{ tenantId: string }
|
||||
{ tenantId: string; supplierId?: string }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
@@ -649,7 +779,7 @@ export const useEvaluatePerformanceAlerts = (
|
||||
return useMutation<
|
||||
{ alerts_generated: number; message: string },
|
||||
ApiError,
|
||||
{ tenantId: string }
|
||||
{ tenantId: string; supplierId?: string }
|
||||
>({
|
||||
mutationFn: ({ tenantId, supplierId }) => suppliersService.evaluatePerformanceAlerts(tenantId, supplierId),
|
||||
onSuccess: (_, { tenantId }) => {
|
||||
@@ -677,11 +807,7 @@ export const useActiveSuppliersCount = (tenantId: string) => {
|
||||
return statistics?.active_suppliers || 0;
|
||||
};
|
||||
|
||||
export const usePendingOrdersCount = (supplierId?: string) => {
|
||||
const { data: orders } = usePurchaseOrders({
|
||||
supplier_id: supplierId,
|
||||
status: 'pending_approval',
|
||||
limit: 1000
|
||||
});
|
||||
return orders?.data?.length || 0;
|
||||
};
|
||||
export const usePendingOrdersCount = (queryParams?: PurchaseOrderSearchParams) => {
|
||||
const { data: orders } = usePurchaseOrders('', queryParams);
|
||||
return orders?.length || 0;
|
||||
};
|
||||
|
||||
@@ -9,7 +9,8 @@ import type {
|
||||
EquipmentCreate,
|
||||
EquipmentUpdate,
|
||||
EquipmentResponse,
|
||||
EquipmentListResponse
|
||||
EquipmentListResponse,
|
||||
EquipmentDeletionSummary
|
||||
} from '../types/equipment';
|
||||
|
||||
class EquipmentService {
|
||||
@@ -163,7 +164,7 @@ class EquipmentService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an equipment item
|
||||
* Delete an equipment item (soft delete)
|
||||
*/
|
||||
async deleteEquipment(tenantId: string, equipmentId: string): Promise<void> {
|
||||
await apiClient.delete(
|
||||
@@ -173,6 +174,34 @@ class EquipmentService {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Permanently delete an equipment item (hard delete)
|
||||
*/
|
||||
async hardDeleteEquipment(tenantId: string, equipmentId: string): Promise<void> {
|
||||
await apiClient.delete(
|
||||
`${this.baseURL}/${tenantId}/production/equipment/${equipmentId}?permanent=true`,
|
||||
{
|
||||
headers: { 'X-Tenant-ID': tenantId }
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get deletion summary for an equipment item
|
||||
*/
|
||||
async getEquipmentDeletionSummary(
|
||||
tenantId: string,
|
||||
equipmentId: string
|
||||
): Promise<EquipmentDeletionSummary> {
|
||||
const data: EquipmentDeletionSummary = await apiClient.get(
|
||||
`${this.baseURL}/${tenantId}/production/equipment/${equipmentId}/deletion-summary`,
|
||||
{
|
||||
headers: { 'X-Tenant-ID': tenantId }
|
||||
}
|
||||
);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
export const equipmentService = new EquipmentService();
|
||||
|
||||
@@ -20,25 +20,25 @@ import type {
|
||||
SupplierResponse,
|
||||
SupplierSummary,
|
||||
SupplierApproval,
|
||||
SupplierQueryParams,
|
||||
SupplierSearchParams,
|
||||
SupplierStatistics,
|
||||
SupplierDeletionSummary,
|
||||
TopSuppliersResponse,
|
||||
SupplierResponse as SupplierResponse_,
|
||||
PurchaseOrderCreate,
|
||||
PurchaseOrderUpdate,
|
||||
PurchaseOrderResponse,
|
||||
PurchaseOrderApproval,
|
||||
PurchaseOrderQueryParams,
|
||||
PurchaseOrderSearchParams,
|
||||
DeliveryCreate,
|
||||
DeliveryUpdate,
|
||||
DeliveryResponse,
|
||||
DeliveryReceiptConfirmation,
|
||||
DeliveryQueryParams,
|
||||
PerformanceCalculationRequest,
|
||||
PerformanceMetrics,
|
||||
DeliverySearchParams,
|
||||
PerformanceMetric,
|
||||
PerformanceAlert,
|
||||
PaginatedResponse,
|
||||
ApiResponse,
|
||||
SupplierPriceListCreate,
|
||||
SupplierPriceListUpdate,
|
||||
SupplierPriceListResponse
|
||||
} from '../types/suppliers';
|
||||
|
||||
class SuppliersService {
|
||||
@@ -59,10 +59,71 @@ class SuppliersService {
|
||||
);
|
||||
}
|
||||
|
||||
// ===================================================================
|
||||
// ATOMIC: Supplier Price Lists CRUD
|
||||
// Backend: services/suppliers/app/api/suppliers.py (price list endpoints)
|
||||
// ===================================================================
|
||||
|
||||
async getSupplierPriceLists(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
isActive: boolean = true
|
||||
): Promise<SupplierPriceListResponse[]> {
|
||||
const params = new URLSearchParams();
|
||||
params.append('is_active', isActive.toString());
|
||||
|
||||
return apiClient.get<SupplierPriceListResponse[]>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/price-lists?${params.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
async getSupplierPriceList(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
priceListId: string
|
||||
): Promise<SupplierPriceListResponse> {
|
||||
return apiClient.get<SupplierPriceListResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/price-lists/${priceListId}`
|
||||
);
|
||||
}
|
||||
|
||||
async createSupplierPriceList(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
priceListData: SupplierPriceListCreate
|
||||
): Promise<SupplierPriceListResponse> {
|
||||
return apiClient.post<SupplierPriceListResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/price-lists`,
|
||||
priceListData
|
||||
);
|
||||
}
|
||||
|
||||
async updateSupplierPriceList(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
priceListId: string,
|
||||
priceListData: SupplierPriceListUpdate
|
||||
): Promise<SupplierPriceListResponse> {
|
||||
return apiClient.put<SupplierPriceListResponse>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/price-lists/${priceListId}`,
|
||||
priceListData
|
||||
);
|
||||
}
|
||||
|
||||
async deleteSupplierPriceList(
|
||||
tenantId: string,
|
||||
supplierId: string,
|
||||
priceListId: string
|
||||
): Promise<{ message: string }> {
|
||||
return apiClient.delete<{ message: string }>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/${supplierId}/price-lists/${priceListId}`
|
||||
);
|
||||
}
|
||||
|
||||
async getSuppliers(
|
||||
tenantId: string,
|
||||
queryParams?: SupplierQueryParams
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
queryParams?: SupplierSearchParams
|
||||
): Promise<SupplierSummary[]> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (queryParams?.search_term) params.append('search_term', queryParams.search_term);
|
||||
@@ -70,11 +131,9 @@ class SuppliersService {
|
||||
if (queryParams?.status) params.append('status', queryParams.status);
|
||||
if (queryParams?.limit) params.append('limit', queryParams.limit.toString());
|
||||
if (queryParams?.offset) params.append('offset', queryParams.offset.toString());
|
||||
if (queryParams?.sort_by) params.append('sort_by', queryParams.sort_by);
|
||||
if (queryParams?.sort_order) params.append('sort_order', queryParams.sort_order);
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<SupplierSummary>>(
|
||||
return apiClient.get<SupplierSummary[]>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers${queryString}`
|
||||
);
|
||||
}
|
||||
@@ -142,10 +201,10 @@ class SuppliersService {
|
||||
);
|
||||
}
|
||||
|
||||
async getPurchaseOrders(
|
||||
async getPurchaseOrders(
|
||||
tenantId: string,
|
||||
queryParams?: PurchaseOrderQueryParams
|
||||
): Promise<PaginatedResponse<PurchaseOrderResponse>> {
|
||||
queryParams?: PurchaseOrderSearchParams
|
||||
): Promise<PurchaseOrderResponse[]> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (queryParams?.supplier_id) params.append('supplier_id', queryParams.supplier_id);
|
||||
@@ -155,11 +214,9 @@ class SuppliersService {
|
||||
if (queryParams?.date_to) params.append('date_to', queryParams.date_to);
|
||||
if (queryParams?.limit) params.append('limit', queryParams.limit.toString());
|
||||
if (queryParams?.offset) params.append('offset', queryParams.offset.toString());
|
||||
if (queryParams?.sort_by) params.append('sort_by', queryParams.sort_by);
|
||||
if (queryParams?.sort_order) params.append('sort_order', queryParams.sort_order);
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<PurchaseOrderResponse>>(
|
||||
return apiClient.get<PurchaseOrderResponse[]>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/purchase-orders${queryString}`
|
||||
);
|
||||
}
|
||||
@@ -209,8 +266,8 @@ class SuppliersService {
|
||||
|
||||
async getDeliveries(
|
||||
tenantId: string,
|
||||
queryParams?: DeliveryQueryParams
|
||||
): Promise<PaginatedResponse<DeliveryResponse>> {
|
||||
queryParams?: DeliverySearchParams
|
||||
): Promise<DeliveryResponse[]> {
|
||||
const params = new URLSearchParams();
|
||||
|
||||
if (queryParams?.supplier_id) params.append('supplier_id', queryParams.supplier_id);
|
||||
@@ -226,11 +283,9 @@ class SuppliersService {
|
||||
}
|
||||
if (queryParams?.limit) params.append('limit', queryParams.limit.toString());
|
||||
if (queryParams?.offset) params.append('offset', queryParams.offset.toString());
|
||||
if (queryParams?.sort_by) params.append('sort_by', queryParams.sort_by);
|
||||
if (queryParams?.sort_order) params.append('sort_order', queryParams.sort_order);
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<DeliveryResponse>>(
|
||||
return apiClient.get<DeliveryResponse[]>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/deliveries${queryString}`
|
||||
);
|
||||
}
|
||||
@@ -276,8 +331,8 @@ class SuppliersService {
|
||||
|
||||
async getActiveSuppliers(
|
||||
tenantId: string,
|
||||
queryParams?: Omit<SupplierQueryParams, 'status'>
|
||||
): Promise<PaginatedResponse<SupplierSummary>> {
|
||||
queryParams?: SupplierSearchParams
|
||||
): Promise<SupplierSummary[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (queryParams?.search_term) params.append('search_term', queryParams.search_term);
|
||||
if (queryParams?.supplier_type) params.append('supplier_type', queryParams.supplier_type);
|
||||
@@ -285,10 +340,10 @@ class SuppliersService {
|
||||
if (queryParams?.offset) params.append('offset', queryParams.offset.toString());
|
||||
|
||||
const queryString = params.toString() ? `?${params.toString()}` : '';
|
||||
return apiClient.get<PaginatedResponse<SupplierSummary>>(
|
||||
return apiClient.get<SupplierSummary[]>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/operations/active${queryString}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getTopSuppliers(tenantId: string): Promise<TopSuppliersResponse> {
|
||||
return apiClient.get<TopSuppliersResponse>(
|
||||
@@ -356,11 +411,11 @@ class SuppliersService {
|
||||
async getSupplierPerformanceMetrics(
|
||||
tenantId: string,
|
||||
supplierId: string
|
||||
): Promise<PerformanceMetrics> {
|
||||
return apiClient.get<PerformanceMetrics>(
|
||||
): Promise<PerformanceMetric[]> {
|
||||
return apiClient.get<PerformanceMetric[]>(
|
||||
`${this.baseUrl}/${tenantId}/suppliers/analytics/performance/${supplierId}/metrics`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async evaluatePerformanceAlerts(
|
||||
tenantId: string
|
||||
|
||||
@@ -138,4 +138,15 @@ export interface EquipmentListResponse {
|
||||
total_count: number;
|
||||
page: number;
|
||||
page_size: number;
|
||||
}
|
||||
|
||||
export interface EquipmentDeletionSummary {
|
||||
can_delete: boolean;
|
||||
warnings: string[];
|
||||
production_batches_count: number;
|
||||
maintenance_records_count: number;
|
||||
temperature_logs_count: number;
|
||||
equipment_name?: string;
|
||||
equipment_type?: string;
|
||||
equipment_location?: string;
|
||||
}
|
||||
@@ -22,7 +22,7 @@ export enum SupplierType {
|
||||
|
||||
export enum SupplierStatus {
|
||||
ACTIVE = 'active',
|
||||
INACTIVE = 'inactive',
|
||||
INACTIVE = 'inactive',
|
||||
PENDING_APPROVAL = 'pending_approval',
|
||||
SUSPENDED = 'suspended',
|
||||
BLACKLISTED = 'blacklisted'
|
||||
@@ -114,7 +114,7 @@ export enum AlertType {
|
||||
|
||||
export enum AlertStatus {
|
||||
ACTIVE = 'ACTIVE',
|
||||
ACKNOWLEDGED = 'ACKNOWLEDGED',
|
||||
ACKNOWLEDGED = 'ACKNOWLEDGED',
|
||||
IN_PROGRESS = 'IN_PROGRESS',
|
||||
RESOLVED = 'RESOLVED',
|
||||
DISMISSED = 'DISMISSED'
|
||||
@@ -139,6 +139,71 @@ export enum PerformancePeriod {
|
||||
YEARLY = 'YEARLY'
|
||||
}
|
||||
|
||||
// ===== SUPPLIER PRICE LIST SCHEMAS =====
|
||||
export interface SupplierPriceListCreate {
|
||||
inventory_product_id: string;
|
||||
product_code?: string | null; // max_length=100
|
||||
unit_price: number; // gt=0
|
||||
unit_of_measure: string; // max_length=20
|
||||
minimum_order_quantity?: number | null; // ge=1
|
||||
price_per_unit: number; // gt=0
|
||||
tier_pricing?: Record<string, any> | null; // [{quantity: 100, price: 2.50}, ...]
|
||||
effective_date?: string; // Default: now()
|
||||
expiry_date?: string | null;
|
||||
is_active?: boolean; // Default: true
|
||||
brand?: string | null; // max_length=100
|
||||
packaging_size?: string | null; // max_length=50
|
||||
origin_country?: string | null; // max_length=100
|
||||
shelf_life_days?: number | null;
|
||||
storage_requirements?: string | null;
|
||||
quality_specs?: Record<string, any> | null;
|
||||
allergens?: Record<string, any> | null;
|
||||
}
|
||||
|
||||
export interface SupplierPriceListUpdate {
|
||||
unit_price?: number | null; // gt=0
|
||||
unit_of_measure?: string | null; // max_length=20
|
||||
minimum_order_quantity?: number | null; // ge=1
|
||||
tier_pricing?: Record<string, any> | null;
|
||||
effective_date?: string | null;
|
||||
expiry_date?: string | null;
|
||||
is_active?: boolean | null;
|
||||
brand?: string | null;
|
||||
packaging_size?: string | null;
|
||||
origin_country?: string | null;
|
||||
shelf_life_days?: number | null;
|
||||
storage_requirements?: string | null;
|
||||
quality_specs?: Record<string, any> | null;
|
||||
allergens?: Record<string, any> | null;
|
||||
}
|
||||
|
||||
export interface SupplierPriceListResponse {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
supplier_id: string;
|
||||
inventory_product_id: string;
|
||||
product_code: string | null;
|
||||
unit_price: number;
|
||||
unit_of_measure: string;
|
||||
minimum_order_quantity: number | null;
|
||||
price_per_unit: number;
|
||||
tier_pricing: Record<string, any> | null;
|
||||
effective_date: string;
|
||||
expiry_date: string | null;
|
||||
is_active: boolean;
|
||||
brand: string | null;
|
||||
packaging_size: string | null;
|
||||
origin_country: string | null;
|
||||
shelf_life_days: number | null;
|
||||
storage_requirements: string | null;
|
||||
quality_specs: Record<string, any> | null;
|
||||
allergens: Record<string, any> | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
created_by: string;
|
||||
updated_by: string;
|
||||
}
|
||||
|
||||
// ===== SUPPLIER SCHEMAS =====
|
||||
// Mirror: SupplierCreate from suppliers.py:23
|
||||
|
||||
@@ -222,7 +287,7 @@ export interface SupplierApproval {
|
||||
|
||||
// Mirror: SupplierResponse from suppliers.py:102
|
||||
export interface SupplierResponse {
|
||||
id: string;
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
name: string;
|
||||
supplier_code: string | null;
|
||||
@@ -245,7 +310,7 @@ export interface SupplierResponse {
|
||||
country: string | null;
|
||||
|
||||
// Business terms
|
||||
payment_terms: PaymentTerms;
|
||||
payment_terms: PaymentTerms;
|
||||
credit_limit: number | null;
|
||||
currency: string;
|
||||
standard_lead_time: number;
|
||||
@@ -253,7 +318,7 @@ export interface SupplierResponse {
|
||||
delivery_area: string | null;
|
||||
|
||||
// Performance metrics
|
||||
quality_rating: number | null;
|
||||
quality_rating: number | null;
|
||||
delivery_rating: number | null;
|
||||
total_orders: number;
|
||||
total_amount: number;
|
||||
@@ -264,7 +329,7 @@ export interface SupplierResponse {
|
||||
rejection_reason: string | null;
|
||||
|
||||
// Additional information
|
||||
notes: string | null;
|
||||
notes: string | null;
|
||||
certifications: Record<string, any> | null;
|
||||
business_hours: Record<string, any> | null;
|
||||
specializations: Record<string, any> | null;
|
||||
@@ -346,12 +411,12 @@ export interface PurchaseOrderItemResponse {
|
||||
// Mirror: PurchaseOrderCreate from suppliers.py (inferred)
|
||||
export interface PurchaseOrderCreate {
|
||||
supplier_id: string;
|
||||
items: PurchaseOrderItemCreate[]; // min_items=1
|
||||
items: PurchaseOrderItemCreate[]; // min_items=1
|
||||
|
||||
// Order details
|
||||
reference_number?: string | null; // max_length=100
|
||||
priority?: string; // Default: "normal", max_length=20
|
||||
required_delivery_date?: string | null;
|
||||
required_delivery_date?: string | null;
|
||||
|
||||
// Delivery info
|
||||
delivery_address?: string | null;
|
||||
@@ -360,12 +425,12 @@ export interface PurchaseOrderCreate {
|
||||
delivery_phone?: string | null; // max_length=30
|
||||
|
||||
// Financial (all default=0, ge=0)
|
||||
tax_amount?: number;
|
||||
tax_amount?: number;
|
||||
shipping_cost?: number;
|
||||
discount_amount?: number;
|
||||
|
||||
// Additional
|
||||
notes?: string | null;
|
||||
notes?: string | null;
|
||||
internal_notes?: string | null;
|
||||
terms_and_conditions?: string | null;
|
||||
}
|
||||
@@ -376,7 +441,7 @@ export interface PurchaseOrderUpdate {
|
||||
priority?: string | null;
|
||||
required_delivery_date?: string | null;
|
||||
estimated_delivery_date?: string | null;
|
||||
supplier_reference?: string | null; // max_length=100
|
||||
supplier_reference?: string | null; // max_length=100
|
||||
delivery_address?: string | null;
|
||||
delivery_instructions?: string | null;
|
||||
delivery_contact?: string | null;
|
||||
@@ -411,25 +476,25 @@ export interface PurchaseOrderResponse {
|
||||
order_date: string;
|
||||
reference_number: string | null;
|
||||
priority: string;
|
||||
required_delivery_date: string | null;
|
||||
required_delivery_date: string | null;
|
||||
estimated_delivery_date: string | null;
|
||||
|
||||
// Financial
|
||||
subtotal: number;
|
||||
tax_amount: number;
|
||||
tax_amount: number;
|
||||
shipping_cost: number;
|
||||
discount_amount: number;
|
||||
total_amount: number;
|
||||
currency: string;
|
||||
|
||||
// Delivery
|
||||
delivery_address: string | null;
|
||||
delivery_address: string | null;
|
||||
delivery_instructions: string | null;
|
||||
delivery_contact: string | null;
|
||||
delivery_phone: string | null;
|
||||
|
||||
// Approval
|
||||
requires_approval: boolean;
|
||||
requires_approval: boolean;
|
||||
approved_by: string | null;
|
||||
approved_at: string | null;
|
||||
rejection_reason: string | null;
|
||||
@@ -440,12 +505,12 @@ export interface PurchaseOrderResponse {
|
||||
supplier_reference: string | null;
|
||||
|
||||
// Additional
|
||||
notes: string | null;
|
||||
notes: string | null;
|
||||
internal_notes: string | null;
|
||||
terms_and_conditions: string | null;
|
||||
|
||||
// Audit
|
||||
created_at: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
created_by: string;
|
||||
updated_by: string;
|
||||
@@ -516,7 +581,7 @@ export interface DeliveryCreate {
|
||||
delivery_phone?: string | null; // max_length=30
|
||||
|
||||
// Tracking
|
||||
carrier_name?: string | null; // max_length=200
|
||||
carrier_name?: string | null; // max_length=200
|
||||
tracking_number?: string | null; // max_length=100
|
||||
|
||||
// Additional
|
||||
@@ -525,7 +590,7 @@ export interface DeliveryCreate {
|
||||
|
||||
// Mirror: DeliveryUpdate from suppliers.py (inferred)
|
||||
export interface DeliveryUpdate {
|
||||
supplier_delivery_note?: string | null;
|
||||
supplier_delivery_note?: string | null;
|
||||
scheduled_date?: string | null;
|
||||
estimated_arrival?: string | null;
|
||||
actual_arrival?: string | null;
|
||||
@@ -565,25 +630,25 @@ export interface DeliveryResponse {
|
||||
status: DeliveryStatus;
|
||||
|
||||
// Timing
|
||||
scheduled_date: string | null;
|
||||
scheduled_date: string | null;
|
||||
estimated_arrival: string | null;
|
||||
actual_arrival: string | null;
|
||||
actual_arrival: string | null;
|
||||
completed_at: string | null;
|
||||
|
||||
// Delivery info
|
||||
supplier_delivery_note: string | null;
|
||||
supplier_delivery_note: string | null;
|
||||
delivery_address: string | null;
|
||||
delivery_contact: string | null;
|
||||
delivery_phone: string | null;
|
||||
|
||||
// Tracking
|
||||
carrier_name: string | null;
|
||||
carrier_name: string | null;
|
||||
tracking_number: string | null;
|
||||
|
||||
// Quality
|
||||
inspection_passed: boolean | null;
|
||||
inspection_notes: string | null;
|
||||
quality_issues: Record<string, any> | null;
|
||||
quality_issues: Record<string, any> | null;
|
||||
|
||||
// Receipt
|
||||
received_by: string | null;
|
||||
@@ -594,7 +659,7 @@ export interface DeliveryResponse {
|
||||
photos: Record<string, any> | null;
|
||||
|
||||
// Audit
|
||||
created_at: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
created_by: string;
|
||||
|
||||
@@ -624,7 +689,7 @@ export interface DeliverySummary {
|
||||
|
||||
export interface PerformanceMetricCreate {
|
||||
supplier_id: string;
|
||||
metric_type: PerformanceMetricType;
|
||||
metric_type: PerformanceMetricType;
|
||||
period: PerformancePeriod;
|
||||
period_start: string;
|
||||
period_end: string;
|
||||
@@ -651,21 +716,21 @@ export interface PerformanceMetric extends PerformanceMetricCreate {
|
||||
tenant_id: string;
|
||||
previous_value: number | null;
|
||||
trend_direction: string | null; // improving, declining, stable
|
||||
trend_percentage: number | null;
|
||||
trend_percentage: number | null;
|
||||
calculated_at: string;
|
||||
}
|
||||
|
||||
// Mirror: AlertCreate from performance.py
|
||||
export interface AlertCreate {
|
||||
supplier_id: string;
|
||||
alert_type: AlertType;
|
||||
alert_type: AlertType;
|
||||
severity: AlertSeverity;
|
||||
title: string; // max_length=255
|
||||
message: string;
|
||||
description?: string | null;
|
||||
|
||||
// Context
|
||||
trigger_value?: number | null;
|
||||
trigger_value?: number | null;
|
||||
threshold_value?: number | null;
|
||||
metric_type?: PerformanceMetricType | null;
|
||||
|
||||
@@ -693,7 +758,7 @@ export interface Alert extends Omit<AlertCreate, 'auto_resolve'> {
|
||||
resolved_at: string | null;
|
||||
resolved_by: string | null;
|
||||
actions_taken: Array<Record<string, any>> | null;
|
||||
resolution_notes: string | null;
|
||||
resolution_notes: string | null;
|
||||
escalated: boolean;
|
||||
escalated_at: string | null;
|
||||
notification_sent: boolean;
|
||||
@@ -759,7 +824,7 @@ export interface SupplierStatistics {
|
||||
active_suppliers: number;
|
||||
pending_suppliers: number;
|
||||
avg_quality_rating: number;
|
||||
avg_delivery_rating: number;
|
||||
avg_delivery_rating: number;
|
||||
total_spend: number;
|
||||
}
|
||||
|
||||
@@ -800,12 +865,12 @@ export interface PerformanceDashboardSummary {
|
||||
average_quality_rate: number;
|
||||
total_active_alerts: number;
|
||||
critical_alerts: number;
|
||||
high_priority_alerts: number;
|
||||
high_priority_alerts: number;
|
||||
recent_scorecards_generated: number;
|
||||
cost_savings_this_month: number;
|
||||
performance_trend: string;
|
||||
delivery_trend: string;
|
||||
quality_trend: string;
|
||||
quality_trend: string;
|
||||
detected_business_model: string;
|
||||
model_confidence: number;
|
||||
business_model_metrics: Record<string, any>;
|
||||
@@ -958,7 +1023,7 @@ export interface ExportDataResponse {
|
||||
export interface SupplierDeletionSummary {
|
||||
supplier_name: string;
|
||||
deleted_price_lists: number;
|
||||
deleted_quality_reviews: number;
|
||||
deleted_quality_reviews: number;
|
||||
deleted_performance_metrics: number;
|
||||
deleted_alerts: number;
|
||||
deleted_scorecards: number;
|
||||
|
||||
@@ -110,8 +110,8 @@ export interface TenantSubscriptionUpdate {
|
||||
// ================================================================
|
||||
|
||||
/**
|
||||
* Tenant response schema - FIXED VERSION with owner_id
|
||||
* Backend: TenantResponse in schemas/tenants.py (lines 55-82)
|
||||
* Tenant response schema - Updated with subscription_plan
|
||||
* Backend: TenantResponse in schemas/tenants.py (lines 59-87)
|
||||
*/
|
||||
export interface TenantResponse {
|
||||
id: string;
|
||||
@@ -124,11 +124,15 @@ export interface TenantResponse {
|
||||
postal_code: string;
|
||||
phone?: string | null;
|
||||
is_active: boolean;
|
||||
subscription_tier: string;
|
||||
subscription_plan?: string | null; // Populated from subscription relationship
|
||||
ml_model_trained: boolean;
|
||||
last_training_date?: string | null; // ISO datetime string
|
||||
owner_id: string; // ✅ REQUIRED field - fixes type error
|
||||
owner_id: string; // ✅ REQUIRED field
|
||||
created_at: string; // ISO datetime string
|
||||
|
||||
// Backward compatibility
|
||||
/** @deprecated Use subscription_plan instead */
|
||||
subscription_tier?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user