mirror of
https://github.com/frappe/books.git
synced 2025-02-02 12:08:27 +00:00
Code Refactoring
This commit is contained in:
parent
f4fa8a6d88
commit
228e069096
178
accounting/gst.js
Normal file
178
accounting/gst.js
Normal file
@ -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;
|
||||||
|
}
|
@ -67,21 +67,7 @@ const viewConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
method: 'general-ledger',
|
method: 'general-ledger',
|
||||||
linkFields: [
|
linkFields: [],
|
||||||
{
|
|
||||||
label: 'Clear Filters',
|
|
||||||
type: 'secondary',
|
|
||||||
action: async (report) => {
|
|
||||||
await report.getReportData({});
|
|
||||||
report.usedToReRender += 1;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Export',
|
|
||||||
type: 'primary',
|
|
||||||
action: () => {},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
getColumns() {
|
getColumns() {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import ExportWizard from '../../src/components/ExportWizard';
|
import ExportWizard from '../../src/components/ExportWizard';
|
||||||
|
import { generateGstr1Json } from '../../accounting/gst';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
filterFields: [
|
filterFields: [
|
||||||
@ -8,12 +9,12 @@ export default {
|
|||||||
placeholder: 'Transfer Type',
|
placeholder: 'Transfer Type',
|
||||||
fieldname: 'transferType',
|
fieldname: 'transferType',
|
||||||
options: [
|
options: [
|
||||||
'',
|
|
||||||
'B2B',
|
'B2B',
|
||||||
'B2C-Large',
|
'B2C-Large',
|
||||||
'B2C-Small',
|
'B2C-Small',
|
||||||
'Nil Rated, Exempted and Non GST supplies',
|
'Nil Rated, Exempted and Non GST supplies',
|
||||||
],
|
],
|
||||||
|
default: 'B2B',
|
||||||
size: 'small',
|
size: 'small',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -40,42 +41,10 @@ export default {
|
|||||||
],
|
],
|
||||||
linkFields: [
|
linkFields: [
|
||||||
{
|
{
|
||||||
label: 'Export',
|
label: 'Export as JSON',
|
||||||
type: 'primary',
|
type: 'primary',
|
||||||
action: async (report) => {
|
action: async (report, transferType) => {
|
||||||
async function getReportDetails() {
|
generateGstr1Json(report, transferType);
|
||||||
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;
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -17,6 +17,7 @@ class GSTR1 extends BaseGSTR {
|
|||||||
'B2B': row => row.gstin,
|
'B2B': row => row.gstin,
|
||||||
'B2C-Large': row => !row.gstin && !row.inState && row.invAmt >= 250000,
|
'B2C-Large': row => !row.gstin && !row.inState && row.invAmt >= 250000,
|
||||||
'B2C-Small': row => !row.gstin && (row.inState || (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
|
'Nil Rated, Exempted and Non GST supplies': row => row
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -30,16 +30,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
linkFields: [
|
linkFields: [],
|
||||||
{
|
|
||||||
label: 'Clear Filters',
|
|
||||||
type: 'secondary',
|
|
||||||
action: async report => {
|
|
||||||
await report.getReportData({});
|
|
||||||
report.usedToReRender += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
getColumns(data) {
|
getColumns(data) {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ label: 'Account', fieldtype: 'Data', fieldname: 'account', width: 2 },
|
{ label: 'Account', fieldtype: 'Data', fieldname: 'account', width: 2 },
|
||||||
|
@ -2,15 +2,15 @@
|
|||||||
<div class="flex flex-col max-w-full">
|
<div class="flex flex-col max-w-full">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
<h1 slot="title" class="text-2xl font-bold">{{ report.title }}</h1>
|
<h1 slot="title" class="text-2xl font-bold">{{ report.title }}</h1>
|
||||||
<template slot="actions">
|
<template slot="actions">
|
||||||
<Button
|
<Button
|
||||||
:icon="true"
|
@click="link.action(reportData, filters.transferType)"
|
||||||
@click="downloadAsJson"
|
v-for="link of report.linkFields"
|
||||||
|
:key="link.label"
|
||||||
type="primary"
|
type="primary"
|
||||||
v-if="isGstReportsPage"
|
|
||||||
class="ml-2 text-white text-xs"
|
class="ml-2 text-white text-xs"
|
||||||
>
|
>
|
||||||
{{ _('Download as JSON') }}
|
{{ link.label }}
|
||||||
</Button>
|
</Button>
|
||||||
<SearchBar class="ml-2" />
|
<SearchBar class="ml-2" />
|
||||||
</template>
|
</template>
|
||||||
@ -102,11 +102,6 @@ import Row from '@/components/Row';
|
|||||||
import WithScroll from '@/components/WithScroll';
|
import WithScroll from '@/components/WithScroll';
|
||||||
import FormControl from '@/components/Controls/FormControl';
|
import FormControl from '@/components/Controls/FormControl';
|
||||||
import reportViewConfig from '@/../reports/view';
|
import reportViewConfig from '@/../reports/view';
|
||||||
import { makeJSON } from '@/utils';
|
|
||||||
import { ipcRenderer } from 'electron';
|
|
||||||
import { IPC_ACTIONS } from '@/messages';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { sleep } from 'frappejs/utils';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Report',
|
name: 'Report',
|
||||||
@ -137,7 +132,6 @@ export default {
|
|||||||
rows: [],
|
rows: [],
|
||||||
columns: [],
|
columns: [],
|
||||||
},
|
},
|
||||||
printSettings: null,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async activated() {
|
async activated() {
|
||||||
@ -145,9 +139,6 @@ export default {
|
|||||||
await this.setDefaultFilters();
|
await this.setDefaultFilters();
|
||||||
await this.fetchReportData();
|
await this.fetchReportData();
|
||||||
},
|
},
|
||||||
async mounted() {
|
|
||||||
this.printSettings = await frappe.getSingle('PrintSettings');
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
onBodyScroll({ scrollLeft }) {
|
onBodyScroll({ scrollLeft }) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
@ -280,147 +271,6 @@ export default {
|
|||||||
this.loading ? 'text-gray-100' : 'text-gray-900',
|
this.loading ? 'text-gray-100' : 'text-gray-900',
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
async downloadAsJson() {
|
|
||||||
const savePath = await this.getSavePath();
|
|
||||||
if (!savePath) return;
|
|
||||||
|
|
||||||
const gstRates = {
|
|
||||||
'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,
|
|
||||||
};
|
|
||||||
|
|
||||||
const csgstRates = {
|
|
||||||
'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-0': 0,
|
|
||||||
'IGST-0.25': 0,
|
|
||||||
'IGST-3': 0,
|
|
||||||
'IGST-5': 0,
|
|
||||||
'IGST-6': 0,
|
|
||||||
'IGST-12': 0,
|
|
||||||
'IGST-18': 0,
|
|
||||||
'IGST-28': 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
const igstRates = {
|
|
||||||
'GST-0': 0,
|
|
||||||
'GST-0.25': 0,
|
|
||||||
'GST-3': 0,
|
|
||||||
'GST-5': 0,
|
|
||||||
'GST-6': 0,
|
|
||||||
'GST-12': 0,
|
|
||||||
'GST-18': 0,
|
|
||||||
'GST-28': 0,
|
|
||||||
'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,
|
|
||||||
};
|
|
||||||
|
|
||||||
const gstData = {
|
|
||||||
version: 'GST3.0.4',
|
|
||||||
hash: 'hash',
|
|
||||||
fp: DateTime.local().toFormat('MMyyyy'),
|
|
||||||
gstin: this.printSettings.gstin,
|
|
||||||
b2b: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
let rows = this.reportData.rows;
|
|
||||||
rows.forEach(async (values) => {
|
|
||||||
|
|
||||||
const b2bRecord = {
|
|
||||||
ctin: values.gstin,
|
|
||||||
inv: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const invRecord = {
|
|
||||||
inum: values.invNo,
|
|
||||||
idt: values.invDate,
|
|
||||||
value: values.invAmt,
|
|
||||||
pos: values.gstin.substring(0, 2),
|
|
||||||
rchrg: values.reverseCharge,
|
|
||||||
itms: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
let items = await frappe.db
|
|
||||||
.knex('SalesInvoiceItem')
|
|
||||||
.where('parent', invRecord.inum);
|
|
||||||
|
|
||||||
items.forEach((_item) => {
|
|
||||||
const item = {
|
|
||||||
num: 1801, // will be replaced by HSN CODE
|
|
||||||
itm_det: {
|
|
||||||
txval: _item.baseAmount,
|
|
||||||
rt: gstRates[_item.tax],
|
|
||||||
cess: 0,
|
|
||||||
camt: (csgstRates[_item.tax] * _item.baseAmount) / 100,
|
|
||||||
samt: (csgstRates[_item.tax] * _item.baseAmount) / 100,
|
|
||||||
iamt: (igstRates[_item.tax] * _item.baseAmount) / 100,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
invRecord.itms.push(item);
|
|
||||||
});
|
|
||||||
|
|
||||||
let found = false;
|
|
||||||
found = gstData.b2b.find((_b2bRecord) => {
|
|
||||||
if(_b2bRecord.ctin === values.gstin) {
|
|
||||||
_b2bRecord.inv.push(invRecord);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
b2bRecord.inv.push(invRecord);
|
|
||||||
gstData.b2b.push(b2bRecord);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
await sleep(1);
|
|
||||||
const jsonData = JSON.stringify(gstData);
|
|
||||||
makeJSON(jsonData, savePath);
|
|
||||||
},
|
|
||||||
async getSavePath() {
|
|
||||||
const options = {
|
|
||||||
title: this._('Select folder'),
|
|
||||||
defaultPath: `${this.reportName}.json`,
|
|
||||||
};
|
|
||||||
|
|
||||||
let { filePath } = await ipcRenderer.invoke(
|
|
||||||
IPC_ACTIONS.GET_SAVE_FILEPATH,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
|
|
||||||
if (filePath) {
|
|
||||||
if (!filePath.endsWith('.json')) {
|
|
||||||
filePath = filePath + '.json';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filePath;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
columns() {
|
columns() {
|
||||||
@ -495,4 +345,4 @@ export default {
|
|||||||
.report-scroll-container::-webkit-scrollbar-track {
|
.report-scroll-container::-webkit-scrollbar-track {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user