diff --git a/models/doctype/GSTR3B/GSTR3B.js b/models/doctype/GSTR3B/GSTR3B.js new file mode 100644 index 00000000..42233400 --- /dev/null +++ b/models/doctype/GSTR3B/GSTR3B.js @@ -0,0 +1,76 @@ +const frappe = require('frappejs'); + +module.exports = { + name: 'GSTR3B', + doctype: 'DocType', + documentClass: require('./GSTR3BDocument.js'), + print: { + printFormat: 'GSTR3B Print Format' + }, + keywordFields: ['name', 'month', 'year'], + fields: [ + { + fieldname: 'year', + label: 'Year', + fieldtype: 'Data', + required: 1 + }, + { + fieldname: 'month', + label: 'Month', + fieldtype: 'Select', + options: [ + '', + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ], + required: 1 + }, + { + fieldname: 'jsonData', + label: 'JSON Data', + fieldtype: 'Code', + formula: doc => doc.getJson(), + required: 1, + disabled: 1, + rows: 15 + } + ], + layout: [ + { + columns: [{ fields: ['year', 'month', 'jsonData'] }] + } + ], + links: [ + { + label: 'Print PDF', + condition: form => !form.doc._notInserted, + action: async form => { + form.$router.push({ + path: `/print/GSTR3B/${form.doc.name}` + }); + } + }, + { + label: 'Delete', + condition: form => !form.doc._notInserted, + action: async form => { + const doc = await frappe.getDoc('GSTR3B', form.doc.name); + await doc.delete(); + form.$router.push({ + path: `/list/GSTR3B` + }); + } + } + ] +}; diff --git a/models/doctype/GSTR3B/GSTR3BDocument.js b/models/doctype/GSTR3B/GSTR3BDocument.js new file mode 100644 index 00000000..9314caec --- /dev/null +++ b/models/doctype/GSTR3B/GSTR3BDocument.js @@ -0,0 +1,147 @@ +const BaseDocument = require('frappejs/model/document'); +const frappe = require('frappejs'); +const { format } = require('./GSTR3BFormat'); + +module.exports = class GSTR3B extends BaseDocument { + async getData() { + const monthIndex = [ + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December' + ].indexOf(this.month); + const month = monthIndex + 1 > 9 ? monthIndex + 1 : `0${monthIndex + 1}`; + const lastDate = new Date(this.year, monthIndex + 1, 0).getDate(); + const filters = { + date: [ + '>=', + `${this.year}-${month}-01`, + '<=', + `${this.year}-${month}-${lastDate}` + ] + }; + const invoices = frappe.db.getAll({ + doctype: 'Invoice', + filters, + fields: ['*'] + }); + const bills = frappe.db.getAll({ + doctype: 'Bill', + filters, + fields: ['*'] + }); + const [gstr1Data, gstr2Data] = await Promise.all([invoices, bills]); + let gstr3bData = [[], []]; + + for (let ledgerEntry of gstr1Data) { + ledgerEntry.doctype = 'Invoice'; + gstr3bData[0].push(await this.makeGSTRow(ledgerEntry)); + } + for (let ledgerEntry of gstr2Data) { + ledgerEntry.doctype = 'Bill'; + gstr3bData[1].push(await this.makeGSTRow(ledgerEntry)); + } + + return gstr3bData; + } + + async makeGSTRow(ledgerEntry) { + let row = {}; + ledgerEntry = await frappe.getDoc(ledgerEntry.doctype, ledgerEntry.name); + let party = await frappe.getDoc( + 'Party', + ledgerEntry.customer || ledgerEntry.supplier + ); + if (party.address) { + let addressDetails = await frappe.getDoc('Address', party.address); + row.place = addressDetails.state || ''; + } + row.gstin = party.gstin; + row.partyName = ledgerEntry.customer || ledgerEntry.supplier; + row.invNo = ledgerEntry.name; + row.invDate = ledgerEntry.date; + row.rate = 0; + row.inState = true; + row.reverseCharge = !party.gstin ? 'Y' : 'N'; + ledgerEntry.taxes.forEach(tax => { + row.rate += tax.rate; + const taxAmt = (tax.rate * ledgerEntry.netTotal) / 100; + if (tax.account === 'IGST') row.igstAmt = taxAmt; + if (tax.account === 'IGST') row.inState = false; + if (tax.account === 'CGST') row.cgstAmt = taxAmt; + if (tax.account === 'SGST') row.sgstAmt = taxAmt; + if (tax.account === 'Nil Rated') row.nilRated = true; + if (tax.account === 'Exempt') row.exempt = true; + if (tax.account === 'Non GST') row.nonGST = true; + }); + row.invAmt = ledgerEntry.grandTotal; + row.taxVal = ledgerEntry.netTotal; + return row; + } + + async createJson(data) { + let jsonData = JSON.parse(JSON.stringify(format)); + + for (let ledgerEntry of data[0]) { + if (ledgerEntry.rate > 0) { + jsonData['sup_details']['osup_det']['samt'] += ledgerEntry.sgstAmt || 0; + jsonData['sup_details']['osup_det']['camt'] += ledgerEntry.cgstAmt || 0; + jsonData['sup_details']['osup_det']['iamt'] += ledgerEntry.igstAmt || 0; + jsonData['sup_details']['osup_det']['txval'] += ledgerEntry.taxVal; + } + if (ledgerEntry.rate === 0) { + jsonData['sup_details']['osup_zero']['txval'] += ledgerEntry.taxVal; + } + if (ledgerEntry.nilRated || ledgerEntry.exempt) { + jsonData['sup_details']['osup_nil_exmp']['txval'] += ledgerEntry.taxVal; + } + if (ledgerEntry.nonGST) { + jsonData['sup_details']['osup_nongst']['txval'] += ledgerEntry.taxVal; + } + if (!ledgerEntry.inState && !ledgerEntry.gstin) { + jsonData['inter_sup']['unreg_details'].push({ + pos: ledgerEntry.place, + txval: ledgerEntry.taxVal, + iAmt: ledgerEntry.igstAmt || 0 + }); + } + } + + for (let ledgerEntry of data[1]) { + if (ledgerEntry.reverseCharge === 'Y') { + jsonData['sup_details']['isup_rev']['samt'] += ledgerEntry.sgstAmt || 0; + jsonData['sup_details']['isup_rev']['camt'] += ledgerEntry.cgstAmt || 0; + jsonData['sup_details']['isup_rev']['iamt'] += ledgerEntry.igstAmt || 0; + jsonData['sup_details']['isup_rev']['txval'] += ledgerEntry.taxVal; + } + if (ledgerEntry.nilRated || ledgerEntry.exempt) { + jsonData['inward_sup']['isup_details'][0][ + ledgerEntry.inState ? 'intra' : 'inter' + ] += ledgerEntry.taxVal; + } + if (ledgerEntry.nonGST) { + jsonData['inward_sup']['isup_details'][0][ + ledgerEntry.inState ? 'intra' : 'inter' + ] += ledgerEntry.taxVal; + } + } + + return jsonData; + } + + async getJson() { + if (this.year && this.month) { + const data = await this.getData(); + const json = await this.createJson(data); + return JSON.stringify(json, undefined, 2); + } + } +}; diff --git a/models/doctype/GSTR3B/GSTR3BFormat.js b/models/doctype/GSTR3B/GSTR3BFormat.js new file mode 100644 index 00000000..2d995177 --- /dev/null +++ b/models/doctype/GSTR3B/GSTR3BFormat.js @@ -0,0 +1,390 @@ +const format = { + gstin: '', + ret_period: '', + inward_sup: { + isup_details: [ + { + ty: 'GST', + intra: 0, + inter: 0 + }, + { + ty: 'NONGST', + inter: 0, + intra: 0 + } + ] + }, + sup_details: { + osup_zero: { + csamt: 0, + txval: 0, + iamt: 0 + }, + osup_nil_exmp: { + txval: 0 + }, + osup_det: { + samt: 0, + csamt: 0, + txval: 0, + camt: 0, + iamt: 0 + }, + isup_rev: { + samt: 0, + csamt: 0, + txval: 0, + camt: 0, + iamt: 0 + }, + osup_nongst: { + txval: 0 + } + }, + inter_sup: { + unreg_details: [], + comp_details: [], + uin_details: [] + }, + itc_elg: { + itc_avl: [ + { + csamt: 0, + samt: 0, + ty: 'IMPG', + camt: 0, + iamt: 0 + }, + { + csamt: 0, + samt: 0, + ty: 'IMPS', + camt: 0, + iamt: 0 + }, + { + samt: 0, + csamt: 0, + ty: 'ISRC', + camt: 0, + iamt: 0 + }, + { + ty: 'ISD', + iamt: 0, + camt: 0, + samt: 0, + csamt: 0 + }, + { + samt: 0, + csamt: 0, + ty: 'OTH', + camt: 0, + iamt: 0 + } + ], + itc_rev: [ + { + ty: 'RUL', + iamt: 0, + camt: 0, + samt: 0, + csamt: 0 + }, + { + ty: 'OTH', + iamt: 0, + camt: 0, + samt: 0, + csamt: 0 + } + ], + itc_net: { + samt: 0, + csamt: 0, + camt: 0, + iamt: 0 + }, + itc_inelg: [ + { + ty: 'RUL', + iamt: 0, + camt: 0, + samt: 0, + csamt: 0 + }, + { + ty: 'OTH', + iamt: 0, + camt: 0, + samt: 0, + csamt: 0 + } + ] + } +}; + +function generateHTML(data) { + let template = ` +
+
+

GSTR3B-Form

+
GSTIN:   ${data.gstin}
+
Period:   ${data.ret_period}
+
+ +
3.1  Details of Outward Supplies and inward supplies liable to reverse charge
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Nature Of SuppliesTotal Taxable valueIntegrated TaxCentral TaxState/UT TaxCess
(a) Outward taxable supplies(other than zero rated, nil rated and exempted${data.sup_details.osup_det.txval}${data.sup_details.osup_det.iamt}${data.sup_details.osup_det.camt}${data.sup_details.osup_det.samt}${data.sup_details.osup_det.csamt}
(b) Outward taxable supplies(zero rated)${data.sup_details.osup_zero.txval}${data.sup_details.osup_zero.iamt}${data.sup_details.osup_zero.csamt}
(b) Other outward supplies(Nil rated,Exempted)${data.sup_details.osup_nil_exmp.txval}
(d) Inward Supplies(liable to reverse charge${data.sup_details.isup_rev.txval}${data.sup_details.isup_rev.iamt}${data.sup_details.isup_rev.camt}${data.sup_details.isup_rev.samt}${data.sup_details.isup_rev.csamt}
(e) Non-GST outward supplies${data.sup_details.osup_nongst.txval}
+ +
+ 3.2  Of the supplies shown in 3.1 (a) above, details of inter-State supplies made to unregisterd + persons, composition taxable persons and UIN holders +
+ + + + + + + + + + + + + + + +
Place Of Supply (State/UT)Total Taxable ValueAmount of Integrated Tax
Supplies made to Unregistered Persons`; + for (let row of data.inter_sup.unreg_details) { + if (row) template += row.pos + '
'; + } + template += '
'; + for (let row of data.inter_sup.unreg_details) { + if (row) template += row.txval + '
'; + } + template += '
'; + for (let row of data.inter_sup.unreg_details) { + if (row) template += row.iamt + '
'; + } + + template += + '
Supplies made to Composition Taxable Persons'; + for (let row of data.inter_sup.comp_details) { + if (row) template += row.pos + '
'; + } + template += '
'; + for (let row of data.inter_sup.comp_details) { + if (row) template += row.txval + '
'; + } + template += '
'; + for (let row of data.inter_sup.comp_details) { + if (row) template += row.iamt + '
'; + } + + template += + '
Supplies made to UIN holders'; + for (let row of data.inter_sup.uin_details) { + if (row) template += row.pos + '
'; + } + template += '
'; + for (let row of data.inter_sup.uin_details) { + if (row) template += row.txval + '
'; + } + template += '
'; + for (let row of data.inter_sup.uin_details) { + if (row) template += row.iamt + '
'; + } + + template += `
+ +
4.   Eligible ITC
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DetailsIntegrated TaxCentral TaxState/UT taxCess
(A) ITC Available (whether in full op part)
  (1) Import of goods ${data.itc_elg.itc_avl[0].iamt}${data.itc_elg.itc_avl[0].camt}${data.itc_elg.itc_avl[0].samt}${data.itc_elg.itc_avl[0].csamt}
  (2) Import of services${data.itc_elg.itc_avl[1].iamt}${data.itc_elg.itc_avl[1].camt}${data.itc_elg.itc_avl[1].samt}${data.itc_elg.itc_avl[1].csamt}
  (3) Inward supplies liable to reverse charge (other than 1 & 2 above)${data.itc_elg.itc_avl[2].iamt}${data.itc_elg.itc_avl[2].camt}${data.itc_elg.itc_avl[2].samt}${data.itc_elg.itc_avl[2].csamt}
  (4) Inward supplies from ISD${data.itc_elg.itc_avl[3].iamt}${data.itc_elg.itc_avl[3].camt}${data.itc_elg.itc_avl[3].samt}${data.itc_elg.itc_avl[3].csamt}
  (5) All other ITC${data.itc_elg.itc_avl[4].iamt}${data.itc_elg.itc_avl[4].camt}${data.itc_elg.itc_avl[4].samt}${data.itc_elg.itc_avl[4].csamt}
(B) ITC Reversed
  (1) As per rules 42 & 43 of CGST Rules${data.itc_elg.itc_rev[0].iamt}${data.itc_elg.itc_rev[0].camt}${data.itc_elg.itc_rev[0].samt}${data.itc_elg.itc_rev[0].csamt}
  (2) Others${data.itc_elg.itc_rev[1].iamt}${data.itc_elg.itc_rev[1].camt}${data.itc_elg.itc_rev[1].samt}${data.itc_elg.itc_rev[1].csamt}
(C) Net ITC Available(A) - (B)${data.itc_elg.itc_net.iamt}${data.itc_elg.itc_net.camt}${data.itc_elg.itc_net.samt}${data.itc_elg.itc_net.csamt}
(D) Ineligible ITC
  (1) As per section 17(5)${data.itc_elg.itc_inelg[0].iamt}${data.itc_elg.itc_inelg[0].camt}${data.itc_elg.itc_inelg[0].samt}${data.itc_elg.itc_inelg[0].csamt}
  (2) Others${data.itc_elg.itc_inelg[1].iamt}${data.itc_elg.itc_inelg[1].camt}${data.itc_elg.itc_inelg[1].samt}${data.itc_elg.itc_inelg[1].csamt}
+ +
5.    Values of exempt, nil rated and non-GST inward supplies
+ + + + + + + + + + + + + + + + + + + + +
Nature of SuppliesInter-State SuppliesIntra-State Supplies
From a supplier under composition scheme, Exempt and Nil rated${data.inward_sup.isup_details[0].inter}${data.inward_sup.isup_details[0].intra}
Non GST Inward Supplies${data.inward_sup.isup_details[1].inter}${data.inward_sup.isup_details[1].intra}
+
`; + + return template; +} + +module.exports = { + format +}; diff --git a/models/doctype/GSTR3B/GSTR3BList.js b/models/doctype/GSTR3B/GSTR3BList.js new file mode 100644 index 00000000..480f1f25 --- /dev/null +++ b/models/doctype/GSTR3B/GSTR3BList.js @@ -0,0 +1,7 @@ +import { _ } from 'frappejs/utils'; + +export default { + doctype: 'GSTR3B', + title: _('GSTR 3B Report'), + columns: ['year', 'month'] +}; diff --git a/models/doctype/GSTR3B/GSTR3BPrintView.vue b/models/doctype/GSTR3B/GSTR3BPrintView.vue new file mode 100644 index 00000000..05e0f11c --- /dev/null +++ b/models/doctype/GSTR3B/GSTR3BPrintView.vue @@ -0,0 +1,304 @@ + + + + + + diff --git a/models/doctype/GSTR3B/GSTR3BServer.js b/models/doctype/GSTR3B/GSTR3BServer.js new file mode 100644 index 00000000..9bce85cd --- /dev/null +++ b/models/doctype/GSTR3B/GSTR3BServer.js @@ -0,0 +1,19 @@ +const GSTR3B = require('./GSTR3BDocument'); + +module.exports = class GSTR3BServer extends GSTR3B { + async validate() { + if (this.month.length === 0 || this.year.length != 4) { + frappe.call({ + method: 'show_dialog', + args: { + title: 'Invalid Entry', + message: `Month or Year is not valid` + } + }); + throw new Error(); + } + } + async beforeInsert() { + this.name = `${this.doctype} Report ${this.month} ${this.year}`; + } +}; diff --git a/models/doctype/Invoice/InvoicePrint.vue b/models/doctype/Invoice/InvoicePrint.vue index 442cf2fa..6dc92d7f 100644 --- a/models/doctype/Invoice/InvoicePrint.vue +++ b/models/doctype/Invoice/InvoicePrint.vue @@ -1,60 +1,89 @@ diff --git a/src/components/MessageDialog.vue b/src/components/MessageDialog.vue new file mode 100644 index 00000000..e114aeeb --- /dev/null +++ b/src/components/MessageDialog.vue @@ -0,0 +1,23 @@ + + + diff --git a/src/components/PageHeader.vue b/src/components/PageHeader.vue index 7078a58b..a345b0a2 100644 --- a/src/components/PageHeader.vue +++ b/src/components/PageHeader.vue @@ -5,7 +5,7 @@ diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index 0284313b..93934ca0 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -68,7 +68,7 @@ export default { methods: { isCurrentRoute(route) { if (this.activeGroup) return false; - return this.$route.path === route; + return this.$route.fullPath === route; }, toggleGroup(groupTitle) { this.groupItems = diff --git a/src/pages/FormView/FormView.vue b/src/pages/FormView/FormView.vue index c3becff6..25ff39b4 100644 --- a/src/pages/FormView/FormView.vue +++ b/src/pages/FormView/FormView.vue @@ -124,18 +124,28 @@ export default { this.$emit('save', this.doc); } catch (e) { console.error(e); - return; + throw e; } }, async submit() { this.doc.set('submitted', 1); - await this.save(); + try { + await this.save(); + } catch (e) { + this.doc.set('submitted', 0); + this.doc.set('_dirty', false); + } }, async revert() { this.doc.set('submitted', 0); - await this.save(); + try { + await this.save(); + } catch (e) { + this.doc.set('submitted', 1); + this.doc._dirty = false; + } }, print() { diff --git a/src/pages/ListView/listConfig.js b/src/pages/ListView/listConfig.js index 91216722..84a6dbb5 100644 --- a/src/pages/ListView/listConfig.js +++ b/src/pages/ListView/listConfig.js @@ -7,6 +7,7 @@ import Payment from '../../../models/doctype/Payment/PaymentList'; import Tax from '../../../models/doctype/Tax/TaxList'; import JournalEntry from '../../../models/doctype/JournalEntry/JournalEntryList'; import Account from '../../../models/doctype/Account/AccountList'; +import GSTR3B from '../../../models/doctype/GSTR3B/GSTR3BList'; export default { Invoice, @@ -17,5 +18,6 @@ export default { Payment, Tax, JournalEntry, - Account + Account, + GSTR3B }; diff --git a/src/pages/PrintView/index.vue b/src/pages/PrintView/index.vue index 813bb029..b4657448 100644 --- a/src/pages/PrintView/index.vue +++ b/src/pages/PrintView/index.vue @@ -1,61 +1,50 @@