Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions api/src/helpers/bill-payload.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,7 @@ function taxRows(sale, branch, allocation = null) {
const taxMinor = money.toMinor(tax, policy);
const firstHalf = Math.ceil(taxMinor / 2);
const rate = gstRate(sale);
const stateKey = (value) => {
const key = String(value || '')
.trim()
.toLowerCase();
const match = require('../json/gst_state_code.json').gststate.find(
(row) => row.id === key.padStart(2, '0') || row.value.toLowerCase() === key
);
return match ? match.id : key;
};
const { stateKey } = require('./gst-place');
const supplierState = stateKey(sale.branch_state || branch.state);
const supplyState = stateKey(sale.place_of_supply || sale.customer_state);
if (supplierState && supplyState && supplierState !== supplyState) {
Expand Down
29 changes: 29 additions & 0 deletions api/src/helpers/gst-place.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { gststate } = require('../json/gst_state_code.json');

function stateKey(value) {
const key = String(value || '')
.trim()
.toLowerCase()
.replace(/\s+/g, ' ');
const state = gststate.find(
(row) => row.id === key.padStart(2, '0') || row.value.toLowerCase() === key
);
return state ? state.id : key;
}

function isInterState(supplierState, customerState) {
const supplier = stateKey(supplierState);
const customer = stateKey(customerState);
return Boolean(supplier && customer && supplier !== customer);
}

function customerState(data, branch, customer) {
const supplier = String(branch.state || '').trim();
const isDefault =
!data.customer_id ||
(branch.default_customer && String(data.customer_id) === String(branch.default_customer));
if (isDefault) return supplier;
return String(data.customer_state || customer?.state || '').trim() || supplier;
}

module.exports = { stateKey, isInterState, customerState };
8 changes: 4 additions & 4 deletions api/src/repositories/sale.repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -949,10 +949,10 @@ class SalesRepository {
const customerState = (doc.customer_state || '').toString().trim();
const branchState = (branchDoc?.state || '').toString().trim();

const isInterState =
customerState &&
branchState &&
customerState.toLowerCase() !== branchState.toLowerCase();
const isInterState = require('../helpers/gst-place').isInterState(
branchState,
customerState
);

if (isInterState) {
cloned.igst_tax = perLineGstAmount;
Expand Down
11 changes: 9 additions & 2 deletions api/src/services/sale.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,13 @@ const processSale = async (
const pricingCustomer = data.customer_id
? await customerRepository.findById(data.customer_id)
: null;
const gstPlace = require('../helpers/gst-place');
const supplierState = context.branchState || pricingBranch.state || '';
const saleCustomerState = gstPlace.customerState(
data,
{ ...pricingBranch, state: supplierState },
pricingCustomer
);
let priceList;
if (pricingCustomer?.category_id) {
const pricingDb = await BaseModel.getDb();
Expand Down Expand Up @@ -662,7 +669,7 @@ const processSale = async (

const indianGstSetting = context.branchSettings?.indian_gst || 'gst_off';

const interPlace = (data.customer_state || '').trim() !== (context.branchState || '').trim();
const interPlace = gstPlace.isInterState(supplierState, saleCustomerState);
if (indianGstSetting === 'gst_on' && gstValue > 0) {
if (interPlace) {
igst_value = gstValue;
Expand Down Expand Up @@ -1274,7 +1281,7 @@ const processSale = async (
}
: {}),
customer_email: (data.customer_email || '').trim(),
customer_state: (data.customer_state || '').trim(),
customer_state: saleCustomerState,
customer_country: (data.customer_country || '').trim(),
country_sort: finalCountrySort,
customer_gst_type: (data.customer_gst_type || '').trim(),
Expand Down
23 changes: 23 additions & 0 deletions api/tests/unit/helpers/gst-place.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const { stateKey, isInterState, customerState } = require('../../../src/helpers/gst-place');

test.each([
['Tamil Nadu', '33', false],
['33', ' tamil nadu ', false],
['Tamil Nadu', 'Karnataka', true],
['33', '29', true],
['33', '', false],
['', '29', false],
])('GST state comparison %s / %s is interstate: %s', (supplier, customer, expected) => {
expect(isInterState(supplier, customer)).toBe(expected);
});

test('walk-in and configured default customers use the branch state', () => {
const branch = { state: 'Tamil Nadu', default_customer: 'walkin-id' };
expect(customerState({}, branch)).toBe('Tamil Nadu');
expect(customerState({ customer_id: 'walkin-id', customer_state: '29' }, branch)).toBe(
'Tamil Nadu'
);
expect(customerState({ customer_id: 'named-id' }, branch, { state: '29' })).toBe('29');
expect(customerState({ customer_id: 'named-id' }, branch)).toBe('Tamil Nadu');
expect(stateKey(' 33 ')).toBe('33');
});
40 changes: 40 additions & 0 deletions api/tests/unit/services/sale.service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,46 @@ describe('SalesService', () => {
mockRegisterRepositoryInstance.validateSessionOwner.mockResolvedValue({ status: true });
});

test.each([
['', undefined, '', false],
['64f8f2f4c2b9c0a1e4b22222', '', '', false],
['64f8f2f4c2b9c0a1e4b22222', '33', '', false],
['64f8f2f4c2b9c0a1e4b22222', ' tamil nadu ', '', false],
['64f8f2f4c2b9c0a1e4b22222', 'Karnataka', '', true],
['64f8f2f4c2b9c0a1e4b22222', '29', '', true],
['64f8f2f4c2b9c0a1e4b22222', '29', '64f8f2f4c2b9c0a1e4b22222', false],
])(
'saves GST by customer state (%s, %s, default %s)',
async (customer_id, customer_state, default_customer, inter) => {
mockItemRepositoryInstance.findItemById.mockResolvedValue(
makeItemDoc({ selling_price: 100, tax: 18, tax_type: 'exclusive' })
);
const result = await salesService.processSale(
makeSaleData({
customer_id,
customer_state,
sales_total: '236',
multi_payment: { Cash: 236 },
}),
'',
'Add',
makeContext({
branchState: 'Tamil Nadu',
branchSettings: { sortname: 'IN', indian_gst: 'gst_on', default_customer },
})
);
expect(result.status).toBe(true);
const saved = salesRepository.create.mock.calls[0][0];
expect(saved.items[0].igst_tax).toBe(inter ? 36 : 0);
expect(saved.items[0].cgst_tax).toBe(inter ? 0 : 18);
expect(saved.items[0].sgst_tax).toBe(inter ? 0 : 18);
expect(saved.items[0].tax_components.map((component) => component.name)).toEqual(
inter ? ['IGST'] : ['CGST', 'SGST']
);
if (!customer_state || default_customer) expect(saved.customer_state).toBe('Tamil Nadu');
}
);

afterEach(() => {
process.removeListener(KOT_EVENT, onKotReady);
consoleErrorSpy.mockRestore();
Expand Down
Loading