Add fixes to procurement logic and fix rel-time connections
This commit is contained in:
@@ -29,6 +29,9 @@ import {
|
||||
GeneratePlanResponse,
|
||||
PaginatedProcurementPlans,
|
||||
GetProcurementPlansParams,
|
||||
CreatePOsResult,
|
||||
LinkRequirementToPORequest,
|
||||
UpdateDeliveryStatusRequest,
|
||||
GetPlanRequirementsParams,
|
||||
UpdatePlanStatusParams,
|
||||
} from '../types/orders';
|
||||
@@ -546,3 +549,186 @@ export const useTriggerDailyScheduler = (
|
||||
});
|
||||
};
|
||||
|
||||
// ===== NEW PROCUREMENT FEATURE HOOKS =====
|
||||
|
||||
/**
|
||||
* Hook to recalculate a procurement plan
|
||||
*/
|
||||
export const useRecalculateProcurementPlan = (
|
||||
options?: UseMutationOptions<GeneratePlanResponse, ApiError, { tenantId: string; planId: string }>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<GeneratePlanResponse, ApiError, { tenantId: string; planId: string }>({
|
||||
mutationFn: ({ tenantId, planId }) => OrdersService.recalculateProcurementPlan(tenantId, planId),
|
||||
onSuccess: (data, variables) => {
|
||||
if (data.plan) {
|
||||
// Update the specific plan in cache
|
||||
queryClient.setQueryData(
|
||||
ordersKeys.procurementPlan(variables.tenantId, variables.planId),
|
||||
data.plan
|
||||
);
|
||||
}
|
||||
|
||||
// Invalidate plans list and dashboard
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersKeys.procurement(),
|
||||
predicate: (query) => {
|
||||
return JSON.stringify(query.queryKey).includes(variables.tenantId);
|
||||
},
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to approve a procurement plan
|
||||
*/
|
||||
export const useApproveProcurementPlan = (
|
||||
options?: UseMutationOptions<ProcurementPlanResponse, ApiError, { tenantId: string; planId: string; approval_notes?: string }>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<ProcurementPlanResponse, ApiError, { tenantId: string; planId: string; approval_notes?: string }>({
|
||||
mutationFn: ({ tenantId, planId, approval_notes }) =>
|
||||
OrdersService.approveProcurementPlan(tenantId, planId, { approval_notes }),
|
||||
onSuccess: (data, variables) => {
|
||||
// Update the specific plan in cache
|
||||
queryClient.setQueryData(
|
||||
ordersKeys.procurementPlan(variables.tenantId, variables.planId),
|
||||
data
|
||||
);
|
||||
|
||||
// Invalidate plans list and dashboard
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersKeys.procurement(),
|
||||
predicate: (query) => {
|
||||
return JSON.stringify(query.queryKey).includes(variables.tenantId);
|
||||
},
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to reject a procurement plan
|
||||
*/
|
||||
export const useRejectProcurementPlan = (
|
||||
options?: UseMutationOptions<ProcurementPlanResponse, ApiError, { tenantId: string; planId: string; rejection_notes?: string }>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<ProcurementPlanResponse, ApiError, { tenantId: string; planId: string; rejection_notes?: string }>({
|
||||
mutationFn: ({ tenantId, planId, rejection_notes }) =>
|
||||
OrdersService.rejectProcurementPlan(tenantId, planId, { rejection_notes }),
|
||||
onSuccess: (data, variables) => {
|
||||
// Update the specific plan in cache
|
||||
queryClient.setQueryData(
|
||||
ordersKeys.procurementPlan(variables.tenantId, variables.planId),
|
||||
data
|
||||
);
|
||||
|
||||
// Invalidate plans list and dashboard
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersKeys.procurement(),
|
||||
predicate: (query) => {
|
||||
return JSON.stringify(query.queryKey).includes(variables.tenantId);
|
||||
},
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to create purchase orders from procurement plan
|
||||
*/
|
||||
export const useCreatePurchaseOrdersFromPlan = (
|
||||
options?: UseMutationOptions<CreatePOsResult, ApiError, { tenantId: string; planId: string; autoApprove?: boolean }>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<CreatePOsResult, ApiError, { tenantId: string; planId: string; autoApprove?: boolean }>({
|
||||
mutationFn: ({ tenantId, planId, autoApprove = false }) =>
|
||||
OrdersService.createPurchaseOrdersFromPlan(tenantId, planId, autoApprove),
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate procurement plan to refresh requirements status
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersKeys.procurementPlan(variables.tenantId, variables.planId),
|
||||
});
|
||||
|
||||
// Invalidate dashboard
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersKeys.procurementDashboard(variables.tenantId),
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to link a requirement to a purchase order
|
||||
*/
|
||||
export const useLinkRequirementToPurchaseOrder = (
|
||||
options?: UseMutationOptions<
|
||||
{ success: boolean; message: string; requirement_id: string; purchase_order_id: string },
|
||||
ApiError,
|
||||
{ tenantId: string; requirementId: string; request: LinkRequirementToPORequest }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
{ success: boolean; message: string; requirement_id: string; purchase_order_id: string },
|
||||
ApiError,
|
||||
{ tenantId: string; requirementId: string; request: LinkRequirementToPORequest }
|
||||
>({
|
||||
mutationFn: ({ tenantId, requirementId, request }) =>
|
||||
OrdersService.linkRequirementToPurchaseOrder(tenantId, requirementId, request),
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate procurement data to refresh requirements
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersKeys.procurement(),
|
||||
predicate: (query) => {
|
||||
return JSON.stringify(query.queryKey).includes(variables.tenantId);
|
||||
},
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to update delivery status for a requirement
|
||||
*/
|
||||
export const useUpdateRequirementDeliveryStatus = (
|
||||
options?: UseMutationOptions<
|
||||
{ success: boolean; message: string; requirement_id: string; delivery_status: string },
|
||||
ApiError,
|
||||
{ tenantId: string; requirementId: string; request: UpdateDeliveryStatusRequest }
|
||||
>
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation<
|
||||
{ success: boolean; message: string; requirement_id: string; delivery_status: string },
|
||||
ApiError,
|
||||
{ tenantId: string; requirementId: string; request: UpdateDeliveryStatusRequest }
|
||||
>({
|
||||
mutationFn: ({ tenantId, requirementId, request }) =>
|
||||
OrdersService.updateRequirementDeliveryStatus(tenantId, requirementId, request),
|
||||
onSuccess: (data, variables) => {
|
||||
// Invalidate procurement data to refresh requirements
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersKeys.procurement(),
|
||||
predicate: (query) => {
|
||||
return JSON.stringify(query.queryKey).includes(variables.tenantId);
|
||||
},
|
||||
});
|
||||
},
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -661,6 +661,12 @@ export {
|
||||
useGenerateProcurementPlan,
|
||||
useUpdateProcurementPlanStatus,
|
||||
useTriggerDailyScheduler,
|
||||
useRecalculateProcurementPlan,
|
||||
useApproveProcurementPlan,
|
||||
useRejectProcurementPlan,
|
||||
useCreatePurchaseOrdersFromPlan,
|
||||
useLinkRequirementToPurchaseOrder,
|
||||
useUpdateRequirementDeliveryStatus,
|
||||
ordersKeys,
|
||||
} from './hooks/orders';
|
||||
|
||||
|
||||
@@ -34,6 +34,11 @@ import {
|
||||
GetProcurementPlansParams,
|
||||
GetPlanRequirementsParams,
|
||||
UpdatePlanStatusParams,
|
||||
CreatePOsResult,
|
||||
LinkRequirementToPORequest,
|
||||
UpdateDeliveryStatusRequest,
|
||||
ApprovalRequest,
|
||||
RejectionRequest,
|
||||
} from '../types/orders';
|
||||
|
||||
export class OrdersService {
|
||||
@@ -303,6 +308,82 @@ export class OrdersService {
|
||||
return apiClient.get<{ status: string; service: string; procurement_enabled: boolean; timestamp: string }>(`/tenants/${tenantId}/procurement/health`);
|
||||
}
|
||||
|
||||
// ===== NEW PROCUREMENT FEATURES =====
|
||||
|
||||
/**
|
||||
* Recalculate an existing procurement plan
|
||||
* POST /tenants/{tenant_id}/procurement/plans/{plan_id}/recalculate
|
||||
*/
|
||||
static async recalculateProcurementPlan(tenantId: string, planId: string): Promise<GeneratePlanResponse> {
|
||||
return apiClient.post<GeneratePlanResponse>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/recalculate`,
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve a procurement plan with notes
|
||||
* POST /tenants/{tenant_id}/procurement/plans/{plan_id}/approve
|
||||
*/
|
||||
static async approveProcurementPlan(tenantId: string, planId: string, request?: ApprovalRequest): Promise<ProcurementPlanResponse> {
|
||||
return apiClient.post<ProcurementPlanResponse>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/approve`,
|
||||
request || {}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reject a procurement plan with notes
|
||||
* POST /tenants/{tenant_id}/procurement/plans/{plan_id}/reject
|
||||
*/
|
||||
static async rejectProcurementPlan(tenantId: string, planId: string, request?: RejectionRequest): Promise<ProcurementPlanResponse> {
|
||||
return apiClient.post<ProcurementPlanResponse>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/reject`,
|
||||
request || {}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create purchase orders automatically from procurement plan
|
||||
* POST /tenants/{tenant_id}/procurement/plans/{plan_id}/create-purchase-orders
|
||||
*/
|
||||
static async createPurchaseOrdersFromPlan(tenantId: string, planId: string, autoApprove: boolean = false): Promise<CreatePOsResult> {
|
||||
return apiClient.post<CreatePOsResult>(
|
||||
`/tenants/${tenantId}/procurement/plans/${planId}/create-purchase-orders`,
|
||||
{ auto_approve: autoApprove }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link a procurement requirement to a purchase order
|
||||
* POST /tenants/{tenant_id}/procurement/requirements/{requirement_id}/link-purchase-order
|
||||
*/
|
||||
static async linkRequirementToPurchaseOrder(
|
||||
tenantId: string,
|
||||
requirementId: string,
|
||||
request: LinkRequirementToPORequest
|
||||
): Promise<{ success: boolean; message: string; requirement_id: string; purchase_order_id: string }> {
|
||||
return apiClient.post<{ success: boolean; message: string; requirement_id: string; purchase_order_id: string }>(
|
||||
`/tenants/${tenantId}/procurement/requirements/${requirementId}/link-purchase-order`,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update delivery status for a requirement
|
||||
* PUT /tenants/{tenant_id}/procurement/requirements/{requirement_id}/delivery-status
|
||||
*/
|
||||
static async updateRequirementDeliveryStatus(
|
||||
tenantId: string,
|
||||
requirementId: string,
|
||||
request: UpdateDeliveryStatusRequest
|
||||
): Promise<{ success: boolean; message: string; requirement_id: string; delivery_status: string }> {
|
||||
return apiClient.put<{ success: boolean; message: string; requirement_id: string; delivery_status: string }>(
|
||||
`/tenants/${tenantId}/procurement/requirements/${requirementId}/delivery-status`,
|
||||
request
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default OrdersService;
|
||||
@@ -479,6 +479,14 @@ export interface ProcurementPlanUpdate {
|
||||
seasonal_adjustments?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface ApprovalWorkflowEntry {
|
||||
timestamp: string;
|
||||
from_status: string;
|
||||
to_status: string;
|
||||
user_id?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export interface ProcurementPlanResponse extends ProcurementPlanBase {
|
||||
id: string;
|
||||
tenant_id: string;
|
||||
@@ -502,6 +510,7 @@ export interface ProcurementPlanResponse extends ProcurementPlanBase {
|
||||
on_time_delivery_rate?: number;
|
||||
cost_accuracy?: number;
|
||||
quality_score?: number;
|
||||
approval_workflow?: ApprovalWorkflowEntry[];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
created_by?: string;
|
||||
@@ -551,6 +560,46 @@ export interface GeneratePlanResponse {
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
// New Feature Types
|
||||
export interface CreatePOsResult {
|
||||
success: boolean;
|
||||
created_pos: {
|
||||
po_id: string;
|
||||
po_number: string;
|
||||
supplier_id: string;
|
||||
items_count: number;
|
||||
total_amount: number;
|
||||
}[];
|
||||
failed_pos: {
|
||||
supplier_id: string;
|
||||
error: string;
|
||||
}[];
|
||||
total_created: number;
|
||||
total_failed: number;
|
||||
}
|
||||
|
||||
export interface LinkRequirementToPORequest {
|
||||
purchase_order_id: string;
|
||||
purchase_order_number: string;
|
||||
ordered_quantity: number;
|
||||
expected_delivery_date?: string;
|
||||
}
|
||||
|
||||
export interface UpdateDeliveryStatusRequest {
|
||||
delivery_status: string;
|
||||
received_quantity?: number;
|
||||
actual_delivery_date?: string;
|
||||
quality_rating?: number;
|
||||
}
|
||||
|
||||
export interface ApprovalRequest {
|
||||
approval_notes?: string;
|
||||
}
|
||||
|
||||
export interface RejectionRequest {
|
||||
rejection_notes?: string;
|
||||
}
|
||||
|
||||
export interface PaginatedProcurementPlans {
|
||||
plans: ProcurementPlanResponse[];
|
||||
total: number;
|
||||
|
||||
Reference in New Issue
Block a user