From 228e0690963647e3690fe4fba7841e8ebdbaa651 Mon Sep 17 00:00:00 2001 From: Piyush Singhania Date: Fri, 10 Dec 2021 21:56:02 +0530 Subject: [PATCH] Code Refactoring --- accounting/gst.js | 178 +++++++++++++++++++ reports/GeneralLedger/viewConfig.js | 16 +- reports/GoodsAndServiceTax/BaseViewConfig.js | 41 +---- reports/GoodsAndServiceTax/GSTR1.js | 1 + reports/TrialBalance/viewConfig.js | 11 +- src/pages/Report.vue | 162 +---------------- 6 files changed, 192 insertions(+), 217 deletions(-) create mode 100644 accounting/gst.js diff --git a/accounting/gst.js b/accounting/gst.js new file mode 100644 index 00000000..1b4aa50d --- /dev/null +++ b/accounting/gst.js @@ -0,0 +1,178 @@ +import frappe from 'frappejs'; +import { _ } from 'frappejs/utils'; +import { IPC_ACTIONS } from '@/messages'; +import { ipcRenderer } from 'electron'; +import { DateTime } from 'luxon'; +import { sleep } from 'frappejs/utils'; +import { makeJSON } from '@/utils'; + +/** + * GST is a map which gives a final rate for any given gst item + * eg: IGST-18 = 18 + * eg: GST-18 = CGST-9 + SGST-9 = 18 + */ +const GST = { + 'GST-0': 0, + 'GST-0.25': 0.25, + 'GST-3': 3, + 'GST-5': 5, + 'GST-6': 6, + 'GST-12': 12, + 'GST-18': 18, + 'GST-28': 28, + 'IGST-0': 0, + 'IGST-0.25': 0.25, + 'IGST-3': 3, + 'IGST-5': 5, + 'IGST-6': 6, + 'IGST-12': 12, + 'IGST-18': 18, + 'IGST-28': 28, +}; + +/** + * CSGST is a map which return the tax rate component for state or central + * eg: GST-12 = 6 + */ +const CSGST = { + 'GST-0': 0, + 'GST-0.25': 0.125, + 'GST-3': 1.5, + 'GST-5': 2.5, + 'GST-6': 3, + 'GST-12': 6, + 'GST-18': 9, + 'GST-28': 14, +}; + +/** + * IGST is a map which return the tax rate for the igst item + * eg: IGST-18 = 18 + */ +const IGST = { + 'IGST-0.25': 0.25, + 'IGST-3': 3, + 'IGST-5': 5, + 'IGST-6': 6, + 'IGST-12': 12, + 'IGST-18': 18, + 'IGST-28': 28, +}; + + +export async function generateGstr1Json(report, transferType) { + const printSettings = await frappe.getSingle('PrintSettings'); + // TODO: if self gstin is not provided throw an error message + // telling that that report cannot be exported if company gst details are not configured + // avoiding hiding the button because that can feel like a bug + + const savePath = await getSavePath(); + if (!savePath) return; + + const gstData = { + version: 'GST3.0.4', + hash: 'hash', + // fp is the the MMYYYY for the last month of the report + // for example if you are extracting report for 1st July 2020 to 31st September 2020 then + // fb = 092020 + // TODO: fix this value to match the last date of range + fp: DateTime.local().toFormat('MMyyyy'), + gstin: printSettings.gstin, + }; + + // based condition we need to triggered different methods + if (transferType === 'B2B') { + gstData.b2b = await getB2bData(report.rows); + } else if (transferType === 'B2CL') { + gstData.b2cl = await getB2clData(report.rows); + } else if (transferType === 'B2CS') { + gstData.b2cs = await getB2csData(report.rows); + } + + await sleep(1); + const jsonData = JSON.stringify(gstData); + makeJSON(jsonData, savePath); +} + +async function getB2bData(invoices) { + const b2b = []; + + invoices.forEach(async (row) => { + // it's must for the customer to have a gstin, if not it should not be here + const customer = { + ctin: row.gstin, + inv: [], + }; + + const invRecord = { + inum: row.invNo, + idt: DateTime.fromFormat(row.invDate, 'yyyy-MM-dd').toFormat('dd-MM-yyyy'), + value: row.invAmt, + pos: row.gstin && row.gstin.substring(0, 2), + rchrg: row.reverseCharge, + itms: [], + }; + + let items = await frappe.db + .knex('SalesInvoiceItem') + .where('parent', invRecord.inum); + + items.forEach((item) => { + const itemRecord = { + num: item.item_code || 1801, // TODO: will be replaced by HSN CODE (item code) + itm_det: { + txval: item.baseAmount, + rt: GST[item.tax], + csamt: 0, + camt: ((CSGST[item.tax] || 0) * item.baseAmount) / 100, + samt: ((CSGST[item.tax] || 0) * item.baseAmount) / 100, + iamt: ((IGST[item.tax] || 0) * item.baseAmount) / 100, + }, + }; + + invRecord.itms.push(itemRecord); + }); + + + const customerRecord = b2b.find((b) => b.ctin === row.gstin); + + if (customerRecord) { + customerRecord.inv.push(invRecord); + } else { + customer.inv.push(invRecord); + b2b.push(customer); + } + }); + + return b2b; +} + +async function getB2clData(invoices) { + return []; +} + +async function getB2csData(invoices) { + return []; +} + +async function getSavePath(name='gstr1') { + const options = { + title: _('Select folder'), + defaultPath: `${name}.json`, + }; + + let { + filePath + } = await ipcRenderer.invoke( + IPC_ACTIONS.GET_SAVE_FILEPATH, + options + ); + + if (filePath) { + if (!filePath.endsWith('.json')) { + filePath = filePath + '.json'; + } + } + + return filePath; +} \ No newline at end of file diff --git a/reports/GeneralLedger/viewConfig.js b/reports/GeneralLedger/viewConfig.js index b3ab820f..3fdab10e 100644 --- a/reports/GeneralLedger/viewConfig.js +++ b/reports/GeneralLedger/viewConfig.js @@ -67,21 +67,7 @@ const viewConfig = { }, ], method: 'general-ledger', - linkFields: [ - { - label: 'Clear Filters', - type: 'secondary', - action: async (report) => { - await report.getReportData({}); - report.usedToReRender += 1; - }, - }, - { - label: 'Export', - type: 'primary', - action: () => {}, - }, - ], + linkFields: [], getColumns() { return [ { diff --git a/reports/GoodsAndServiceTax/BaseViewConfig.js b/reports/GoodsAndServiceTax/BaseViewConfig.js index dd41b9e1..c44675f3 100644 --- a/reports/GoodsAndServiceTax/BaseViewConfig.js +++ b/reports/GoodsAndServiceTax/BaseViewConfig.js @@ -1,4 +1,5 @@ import ExportWizard from '../../src/components/ExportWizard'; +import { generateGstr1Json } from '../../accounting/gst'; export default { filterFields: [ @@ -8,12 +9,12 @@ export default { placeholder: 'Transfer Type', fieldname: 'transferType', options: [ - '', 'B2B', 'B2C-Large', 'B2C-Small', 'Nil Rated, Exempted and Non GST supplies', ], + default: 'B2B', size: 'small', }, { @@ -40,42 +41,10 @@ export default { ], linkFields: [ { - label: 'Export', + label: 'Export as JSON', type: 'primary', - action: async (report) => { - async function getReportDetails() { - let [rows, columns] = await report.getReportData( - report.currentFilters - ); - let columnData = columns.map((column) => { - return { - id: column.id, - content: column.content, - checked: true, - }; - }); - return { - title: title, - rows: rows, - columnData: columnData, - }; - } - report.$modal.show({ - modalProps: { - title: `Export ${title}`, - noFooter: true, - }, - component: ExportWizard, - props: await getReportDetails(), - }); - }, - }, - { - label: 'Clear Filters', - type: 'secondary', - action: async (report) => { - await report.getReportData({}); - report.usedToReRender += 1; + action: async (report, transferType) => { + generateGstr1Json(report, transferType); }, }, ], diff --git a/reports/GoodsAndServiceTax/GSTR1.js b/reports/GoodsAndServiceTax/GSTR1.js index 0189bcbe..7870c400 100644 --- a/reports/GoodsAndServiceTax/GSTR1.js +++ b/reports/GoodsAndServiceTax/GSTR1.js @@ -17,6 +17,7 @@ class GSTR1 extends BaseGSTR { 'B2B': row => row.gstin, 'B2C-Large': row => !row.gstin && !row.inState && row.invAmt >= 250000, 'B2C-Small': row => !row.gstin && (row.inState || (row.inState && row.invAmt < 250000)), + // TODO: fix the condition for nil rated 'Nil Rated, Exempted and Non GST supplies': row => row }; diff --git a/reports/TrialBalance/viewConfig.js b/reports/TrialBalance/viewConfig.js index d1dba6f1..147e69b3 100644 --- a/reports/TrialBalance/viewConfig.js +++ b/reports/TrialBalance/viewConfig.js @@ -30,16 +30,7 @@ export default { } } ], - linkFields: [ - { - label: 'Clear Filters', - type: 'secondary', - action: async report => { - await report.getReportData({}); - report.usedToReRender += 1; - } - } - ], + linkFields: [], getColumns(data) { const columns = [ { label: 'Account', fieldtype: 'Data', fieldname: 'account', width: 2 }, diff --git a/src/pages/Report.vue b/src/pages/Report.vue index 5fa23d98..a37237b9 100644 --- a/src/pages/Report.vue +++ b/src/pages/Report.vue @@ -2,15 +2,15 @@

{{ report.title }}

-