566 lines
17 KiB
TypeScript
566 lines
17 KiB
TypeScript
import { apiFetch } from './client';
|
|
import { API_ENDPOINTS } from './config';
|
|
|
|
export interface BrandResponse {
|
|
brand_id: string;
|
|
name: string;
|
|
slug: string;
|
|
logo_url: string | null;
|
|
is_active: boolean;
|
|
device_types?: string[];
|
|
created_at: string;
|
|
}
|
|
|
|
export interface CategoryResponse {
|
|
category_id: string;
|
|
parent_category_id: string | null;
|
|
name: string;
|
|
slug: string;
|
|
description: string | null;
|
|
image_url: string | null;
|
|
sort_order: string;
|
|
is_parent_feature?: boolean;
|
|
is_active: boolean;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface PartResponse {
|
|
part_id: string;
|
|
sku: string;
|
|
name: string;
|
|
cost_price: number;
|
|
low_stock_alert: number;
|
|
supplier: string | null;
|
|
barcode: string | null;
|
|
is_active: boolean;
|
|
stock: number;
|
|
}
|
|
|
|
export interface DeviceSeriesResponse {
|
|
series_id: string;
|
|
brand_id: string;
|
|
name: string;
|
|
slug: string;
|
|
device_type?: string | null;
|
|
sort_order: number;
|
|
is_active: boolean;
|
|
}
|
|
|
|
export interface DeviceModelResponse {
|
|
model_id: string;
|
|
series_id: string;
|
|
brand_id: string;
|
|
name: string;
|
|
slug: string;
|
|
device_type?: string | null;
|
|
full_path: string;
|
|
release_year: number | null;
|
|
image_url: string | null;
|
|
is_active: boolean;
|
|
}
|
|
|
|
export interface ServiceTypeResponse {
|
|
service_type_id: string;
|
|
name: string;
|
|
slug: string;
|
|
icon_url: string | null;
|
|
description: string | null;
|
|
is_active: boolean;
|
|
}
|
|
|
|
export interface RepairServiceResponse {
|
|
repair_service_id: string;
|
|
model_id: string;
|
|
service_type_id: string;
|
|
slug: string;
|
|
full_path: string;
|
|
description: string | null;
|
|
}
|
|
|
|
export interface RepairVariantPartResponse {
|
|
id: string;
|
|
variant_id: string;
|
|
part_id: string;
|
|
quantity: number;
|
|
}
|
|
|
|
export interface RepairVariantResponse {
|
|
variant_id: string;
|
|
repair_service_id: string;
|
|
name: string;
|
|
price: number;
|
|
cost: number;
|
|
duration_minutes: number;
|
|
warranty_days: number;
|
|
status: string;
|
|
created_at: string;
|
|
bom_parts?: RepairVariantPartResponse[];
|
|
}
|
|
|
|
export interface StockMovementResponse {
|
|
movement_id: string;
|
|
entity_type: string;
|
|
entity_id: string;
|
|
movement_type: string;
|
|
quantity: number;
|
|
reference_type: string;
|
|
reference_id: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export interface PurchaseOrderItemResponse {
|
|
id: string;
|
|
part_id: string;
|
|
quantity_ordered: number;
|
|
quantity_received: number;
|
|
unit_price: number;
|
|
}
|
|
|
|
export interface PurchaseOrderResponse {
|
|
purchase_order_id: string;
|
|
po_number: string;
|
|
supplier_name: string;
|
|
status: string;
|
|
total_amount: number;
|
|
created_at: string;
|
|
items: PurchaseOrderItemResponse[];
|
|
}
|
|
|
|
export interface AttributeTypeResponse {
|
|
attribute_id: string;
|
|
name: string;
|
|
code: string;
|
|
status: string;
|
|
preset_values?: string[];
|
|
created_at?: string;
|
|
}
|
|
|
|
export const catalogService = {
|
|
// Brands
|
|
async getBrands(): Promise<BrandResponse[]> {
|
|
return apiFetch<BrandResponse[]>(API_ENDPOINTS.CATALOG_BRANDS_ALL, { skipAuth: true });
|
|
},
|
|
async createBrand(data: { name: string; logo_url?: string; device_types?: string[] }): Promise<BrandResponse> {
|
|
return apiFetch<BrandResponse>(API_ENDPOINTS.CATALOG_BRANDS_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Categories
|
|
async getCategories(): Promise<CategoryResponse[]> {
|
|
return apiFetch<CategoryResponse[]>(API_ENDPOINTS.CATALOG_CATEGORIES_ALL, { skipAuth: true });
|
|
},
|
|
async createCategory(data: {
|
|
name: string;
|
|
parent_category_id?: string | null;
|
|
description?: string;
|
|
image_url?: string;
|
|
sort_order?: string;
|
|
is_parent_feature?: boolean;
|
|
}): Promise<CategoryResponse> {
|
|
return apiFetch<CategoryResponse>(API_ENDPOINTS.CATALOG_CATEGORIES_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Parts (Inventory Items)
|
|
async getParts(): Promise<PartResponse[]> {
|
|
return apiFetch<PartResponse[]>(API_ENDPOINTS.CATALOG_PARTS_ALL, { skipAuth: true });
|
|
},
|
|
async createPart(data: {
|
|
sku: string;
|
|
name: string;
|
|
cost_price: number;
|
|
low_stock_alert?: number;
|
|
supplier?: string;
|
|
barcode?: string;
|
|
}): Promise<PartResponse> {
|
|
return apiFetch<PartResponse>(API_ENDPOINTS.CATALOG_PARTS_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Device Series
|
|
async getDeviceSeries(): Promise<DeviceSeriesResponse[]> {
|
|
return apiFetch<DeviceSeriesResponse[]>(API_ENDPOINTS.CATALOG_DEVICE_SERIES_ALL, { skipAuth: true });
|
|
},
|
|
async createDeviceSeries(data: { brand_id: string; name: string; device_type?: string | null; sort_order?: number }): Promise<DeviceSeriesResponse> {
|
|
return apiFetch<DeviceSeriesResponse>(API_ENDPOINTS.CATALOG_DEVICE_SERIES_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Device Models
|
|
async getDeviceModels(): Promise<DeviceModelResponse[]> {
|
|
return apiFetch<DeviceModelResponse[]>(API_ENDPOINTS.CATALOG_DEVICE_MODELS_ALL, { skipAuth: true });
|
|
},
|
|
async createDeviceModel(data: {
|
|
series_id?: string;
|
|
brand_id: string;
|
|
name: string;
|
|
device_type?: string | null;
|
|
release_year?: number;
|
|
image_url?: string;
|
|
}): Promise<DeviceModelResponse> {
|
|
return apiFetch<DeviceModelResponse>(API_ENDPOINTS.CATALOG_DEVICE_MODELS_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Service Types
|
|
async getServiceTypes(): Promise<ServiceTypeResponse[]> {
|
|
return apiFetch<ServiceTypeResponse[]>(API_ENDPOINTS.CATALOG_SERVICE_TYPES_ALL, { skipAuth: true });
|
|
},
|
|
async createServiceType(data: { name: string; icon_url?: string; description?: string }): Promise<ServiceTypeResponse> {
|
|
return apiFetch<ServiceTypeResponse>(API_ENDPOINTS.CATALOG_SERVICE_TYPES_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Repair Services
|
|
async getRepairServices(): Promise<RepairServiceResponse[]> {
|
|
return apiFetch<RepairServiceResponse[]>(API_ENDPOINTS.CATALOG_REPAIR_SERVICES_ALL, { skipAuth: true });
|
|
},
|
|
async createRepairService(data: { model_id: string; service_type_id: string; description?: string }): Promise<RepairServiceResponse> {
|
|
return apiFetch<RepairServiceResponse>(API_ENDPOINTS.CATALOG_REPAIR_SERVICES_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Repair Variants
|
|
async getRepairVariants(): Promise<RepairVariantResponse[]> {
|
|
return apiFetch<RepairVariantResponse[]>(API_ENDPOINTS.CATALOG_REPAIR_VARIANTS_ALL, { skipAuth: true });
|
|
},
|
|
async createRepairVariant(data: {
|
|
repair_service_id: string;
|
|
name: string;
|
|
price: number;
|
|
cost: number;
|
|
duration_minutes?: number;
|
|
warranty_days?: number;
|
|
parts: Array<{ part_id: string; quantity?: number }>;
|
|
}): Promise<RepairVariantResponse> {
|
|
return apiFetch<RepairVariantResponse>(API_ENDPOINTS.CATALOG_REPAIR_VARIANTS_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Stock Ledger
|
|
async getStockHistory(): Promise<StockMovementResponse[]> {
|
|
return apiFetch<StockMovementResponse[]>(API_ENDPOINTS.CATALOG_STOCK_HISTORY, { skipAuth: true });
|
|
},
|
|
async adjustStock(data: {
|
|
entity_type: 'variant' | 'part';
|
|
entity_id: string;
|
|
movement_type: 'Adjustment' | 'Damage';
|
|
quantity: number;
|
|
reference_type: 'ManualAdjustment';
|
|
reference_id: string;
|
|
}): Promise<StockMovementResponse> {
|
|
return apiFetch<StockMovementResponse>(API_ENDPOINTS.CATALOG_STOCK_ADJUST, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Purchase Orders
|
|
async getPurchaseOrders(): Promise<PurchaseOrderResponse[]> {
|
|
return apiFetch<PurchaseOrderResponse[]>(API_ENDPOINTS.CATALOG_PO_ALL, { skipAuth: true });
|
|
},
|
|
async createPurchaseOrder(data: {
|
|
supplier_name: string;
|
|
items: Array<{ part_id: string; quantity_ordered: number; unit_price: number }>;
|
|
}): Promise<PurchaseOrderResponse> {
|
|
return apiFetch<PurchaseOrderResponse>(API_ENDPOINTS.CATALOG_PO_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async receivePurchaseOrder(
|
|
poId: string,
|
|
itemsReceived: Array<{ id: string; quantity_received: number }>
|
|
): Promise<PurchaseOrderResponse> {
|
|
return apiFetch<PurchaseOrderResponse>(API_ENDPOINTS.CATALOG_PO_RECEIVE(poId), {
|
|
method: 'POST',
|
|
body: JSON.stringify({ items_received: itemsReceived }),
|
|
});
|
|
},
|
|
|
|
// Attributes
|
|
async getAttributes(): Promise<AttributeTypeResponse[]> {
|
|
return apiFetch<AttributeTypeResponse[]>(API_ENDPOINTS.CATALOG_ATTRIBUTES_ALL, { skipAuth: true });
|
|
},
|
|
async createAttribute(data: { name: string; code: string; preset_values?: string[] }): Promise<AttributeTypeResponse> {
|
|
return apiFetch<AttributeTypeResponse>(API_ENDPOINTS.CATALOG_ATTRIBUTES_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async updateAttribute(attributeId: string, data: { name?: string; code?: string; preset_values?: string[] }): Promise<AttributeTypeResponse> {
|
|
return apiFetch<AttributeTypeResponse>(API_ENDPOINTS.CATALOG_ATTRIBUTES_UPDATE(attributeId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
// Products
|
|
async getProducts(params: { page?: number; limit?: number; search?: string } = {}): Promise<{
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
products: ProductResponse[];
|
|
}> {
|
|
const query = new URLSearchParams();
|
|
query.set('page', String(params.page || 1));
|
|
query.set('limit', String(params.limit || 50));
|
|
if (params.search) query.set('search', params.search);
|
|
return apiFetch(`${API_ENDPOINTS.CATALOG_PRODUCTS_ALL}?${query.toString()}`, { skipAuth: true });
|
|
},
|
|
async getSellableSkus(params: { page?: number; limit?: number; q?: string } = {}): Promise<{
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
items: SellableSkuRow[];
|
|
}> {
|
|
const query = new URLSearchParams();
|
|
query.set('page', String(params.page || 1));
|
|
query.set('limit', String(params.limit || 50));
|
|
if (params.q) query.set('q', params.q);
|
|
return apiFetch(`${API_ENDPOINTS.INVENTORY_SKUS}?${query.toString()}`);
|
|
},
|
|
async adjustSellableStock(data: {
|
|
variant_id: string;
|
|
event_type: string;
|
|
qty: number;
|
|
notes?: string;
|
|
}): Promise<{ message: string; ledger_id: string }> {
|
|
return apiFetch(API_ENDPOINTS.INVENTORY_ADJUST, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async createProduct(data: {
|
|
category_id: string;
|
|
brand_id?: string | null;
|
|
device_series_id?: string | null;
|
|
device_model_id?: string | null;
|
|
name: string;
|
|
description?: string;
|
|
warranty_type?: string;
|
|
warranty_summary?: string;
|
|
seo_title?: string;
|
|
seo_description?: string;
|
|
meta_keywords?: string;
|
|
images?: Array<{ image_url: string; alt_text?: string; sort_order?: number; is_banner?: boolean }>;
|
|
variants: Array<{
|
|
sku: string;
|
|
barcode?: string;
|
|
price: number;
|
|
compare_price?: number;
|
|
cost_price: number;
|
|
low_stock_threshold?: number;
|
|
initial_stock?: number;
|
|
attributes: Array<{ attribute_id: string; attribute_value: string }>;
|
|
images?: Array<{ image_url: string; sort_order?: number; is_primary?: boolean }>;
|
|
}>;
|
|
}): Promise<ProductResponse> {
|
|
return apiFetch<ProductResponse>(API_ENDPOINTS.CATALOG_PRODUCTS_CREATE, {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
|
|
async bulkDeleteProducts(productIds: string[]): Promise<{ status: string; deleted_products_count: number; deleted_images_count: number; message: string }> {
|
|
return apiFetch('/api/v1/catalog/products/bulk-delete', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ product_ids: productIds }),
|
|
});
|
|
},
|
|
|
|
async updateBrand(brandId: string, data: { name: string; logo_url?: string; is_active?: boolean; device_types?: string[] }): Promise<BrandResponse> {
|
|
return apiFetch<BrandResponse>(API_ENDPOINTS.CATALOG_BRANDS_UPDATE(brandId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async updateCategory(categoryId: string, data: {
|
|
name?: string;
|
|
parent_category_id?: string | null;
|
|
description?: string;
|
|
image_url?: string;
|
|
sort_order?: string;
|
|
is_active?: boolean;
|
|
is_parent_feature?: boolean;
|
|
}): Promise<CategoryResponse> {
|
|
return apiFetch<CategoryResponse>(API_ENDPOINTS.CATALOG_CATEGORIES_UPDATE(categoryId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async updateDeviceSeries(seriesId: string, data: { brand_id?: string; name?: string; device_type?: string | null; sort_order?: number; is_active?: boolean }): Promise<DeviceSeriesResponse> {
|
|
return apiFetch<DeviceSeriesResponse>(API_ENDPOINTS.CATALOG_DEVICE_SERIES_UPDATE(seriesId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async updateDeviceModel(modelId: string, data: {
|
|
series_id?: string;
|
|
brand_id?: string;
|
|
name?: string;
|
|
device_type?: string | null;
|
|
release_year?: number;
|
|
image_url?: string;
|
|
is_active?: boolean;
|
|
}): Promise<DeviceModelResponse> {
|
|
return apiFetch<DeviceModelResponse>(API_ENDPOINTS.CATALOG_DEVICE_MODELS_UPDATE(modelId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async updateServiceType(serviceTypeId: string, data: { name?: string; icon_url?: string; description?: string; is_active?: boolean }): Promise<ServiceTypeResponse> {
|
|
return apiFetch<ServiceTypeResponse>(API_ENDPOINTS.CATALOG_SERVICE_TYPES_UPDATE(serviceTypeId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async updateRepairService(repairServiceId: string, data: { model_id?: string; service_type_id?: string; description?: string }): Promise<RepairServiceResponse> {
|
|
return apiFetch<RepairServiceResponse>(API_ENDPOINTS.CATALOG_REPAIR_SERVICES_UPDATE(repairServiceId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async updateRepairVariant(variantId: string, data: {
|
|
repair_service_id?: string;
|
|
name?: string;
|
|
price?: number;
|
|
cost?: number;
|
|
duration_minutes?: number;
|
|
warranty_days?: number;
|
|
parts?: Array<{ part_id: string; quantity?: number }>;
|
|
status?: string;
|
|
}): Promise<RepairVariantResponse> {
|
|
return apiFetch<RepairVariantResponse>(API_ENDPOINTS.CATALOG_REPAIR_VARIANTS_UPDATE(variantId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async updateProduct(productId: string, data: any): Promise<ProductResponse> {
|
|
return apiFetch<ProductResponse>(API_ENDPOINTS.CATALOG_PRODUCTS_UPDATE(productId), {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
},
|
|
async deleteProduct(id: string): Promise<any> {
|
|
return apiFetch<any>(API_ENDPOINTS.CATALOG_PRODUCTS_DELETE(id), { method: 'DELETE' });
|
|
},
|
|
async deleteCategory(id: string): Promise<any> {
|
|
return apiFetch<any>(API_ENDPOINTS.CATALOG_CATEGORIES_DELETE(id), { method: 'DELETE' });
|
|
},
|
|
async deleteBrand(id: string): Promise<any> {
|
|
return apiFetch<any>(API_ENDPOINTS.CATALOG_BRANDS_DELETE(id), { method: 'DELETE' });
|
|
},
|
|
async deleteDeviceSeries(id: string): Promise<any> {
|
|
return apiFetch<any>(API_ENDPOINTS.CATALOG_DEVICE_SERIES_DELETE(id), { method: 'DELETE' });
|
|
},
|
|
async deleteDeviceModel(id: string): Promise<any> {
|
|
return apiFetch<any>(API_ENDPOINTS.CATALOG_DEVICE_MODELS_DELETE(id), { method: 'DELETE' });
|
|
},
|
|
async deleteAttribute(id: string): Promise<any> {
|
|
return apiFetch<any>(API_ENDPOINTS.CATALOG_ATTRIBUTES_DELETE(id), { method: 'DELETE' });
|
|
},
|
|
};
|
|
|
|
export interface AttributeTypeResponse {
|
|
attribute_id: string;
|
|
name: string;
|
|
code: string;
|
|
status: string;
|
|
preset_values?: string[];
|
|
created_at?: string;
|
|
}
|
|
|
|
export interface VariantAttributeResponse {
|
|
id: string;
|
|
variant_id: string;
|
|
attribute_id: string;
|
|
attribute_value: string;
|
|
}
|
|
|
|
export interface ProductImageResponse {
|
|
image_id: string;
|
|
product_id: string;
|
|
image_url: string;
|
|
alt_text: string | null;
|
|
sort_order: number;
|
|
is_banner: boolean;
|
|
}
|
|
|
|
export interface VariantImageResponse {
|
|
image_id: string;
|
|
variant_id: string;
|
|
image_url: string;
|
|
sort_order: number;
|
|
is_primary: boolean;
|
|
}
|
|
|
|
export interface ProductVariantResponse {
|
|
variant_id: string;
|
|
product_id: string;
|
|
sku: string;
|
|
barcode: string | null;
|
|
price: number;
|
|
compare_price: number | null;
|
|
cost_price: number;
|
|
low_stock_threshold: number;
|
|
status: string;
|
|
available_stock?: number;
|
|
attributes?: VariantAttributeResponse[];
|
|
images?: VariantImageResponse[];
|
|
}
|
|
|
|
export interface SellableSkuRow {
|
|
variant_id: string;
|
|
product_id: string;
|
|
product_name: string;
|
|
sku: string;
|
|
barcode: string | null;
|
|
price: number;
|
|
cost_price: number;
|
|
available_stock: number;
|
|
pending_confirmation_units?: number;
|
|
confirmed_units?: number;
|
|
low_stock_threshold: number;
|
|
status: string;
|
|
is_low: boolean;
|
|
}
|
|
|
|
export interface ProductResponse {
|
|
product_id: string;
|
|
category_id: string;
|
|
brand_id: string | null;
|
|
device_series_id: string | null;
|
|
device_model_id: string | null;
|
|
device_type?: string | null;
|
|
name: string;
|
|
slug: string;
|
|
full_path: string;
|
|
description: string | null;
|
|
warranty_type?: string | null;
|
|
warranty_summary?: string | null;
|
|
seo_title?: string | null;
|
|
seo_description?: string | null;
|
|
meta_keywords?: string | null;
|
|
show_specifications?: boolean;
|
|
status: string;
|
|
created_at: string;
|
|
images: ProductImageResponse[];
|
|
variants: ProductVariantResponse[];
|
|
}
|