new updates

This commit is contained in:
2026-09-17 14:22:45 +05:30
committed by ImanThiyanga
parent e1f19fe413
commit 8c3d62223c
261 changed files with 102091 additions and 549 deletions
+9 -3
View File
@@ -5,6 +5,7 @@ import { ApiResult, PagedResponse } from "@/types/common"
import {
CreateEmployeeRequest,
EmployeeBankDetail,
EmployeeDeleteResult,
EmployeeDetail,
EmployeeDocument,
EmployeeListItem,
@@ -59,9 +60,14 @@ export const employeesApi = {
updateStatus(employeeId: number, status: EmployeeStatus): Promise<void> {
return apiRequest<void>(`/employees/${employeeId}/status`, { method: "PATCH", body: { status } })
},
/** Permanently deletes the employee and every dependent HR record. Blocked (409) if payroll history exists. */
remove(employeeId: number): Promise<void> {
return apiRequest<void>(`/employees/${employeeId}`, { method: "DELETE" })
/**
* Permanently deletes the employee and every dependent HR record. Blocked (409) if payroll
* history exists. `deleteUserAccount` asks for a linked login to be hard-deleted too, instead
* of the default (deactivate) — the backend falls back to deactivating if the account has
* real linked business/audit records, reflected in the returned result.
*/
remove(employeeId: number, deleteUserAccount = false): Promise<EmployeeDeleteResult> {
return apiRequest<EmployeeDeleteResult>(`/employees/${employeeId}${buildQuery({ deleteUserAccount })}`, { method: "DELETE" })
},
/** Advisory: does a System User already exist with this email? */
+13
View File
@@ -7,6 +7,7 @@ import {
Item,
ItemListItem,
ItemReorderSetting,
ItemScanResult,
TrackingMode,
UpdateItemReorderRequest,
UpdateItemRequest,
@@ -57,4 +58,16 @@ export const itemsApi = {
body: request,
})
},
/**
* Resolves a scanned barcode/RFID code (HID scanner keyboard input) against an item SKU,
* a batch number, or a serial number — whichever it matches. `itemId` narrows the lookup
* to a specific item's own batches/serials (e.g. scanning a lot barcode for a line that
* already has its item picked). Throws `ApiError` with status 404 when the code matches
* nothing — callers should catch that case and show an inline "unknown barcode" message
* rather than a generic error (see components/scanning/ScanInput.tsx usage).
*/
scan(code: string, itemId?: number): Promise<ItemScanResult> {
return apiRequest<ItemScanResult>(`/items/scan${buildQuery({ code, itemId })}`)
},
}
@@ -0,0 +1,12 @@
import { apiRequest } from "@/lib/api-client"
import { NotificationFeed } from "@/types/notification-feed"
export const notificationsApi = {
getFeed(take = 20): Promise<NotificationFeed> {
return apiRequest<NotificationFeed>(`/notifications?take=${take}`)
},
markAllRead(): Promise<void> {
return apiRequest<void>("/notifications/mark-all-read", { method: "POST" })
},
}
+11 -3
View File
@@ -8,6 +8,7 @@ import {
PurchaseOrder,
PurchaseOrderStatus,
PurchaseOrderSummary,
RejectPurchaseOrderRequest,
UpdatePurchaseOrderRequest,
} from "@/types/procurement"
@@ -39,7 +40,7 @@ export const purchaseOrdersApi = {
return apiRequestWithETag<PurchaseOrder>(`/purchase-orders/${poId}`)
},
/** Auto-approved on creation in Phase 1 (approvalRequired defaults false, FR-PROC-04). */
/** Non-draft is issued straight into PendingApproval (FR-PROC-04). */
create(request: CreatePurchaseOrderRequest): Promise<ApiResult<PurchaseOrder>> {
return apiRequestWithETag<PurchaseOrder>("/purchase-orders", { method: "POST", body: request })
},
@@ -49,12 +50,19 @@ export const purchaseOrdersApi = {
return apiRequestWithETag<PurchaseOrder>(`/purchase-orders/${poId}`, { method: "PUT", body: request, ifMatch })
},
/** No-op in Phase 1 unless approvals are enabled (FR-PROC-04). */
/** PendingApproval → Approved. 403 PO_APPROVAL_FORBIDDEN if the caller isn't an Admin or
* is the PO's own creator; 409 PO_NOT_PENDING_APPROVAL otherwise. */
approve(poId: number): Promise<PurchaseOrder> {
return apiRequest<PurchaseOrder>(`/purchase-orders/${poId}/approve`, { method: "POST" })
},
/** Submit a Draft PO (Draft → Approved). 409 PO_NOT_EDITABLE if not Draft. */
/** PendingApproval → Draft, so the requester can revise and resubmit. Same caller
* restrictions as {@link approve}. */
reject(poId: number, request: RejectPurchaseOrderRequest = {}): Promise<PurchaseOrder> {
return apiRequest<PurchaseOrder>(`/purchase-orders/${poId}/reject`, { method: "POST", body: request })
},
/** Submit a Draft PO (Draft → PendingApproval). 409 PO_NOT_EDITABLE if not Draft. */
submit(poId: number): Promise<PurchaseOrder> {
return apiRequest<PurchaseOrder>(`/purchase-orders/${poId}/submit`, { method: "POST" })
},
+8
View File
@@ -0,0 +1,8 @@
import { apiRequest } from "@/lib/api-client"
import { GlobalSearchResult } from "@/types/search"
export const searchApi = {
search(q: string): Promise<GlobalSearchResult[]> {
return apiRequest<GlobalSearchResult[]>(`/search?q=${encodeURIComponent(q)}`)
},
}
@@ -0,0 +1,31 @@
import { apiRequest, apiRequestWithETag } from "@/lib/api-client"
import { ApiResult } from "@/types/common"
import {
AddSupportTicketMessageRequest,
CreateSupportTicketRequest,
SupportTicket,
SupportTicketSummary,
UpdateSupportTicketStatusRequest,
} from "@/types/support"
export const supportTicketsApi = {
list(): Promise<SupportTicketSummary[]> {
return apiRequest<SupportTicketSummary[]>("/support-tickets")
},
get(ticketId: number): Promise<ApiResult<SupportTicket>> {
return apiRequestWithETag<SupportTicket>(`/support-tickets/${ticketId}`)
},
create(request: CreateSupportTicketRequest): Promise<ApiResult<SupportTicket>> {
return apiRequestWithETag<SupportTicket>("/support-tickets", { method: "POST", body: request })
},
addMessage(ticketId: number, request: AddSupportTicketMessageRequest, ifMatch: string): Promise<ApiResult<SupportTicket>> {
return apiRequestWithETag<SupportTicket>(`/support-tickets/${ticketId}/messages`, { method: "POST", body: request, ifMatch })
},
updateStatus(ticketId: number, request: UpdateSupportTicketStatusRequest, ifMatch: string): Promise<ApiResult<SupportTicket>> {
return apiRequestWithETag<SupportTicket>(`/support-tickets/${ticketId}/status`, { method: "PUT", body: request, ifMatch })
},
}
+34
View File
@@ -0,0 +1,34 @@
// One typed client method per TaxRate endpoint (ERPCore Controllers/TaxRatesController.cs).
import { apiRequest, apiRequestWithETag, buildQuery } from "@/lib/api-client"
import { ApiResult, EntityStatus, PagedResponse } from "@/types/common"
import { CreateTaxRateRequest, TaxRate, UpdateTaxRateRequest } from "@/types/master-data"
export interface ListTaxRatesParams {
page?: number
pageSize?: number
q?: string
status?: EntityStatus
}
export const taxRatesApi = {
list(params: ListTaxRatesParams = {}): Promise<PagedResponse<TaxRate>> {
return apiRequest<PagedResponse<TaxRate>>(`/tax-rates${buildQuery(params)}`)
},
get(taxRateId: number): Promise<ApiResult<TaxRate>> {
return apiRequestWithETag<TaxRate>(`/tax-rates/${taxRateId}`)
},
create(request: CreateTaxRateRequest): Promise<ApiResult<TaxRate>> {
return apiRequestWithETag<TaxRate>("/tax-rates", { method: "POST", body: request })
},
update(taxRateId: number, request: UpdateTaxRateRequest, ifMatch: string): Promise<ApiResult<TaxRate>> {
return apiRequestWithETag<TaxRate>(`/tax-rates/${taxRateId}`, { method: "PUT", body: request, ifMatch })
},
/** Deactivate/reactivate. There is no DELETE — masters referenced by items are deactivated, never removed. */
updateStatus(taxRateId: number, status: EntityStatus): Promise<void> {
return apiRequest<void>(`/tax-rates/${taxRateId}/status`, { method: "PATCH", body: { status } })
},
}
+19 -1
View File
@@ -3,7 +3,15 @@
import { apiRequest, buildQuery } from "@/lib/api-client"
import { EntityStatus, PagedResponse } from "@/types/common"
import { LoginActivity } from "@/types/audit"
import { CreateUserRequest, ManagedUser, UpdateUserRoleRequest, UpdateUserStatusRequest, UserTypeOption } from "@/types/users"
import {
CreateUserRequest,
ManagedUser,
PurgeAuthHexIdentityRequest,
PurgeAuthHexIdentityResult,
UpdateUserRoleRequest,
UpdateUserStatusRequest,
UserTypeOption,
} from "@/types/users"
export interface ListUsersParams {
page?: number
@@ -43,4 +51,14 @@ export const usersApi = {
loginDevices(userId: number, params: { page?: number; pageSize?: number } = {}): Promise<PagedResponse<LoginActivity>> {
return apiRequest<PagedResponse<LoginActivity>>(`/users/${userId}/login-devices${buildQuery(params)}`)
},
/**
* Admin-only escape hatch: permanently deletes a stale AuthHex identity blocking new
* registrations (e.g. an email/username that survived a local User delete because the
* automatic purge on that delete failed or wasn't deployed). Doesn't touch ERPCore's
* own `users` table — there may be nothing there to touch.
*/
purgeAuthHexIdentity(request: PurgeAuthHexIdentityRequest): Promise<PurgeAuthHexIdentityResult> {
return apiRequest<PurgeAuthHexIdentityResult>("/users/purge-authhex-identity", { method: "POST", body: request })
},
}
+12 -3
View File
@@ -1,12 +1,13 @@
// One typed client method per warehouse/bin endpoint (docs/11-BACKEND-PHASE1.md §2.5,
// FR-MD-07/FR-WH-01).
//
// Warehouses are create-and-list only: there is no PUT, no status, and no ETag on them.
// Warehouses have no status/ETag, but do support update and delete (hard delete, blocked
// with a 409 if the warehouse is still referenced by bins/stock/documents).
import { apiRequest, buildQuery } from "@/lib/api-client"
import { PagedResponse } from "@/types/common"
import { Bin, CreateBinRequest, CreateWarehouseRequest, Warehouse } from "@/types/master-data"
import { Bin, CreateBinRequest, CreateWarehouseRequest, UpdateWarehouseRequest, Warehouse } from "@/types/master-data"
export type { CreateBinRequest, CreateWarehouseRequest }
export type { CreateBinRequest, CreateWarehouseRequest, UpdateWarehouseRequest }
export interface ListWarehousesParams {
page?: number
@@ -29,6 +30,14 @@ export const warehousesApi = {
return apiRequest<Warehouse>("/warehouses", { method: "POST", body: request })
},
update(warehouseId: number, request: UpdateWarehouseRequest): Promise<Warehouse> {
return apiRequest<Warehouse>(`/warehouses/${warehouseId}`, { method: "PUT", body: request })
},
remove(warehouseId: number): Promise<void> {
return apiRequest<void>(`/warehouses/${warehouseId}`, { method: "DELETE" })
},
/** Returns a bare array, not a paged envelope — the server does not page bins. */
listBins(warehouseId: number): Promise<Bin[]> {
return apiRequest<Bin[]>(`/warehouses/${warehouseId}/bins`)
+3
View File
@@ -51,6 +51,8 @@ export interface ListWastageParams {
export interface RecordWastageInput {
warehouseId: number
/** Required when branching is enabled (mirrors `CreateAdjustmentRequest.branchId`). */
branchId: number
itemId: number
binId?: number | null
qty: number
@@ -117,6 +119,7 @@ export const wastageApi = {
record(input: RecordWastageInput): Promise<StockAdjustment> {
return stockAdjustmentsApi.create({
warehouseId: input.warehouseId,
branchId: input.branchId,
reasonCodeId: input.reasonCodeId,
lines: [{ itemId: input.itemId, binId: input.binId ?? null, qtyDelta: -Math.abs(input.qty) }],
})