sale order
SAP-ERP Portal CI/CD / build (push) Successful in 3m57s

This commit is contained in:
John
2026-10-05 18:45:17 +05:30
parent e725a6571b
commit eead8f5ffd
129 changed files with 14252 additions and 452 deletions
+184
View File
@@ -0,0 +1,184 @@
// services/sales/constants.js
// Status codes, approval steps and default "Sales User Type" permission
// templates for the Sales Order module. Status numbers deliberately match the
// old msale portal's sales_order.status column 1:1 so migrated orders keep
// their meaning without any remapping.
'use strict';
const S = {
CREATED: 1, // customer placed it; awaiting BUH/Tender review
LOGISTICS: 2, // sent to Logistics to confirm qty & dispatch time
QTY_CONFIRMED: 3, // Logistics confirmed (direct orders wait here for BUH)
APPROVED: 4, // approved — customer must pay
MODIFY: 5, // sent back for modification
PAYMENT_SUBMITTED: 6,// customer paid; Accounts must verify
READY_FOR_SAP: 7, // payment received / direct order placed → post to SAP
IN_SAP: 8, // SAP Sales Order created; invoicing in progress
CLOSED: 9, // fully invoiced / closed
CANCELLED: 10,
DELETED: 11,
};
// Employee-facing labels (direct orders read slightly differently, as in msale).
function statusLabel(status, orderType) {
const direct = orderType === 'DIRECT';
switch (Number(status)) {
case 1: return 'Review the sale order';
case 2: return 'To be confirmed by Logistics';
case 3: return 'Qty & dispatch time confirmed by Logistics';
case 4: return 'Approved — payment pending';
case 5: return direct ? 'Pending modification' : 'Pending modification by customer';
case 6: return 'Payment to be confirmed by Accounts';
case 7: return direct ? 'To be uploaded in SAP' : 'Payment received — to be uploaded in SAP';
case 8: return 'Uploaded in SAP';
case 9: return 'Closed';
case 10: return 'Cancelled';
case 11: return 'Deleted';
default: return 'Unknown';
}
}
// Customer-facing labels (msale get_so_status_type() with no user_type).
function customerStatusLabel(status) {
switch (Number(status)) {
case 1: case 2: case 3: return 'Order is under verification';
case 4: return 'Order is approved, please make payment';
case 5: return 'Modify order as required';
case 6: return 'Payment to be confirmed by MIPL';
case 7: case 8: return 'Invoice processing';
case 9: return 'Closed';
case 10: return 'Cancelled';
default: return 'Unknown';
}
}
const SO_TYPES = { 1: 'Trade', 2: 'Institution' };
// Value written to SAP ORDR.U_SalesType — matches what the old external job wrote.
function sapSalesType(soType, orderType) {
if (Number(soType) === 1) return 'Trade';
return orderType === 'DIRECT' ? 'Institute Dir' : 'Institute Ind';
}
// Sample request (domestic) statuses — msale sample_request.status.
const SAMPLE = { SUBMITTED: 1, APPROVED: 2, MODIFICATION: 3, REJECTED: 4, RESUBMITTED: 5, QA_PENDING: 6, IN_PRODUCTION: 7, SHIPPED: 8, COMPLETED: 9 };
function sampleStatusLabel(s) {
return ({ 1: 'Submitted', 2: 'Approved by BUH', 3: 'Modification requested', 4: 'Rejected', 5: 'Resubmitted after modification',
6: 'QA verification pending', 7: 'In production', 8: 'Shipped — feedback awaited', 9: 'Completed' })[Number(s)] || 'Unknown';
}
// Export sample statuses — msale sample_request_export.sample_status.
const EXPORT_SAMPLE = { SUBMITTED: 1, IN_PRODUCTION: 2, QA_PENDING: 3, DISPATCHED: 4 };
function exportSampleStatusLabel(s) {
return ({ 1: 'Submitted', 2: 'In production', 3: 'QA verification pending', 4: 'Dispatched' })[Number(s)] || 'Unknown';
}
// Approval steps registered in ZAPPROVAL_STEPS (see approvalStepsStore.js).
const STEPS = [
{ workflow: 'sales_order', key: 'view', label: 'View sales orders (own divisions)', order: 1 },
{ workflow: 'sales_order', key: 'create_direct', label: 'Create / modify Direct Order (on behalf of customer)', order: 2 },
{ workflow: 'sales_order', key: 'review', label: 'Review customer order (send to Logistics / back for modification)', order: 3 },
{ workflow: 'sales_order', key: 'logistics_confirm', label: 'Logistics — confirm qty & dispatch time', order: 4 },
{ workflow: 'sales_order', key: 'final_approve', label: 'Final approval after Logistics (Direct orders)', order: 5 },
{ workflow: 'sales_order', key: 'payment_verify', label: 'Accounts — verify customer payment', order: 6 },
{ workflow: 'sales_order', key: 'payment_entry', label: 'Accounts — on-account payment entry & credit limit', order: 7 },
{ workflow: 'sales_order', key: 'sap_post', label: 'Post order to SAP / close order', order: 8 },
{ workflow: 'sales_order', key: 'cancel', label: 'Cancel sales order', order: 9 },
{ workflow: 'sales_sample', key: 'view', label: 'View sample requests (domestic)', order: 1 },
{ workflow: 'sales_sample', key: 'create', label: 'Raise / modify sample request', order: 2 },
{ workflow: 'sales_sample', key: 'buh_review', label: 'BUH — approve / modify / reject sample', order: 3 },
{ workflow: 'sales_sample', key: 'logistics', label: 'Logistics — stock, QA verification, dispatch', order: 4 },
{ workflow: 'sales_sample', key: 'feedback', label: 'Sales — record customer feedback', order: 5 },
{ workflow: 'sales_export_sample', key: 'view', label: 'View export sample requests', order: 1 },
{ workflow: 'sales_export_sample', key: 'create', label: 'Raise / modify export sample request', order: 2 },
{ workflow: 'sales_export_sample', key: 'logistics', label: 'Logistics — stock, QA verification, dispatch', order: 3 },
{ workflow: 'sales_master', key: 'products', label: 'Maintain products & prices', order: 1 },
{ workflow: 'sales_master', key: 'mapping', label: 'Maintain sales person ↔ customer mapping', order: 2 },
{ workflow: 'sales_master', key: 'customers', label: 'Maintain customer portal logins', order: 3 },
];
const ALL = ['view', 'add', 'edit', 'approve', 'delete'];
const st = (step, perms) => ({ step, perms });
// Default permission template per msale user_type (ids kept identical so the
// employee_master.user_type migration is a straight copy). `scope`:
// all — every customer in the user's divisions
// mapped — only customers mapped to this user as a sales person (msale SM)
// own — only orders this user created (msale Tender)
// `listStatuses` = the default status filter on the order list (msale
// restricted Accounts to 6-9 and Logistics to 2,7,8,9).
const VIEW_ONLY_ORDERS = [st('sales_order:view', ['view'])];
const USER_TYPES = [
{ id: 1, name: 'Superuser', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-samples', 'sales-reports', 'sales-admin'],
steps: STEPS.map(s => st(`${s.workflow}:${s.key}`, ALL)) },
{ id: 2, name: 'Managing Director', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-samples', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_sample:view', ['view']), st('sales_export_sample:view', ['view'])] },
{ id: 3, name: 'Vice President', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-samples', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_sample:view', ['view']), st('sales_export_sample:view', ['view'])] },
{ id: 4, name: 'Account', scope: 'all', listStatuses: '6,7,8,9', modules: ['sales-orders', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_order:payment_verify', ['view', 'approve']), st('sales_order:payment_entry', ['view', 'add', 'edit']),
st('sales_order:sap_post', ['view', 'approve']), st('sales_order:cancel', ['view', 'delete'])] },
{ id: 5, name: 'Business Unit Head', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-samples', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_order:create_direct', ['view', 'add', 'edit']), st('sales_order:review', ['view', 'approve']),
st('sales_order:final_approve', ['view', 'approve']), st('sales_order:cancel', ['view', 'delete']),
st('sales_sample:view', ['view']), st('sales_sample:create', ['view', 'add', 'edit']), st('sales_sample:buh_review', ['view', 'approve'])] },
{ id: 6, name: 'Tender', scope: 'own', listStatuses: '', modules: ['sales-orders', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_order:create_direct', ['view', 'add', 'edit']), st('sales_order:review', ['view', 'approve']),
st('sales_order:final_approve', ['view', 'approve']), st('sales_order:cancel', ['view', 'delete'])] },
{ id: 7, name: 'Logistics', scope: 'all', listStatuses: '2,7,8,9', modules: ['sales-orders', 'sales-samples', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_order:logistics_confirm', ['view', 'approve']), st('sales_order:sap_post', ['view', 'approve']),
st('sales_order:cancel', ['view', 'delete']), st('sales_sample:view', ['view']), st('sales_sample:logistics', ['view', 'approve']),
st('sales_export_sample:view', ['view']), st('sales_export_sample:logistics', ['view', 'approve'])] },
{ id: 8, name: 'Sale Manager', scope: 'mapped', listStatuses: '', modules: ['sales-orders', 'sales-samples', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_sample:view', ['view']), st('sales_sample:create', ['view', 'add', 'edit']), st('sales_sample:feedback', ['view', 'approve'])] },
{ id: 9, name: 'National Sale Manager', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-reports'], steps: [...VIEW_ONLY_ORDERS] },
{ id: 10, name: 'Regional Sale Manager', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-reports'], steps: [...VIEW_ONLY_ORDERS] },
{ id: 11, name: 'Area Sale Manager', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-reports'], steps: [...VIEW_ONLY_ORDERS] },
{ id: 12, name: 'Zonal Sale Manager', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-samples', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_sample:view', ['view'])] },
{ id: 13, name: 'H (NP-CS)', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-reports'], steps: [...VIEW_ONLY_ORDERS] },
{ id: 14, name: 'Plant Head', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-samples', 'sales-reports'],
steps: [...VIEW_ONLY_ORDERS, st('sales_sample:view', ['view'])] },
{ id: 15, name: 'Production', scope: 'all', listStatuses: '', modules: ['sales-orders', 'sales-samples'],
steps: [...VIEW_ONLY_ORDERS, st('sales_sample:view', ['view'])] },
{ id: 16, name: 'IPQA', scope: 'all', listStatuses: '', modules: ['sales-samples'],
steps: [st('sales_sample:view', ['view']), st('sales_export_sample:view', ['view'])] },
{ id: 17, name: 'Export', scope: 'all', listStatuses: '', modules: ['sales-samples'],
steps: [st('sales_export_sample:view', ['view']), st('sales_export_sample:create', ['view', 'add', 'edit'])] },
{ id: 18, name: 'BUH-Export', scope: 'all', listStatuses: '', modules: ['sales-samples'],
steps: [st('sales_export_sample:view', ['view']), st('sales_export_sample:create', ['view', 'add', 'edit'])] },
];
const DEFAULT_DIVISIONS = [
{ id: 101, name: 'FG BLOOD BAG', code: 'TM', itemGroups: '101', warehouse: '01', sort: 1 },
{ id: 102, name: 'FG CAPD', code: 'PD', itemGroups: '102,143', warehouse: '01', sort: 2 },
{ id: 105, name: 'FG STENT', code: 'TT', itemGroups: '', warehouse: '01', sort: 3 },
{ id: 106, name: 'STENT ACCESSORIES', code: 'TT', itemGroups: '', warehouse: '01', sort: 4 },
];
// Module-wide settings (ZSO_SETTINGS). Defaults reproduce msale behaviour.
const DEFAULT_SETTINGS = {
company: '', // SAP company DB for this module ('' = .env default)
productSource: 'catalog', // 'catalog' (ERP product list) | 'sap' (OITM + SAP price list)
sapPriceList: 1, // SAP price list number when productSource = 'sap'
companyState: 'HR', // ship-to in this state → CGST+SGST, else IGST
intraTaxCode: 'NC2NS2', cgstRate: 2.5, sgstRate: 2.5,
interTaxCode: 'NIG5', igstRate: 5,
tcsRate: 0, // msale had TCS switched off
sapSeries: null, // SAP ORDR Series (null = SAP default)
autoPostToSap: false, // post automatically (shared .env SAP user) when an order reaches status 7
roundingTolerance: 5, // msale "Recede" — under-payment up to ₹5 written off as rebate
bankApiUrl: '', // optional bank payment auto-match API (msale sapapi/payment_details)
bankApiKey: '',
orderEmailCc: '', // extra CC on every order email
emailNotifications: true, // Sales-only email on/off (the ERP-wide switch must ALSO be on)
// Test mode: while ON, every Sales email goes ONLY to emailTestRecipient
// (subject prefixed [TEST], real intended recipients listed in the body).
// Turn OFF at go-live so customers/employees get their emails.
invoiceEmail: true, // email "invoice raised" to customer + all concerned when SAP invoices an order
invoiceEmailSince: '', // set at first deploy; invoices dated earlier are never emailed
emailTestMode: true,
emailTestRecipient: 'chandan.singh@mitraindustries.com',
};
module.exports = {
S, statusLabel, customerStatusLabel, SO_TYPES, sapSalesType,
SAMPLE, sampleStatusLabel, EXPORT_SAMPLE, exportSampleStatusLabel,
STEPS, USER_TYPES, DEFAULT_DIVISIONS, DEFAULT_SETTINGS,
};