mirror of
https://github.com/frappe/books.git
synced 2024-12-22 10:58:59 +00:00
incr: display gstr reports
This commit is contained in:
parent
67f35b5f79
commit
496b2b77aa
@ -1,332 +0,0 @@
|
|||||||
import { t } from 'fyo';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { fyo } from 'src/initFyo';
|
|
||||||
import { showMessageDialog } from 'src/utils';
|
|
||||||
import { stateCodeMap } from '../regional/in';
|
|
||||||
import { exportCsv, saveExportData } from '../reports/commonExporter';
|
|
||||||
import { getSavePath } from '../src/utils';
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
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,
|
|
||||||
};
|
|
||||||
|
|
||||||
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(getReportData) {
|
|
||||||
const { gstin } = fyo.AccountingSettings;
|
|
||||||
if (!gstin) {
|
|
||||||
showMessageDialog({
|
|
||||||
message: t`Export Failed`,
|
|
||||||
detail: t`Please set GSTIN in General Settings.`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
rows,
|
|
||||||
filters: { transferType, toDate },
|
|
||||||
} = getReportData();
|
|
||||||
|
|
||||||
const { filePath, canceled } = await getSavePath('gstr-1', 'json');
|
|
||||||
if (canceled || !filePath) return;
|
|
||||||
|
|
||||||
const gstData = {
|
|
||||||
version: 'GST3.0.4',
|
|
||||||
hash: 'hash',
|
|
||||||
gstin: gstin,
|
|
||||||
// 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
|
|
||||||
fp: DateTime.fromISO(toDate).toFormat('MMyyyy'),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (transferType === 'B2B') {
|
|
||||||
gstData.b2b = await generateB2bData(rows);
|
|
||||||
} else if (transferType === 'B2CL') {
|
|
||||||
gstData.b2cl = await generateB2clData(rows);
|
|
||||||
} else if (transferType === 'B2CS') {
|
|
||||||
gstData.b2cs = await generateB2csData(rows);
|
|
||||||
}
|
|
||||||
|
|
||||||
const jsonData = JSON.stringify(gstData);
|
|
||||||
await saveExportData(jsonData, filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function generateB2bData(rows) {
|
|
||||||
const b2b = [];
|
|
||||||
|
|
||||||
for (let row of rows) {
|
|
||||||
const customer = {
|
|
||||||
ctin: row.gstin,
|
|
||||||
inv: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const invRecord = {
|
|
||||||
inum: row.invNo,
|
|
||||||
idt: DateTime.fromFormat(row.invDate, 'yyyy-MM-dd').toFormat(
|
|
||||||
'dd-MM-yyyy'
|
|
||||||
),
|
|
||||||
val: row.invAmt,
|
|
||||||
pos: row.gstin && row.gstin.substring(0, 2),
|
|
||||||
rchrg: row.reverseCharge,
|
|
||||||
inv_typ: 'R',
|
|
||||||
itms: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const items = await fyo.db.getAllRaw('SalesInvoiceItem', {
|
|
||||||
fields: ['*'],
|
|
||||||
filters: { parent: invRecord.inum },
|
|
||||||
});
|
|
||||||
|
|
||||||
items.forEach((item) => {
|
|
||||||
const itemRecord = {
|
|
||||||
num: item.hsnCode,
|
|
||||||
itm_det: {
|
|
||||||
txval: fyo.pesa(item.baseAmount).float,
|
|
||||||
rt: GST[item.tax],
|
|
||||||
csamt: 0,
|
|
||||||
camt: fyo
|
|
||||||
.pesa(CSGST[item.tax] || 0)
|
|
||||||
.mul(item.baseAmount)
|
|
||||||
.div(100).float,
|
|
||||||
samt: fyo
|
|
||||||
.pesa(CSGST[item.tax] || 0)
|
|
||||||
.mul(item.baseAmount)
|
|
||||||
.div(100).float,
|
|
||||||
iamt: fyo
|
|
||||||
.pesa(IGST[item.tax] || 0)
|
|
||||||
.mul(item.baseAmount)
|
|
||||||
.div(100).float,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
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 generateB2clData(invoices) {
|
|
||||||
const b2cl = [];
|
|
||||||
|
|
||||||
for (let invoice of invoices) {
|
|
||||||
const stateInvoiceRecord = {
|
|
||||||
pos: stateCodeMap[invoice.place.toUpperCase()],
|
|
||||||
inv: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const invRecord = {
|
|
||||||
inum: invoice.invNo,
|
|
||||||
idt: DateTime.fromFormat(invoice.invDate, 'yyyy-MM-dd').toFormat(
|
|
||||||
'dd-MM-yyyy'
|
|
||||||
),
|
|
||||||
val: invoice.invAmt,
|
|
||||||
itms: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
const items = await fyo.db.getAllRaw('SalesInvoiceItem', {
|
|
||||||
fields: ['*'],
|
|
||||||
filters: { parent: invRecord.inum },
|
|
||||||
});
|
|
||||||
|
|
||||||
items.forEach((item) => {
|
|
||||||
const itemRecord = {
|
|
||||||
num: item.hsnCode,
|
|
||||||
itm_det: {
|
|
||||||
txval: fyo.pesa(item.baseAmount).float,
|
|
||||||
rt: GST[item.tax],
|
|
||||||
csamt: 0,
|
|
||||||
iamt: fyo
|
|
||||||
.pesa(invoice.rate || 0)
|
|
||||||
.mul(item.baseAmount)
|
|
||||||
.div(100).float,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
invRecord.itms.push(itemRecord);
|
|
||||||
});
|
|
||||||
|
|
||||||
const stateRecord = b2cl.find((b) => b.pos === stateCodeMap[invoice.place]);
|
|
||||||
|
|
||||||
if (stateRecord) {
|
|
||||||
stateRecord.inv.push(invRecord);
|
|
||||||
} else {
|
|
||||||
stateInvoiceRecord.inv.push(invRecord);
|
|
||||||
b2cl.push(stateInvoiceRecord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b2cl;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function generateB2csData(invoices) {
|
|
||||||
const b2cs = [];
|
|
||||||
|
|
||||||
for (let invoice of invoices) {
|
|
||||||
const pos = invoice.place.toUpperCase();
|
|
||||||
|
|
||||||
const invRecord = {
|
|
||||||
sply_ty: invoice.inState ? 'INTRA' : 'INTER',
|
|
||||||
pos: stateCodeMap[pos],
|
|
||||||
// "OE" - Abbreviation for errors and omissions excepted.
|
|
||||||
typ: 'OE',
|
|
||||||
txval: invoice.taxVal,
|
|
||||||
rt: invoice.rate,
|
|
||||||
iamt: !invoice.inState ? (invoice.taxVal * invoice.rate) / 100 : 0,
|
|
||||||
camt: invoice.inState ? invoice.cgstAmt : 0,
|
|
||||||
samt: invoice.inState ? invoice.sgstAmt : 0,
|
|
||||||
csamt: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
b2cs.push(invRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
return b2cs;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateGstr2Csv(getReportData) {
|
|
||||||
const { gstin } = fyo.AccountingSettings;
|
|
||||||
if (!gstin) {
|
|
||||||
showMessageDialog({
|
|
||||||
message: t`Export Failed`,
|
|
||||||
detail: t`Please set GSTIN in General Settings.`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
rows,
|
|
||||||
columns,
|
|
||||||
filters: { transferType, toDate },
|
|
||||||
} = getReportData();
|
|
||||||
|
|
||||||
const { filePath, canceled } = await getSavePath('gstr-2', 'csv');
|
|
||||||
if (canceled || !filePath) return;
|
|
||||||
|
|
||||||
let gstData;
|
|
||||||
if (transferType === 'B2B') {
|
|
||||||
gstData = await generateB2bCsvGstr2(rows, columns);
|
|
||||||
}
|
|
||||||
|
|
||||||
await exportCsv(gstData.rows, gstData.columns, filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function generateB2bCsvGstr2(rows, columns) {
|
|
||||||
const csvColumns = [
|
|
||||||
{
|
|
||||||
label: t`GSTIN of Supplier`,
|
|
||||||
fieldname: 'gstin',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Invoice Number`,
|
|
||||||
fieldname: 'invNo',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Invoice Date`,
|
|
||||||
fieldname: 'invDate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Invoice Value`,
|
|
||||||
fieldname: 'invAmt',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Place of supply`,
|
|
||||||
fieldname: 'place',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Reverse Charge`,
|
|
||||||
fieldname: 'reverseCharge',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Rate`,
|
|
||||||
fieldname: 'rate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Taxable Value`,
|
|
||||||
fieldname: 'taxVal',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Intergrated Tax Paid`,
|
|
||||||
fieldname: 'igstAmt',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Central Tax Paid`,
|
|
||||||
fieldname: 'cgstAmt',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`State/UT Tax Paid`,
|
|
||||||
fieldname: 'sgstAmt',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
columns: csvColumns || [],
|
|
||||||
rows: rows || [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function generateGstr1Csv(getReportData) {
|
|
||||||
const { gstin } = fyo.AccountingSettings;
|
|
||||||
if (!gstin) {
|
|
||||||
showMessageDialog({
|
|
||||||
message: t`Export Failed`,
|
|
||||||
detail: t`Please set GSTIN in General Settings.`,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
rows,
|
|
||||||
columns,
|
|
||||||
filters: { transferType, toDate },
|
|
||||||
} = getReportData();
|
|
||||||
|
|
||||||
const { filePath, canceled } = await getSavePath('gstr-1', 'csv');
|
|
||||||
if (canceled || !filePath) return;
|
|
||||||
|
|
||||||
await exportCsv(rows, columns, filePath);
|
|
||||||
}
|
|
@ -22,9 +22,7 @@ export const purchaseItemPartyMap: Record<string, string> = Object.keys(
|
|||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, string>);
|
}, {} as Record<string, string>);
|
||||||
|
|
||||||
export const flow = [
|
export const flow = [0.25, 0.2, 0.35, 0.2, 0.1, 0.01, 0.01, 0.15, 0, 0.25, 0.3, 0.5];
|
||||||
0.15, 0.1, 0.25, 0.1, 0.01, 0.01, 0.01, 0.05, 0, 0.15, 0.2, 0.4,
|
|
||||||
];
|
|
||||||
export function getFlowConstant(months: number) {
|
export function getFlowConstant(months: number) {
|
||||||
// Jan to December
|
// Jan to December
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
[{"name": "Dry-Cleaning", "description": null, "unit": "Unit", "itemType": "Service", "incomeAccount": "Service", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 69, "hsnCode": 999712, "for": "Sales"}, {"name": "Electricity", "description": "Bzz Bzz", "unit": "Day", "itemType": "Service", "incomeAccount": "Service", "expenseAccount": "Utility Expenses", "tax": "GST-0", "rate": 6000, "hsnCode": 271600, "for": "Purchases"}, {"name": "Marketing - Video", "description": "One single video", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Marketing Expenses", "tax": "GST-18", "rate": 15000, "hsnCode": 998371, "for": "Purchases"}, {"name": "Office Rent", "description": "Rent per day", "unit": "Day", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Office Rent", "tax": "GST-18", "rate": 100000, "hsnCode": 997212, "for": "Purchases"}, {"name": "Office Cleaning", "description": "Cleaning cost per day", "unit": "Day", "itemType": "Service", "incomeAccount": "Service", "expenseAccount": "Office Maintenance Expenses", "tax": "GST-18", "rate": 100000, "hsnCode": 998533, "for": "Purchases"}, {"name": "Social Ads", "description": "Cost per click", "unit": "Unit", "itemType": "Service", "incomeAccount": "Service", "expenseAccount": "Marketing Expenses", "tax": "GST-18", "rate": 50, "hsnCode": 99836, "for": "Purchases"}, {"name": "Cool Cloth", "description": "Some real \ud83c\udd92 cloth", "unit": "Meter", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 4000, "hsnCode": 59111000, "for": "Both"}, {"name": "611 Jeans - PCH", "description": "Peach coloured 611s", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 4499, "hsnCode": 62034990, "for": "Both"}, {"name": "611 Jeans - SHR", "description": "Shark skin 611s", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 6499, "hsnCode": 62034990, "for": "Both"}, {"name": "Bominga Shoes", "description": null, "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 4999, "hsnCode": 640291, "for": "Both"}, {"name": "Cryo Gloves", "description": "Keeps hands cool", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 3499, "hsnCode": 611693, "for": "Both"}, {"name": "Epaulettes - 4POR", "description": "Porcelain epaulettes", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 2499, "hsnCode": 62179090, "for": "Both"}, {"name": "Full Sleeve - BLK", "description": "Black sleeved", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 599, "hsnCode": 100820, "for": "Both"}, {"name": "Full Sleeve - COL", "description": "All color sleeved", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 499, "hsnCode": 100820, "for": "Both"}, {"name": "Jacket - RAW", "description": "Raw baby skinned jackets", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 8999, "hsnCode": 100820, "for": "Both"}, {"name": "Jade Slippers", "description": null, "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 2999, "hsnCode": 640520, "for": "Both"}]
|
[{"name": "Dry-Cleaning", "description": null, "unit": "Unit", "itemType": "Service", "incomeAccount": "Service", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 69, "hsnCode": 999712, "for": "Sales"}, {"name": "Electricity", "description": "Bzz Bzz", "unit": "Day", "itemType": "Service", "incomeAccount": "Service", "expenseAccount": "Utility Expenses", "tax": "GST-0", "rate": 6000, "hsnCode": 271600, "for": "Purchases"}, {"name": "Marketing - Video", "description": "One single video", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Marketing Expenses", "tax": "GST-18", "rate": 15000, "hsnCode": 998371, "for": "Purchases"}, {"name": "Office Rent", "description": "Rent per day", "unit": "Day", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Office Rent", "tax": "GST-18", "rate": 50000, "hsnCode": 997212, "for": "Purchases"}, {"name": "Office Cleaning", "description": "Cleaning cost per day", "unit": "Day", "itemType": "Service", "incomeAccount": "Service", "expenseAccount": "Office Maintenance Expenses", "tax": "GST-18", "rate": 7500, "hsnCode": 998533, "for": "Purchases"}, {"name": "Social Ads", "description": "Cost per click", "unit": "Unit", "itemType": "Service", "incomeAccount": "Service", "expenseAccount": "Marketing Expenses", "tax": "GST-18", "rate": 50, "hsnCode": 99836, "for": "Purchases"}, {"name": "Cool Cloth", "description": "Some real \ud83c\udd92 cloth", "unit": "Meter", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 4000, "hsnCode": 59111000, "for": "Both"}, {"name": "611 Jeans - PCH", "description": "Peach coloured 611s", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 4499, "hsnCode": 62034990, "for": "Both"}, {"name": "611 Jeans - SHR", "description": "Shark skin 611s", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 6499, "hsnCode": 62034990, "for": "Both"}, {"name": "Bominga Shoes", "description": null, "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 4999, "hsnCode": 640291, "for": "Both"}, {"name": "Cryo Gloves", "description": "Keeps hands cool", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 3499, "hsnCode": 611693, "for": "Both"}, {"name": "Epaulettes - 4POR", "description": "Porcelain epaulettes", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 2499, "hsnCode": 62179090, "for": "Both"}, {"name": "Full Sleeve - BLK", "description": "Black sleeved", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 599, "hsnCode": 100820, "for": "Both"}, {"name": "Full Sleeve - COL", "description": "All color sleeved", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 499, "hsnCode": 100820, "for": "Both"}, {"name": "Jacket - RAW", "description": "Raw baby skinned jackets", "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-12", "rate": 8999, "hsnCode": 100820, "for": "Both"}, {"name": "Jade Slippers", "description": null, "unit": "Unit", "itemType": "Product", "incomeAccount": "Sales", "expenseAccount": "Cost of Goods Sold", "tax": "GST-18", "rate": 2999, "hsnCode": 640520, "for": "Both"}]
|
File diff suppressed because one or more lines are too long
@ -1,8 +1,7 @@
|
|||||||
import { t } from 'fyo';
|
import { t } from 'fyo';
|
||||||
import { Doc } from 'fyo/model/doc';
|
import { Doc } from 'fyo/model/doc';
|
||||||
import { EmptyMessageMap, FormulaMap, ListsMap } from 'fyo/model/types';
|
import { EmptyMessageMap, FormulaMap, ListsMap } from 'fyo/model/types';
|
||||||
import { stateCodeMap } from 'regional/in';
|
import { codeStateMap } from 'regional/in';
|
||||||
import { titleCase } from 'utils';
|
|
||||||
import { getCountryInfo } from 'utils/misc';
|
import { getCountryInfo } from 'utils/misc';
|
||||||
|
|
||||||
export class Address extends Doc {
|
export class Address extends Doc {
|
||||||
@ -36,7 +35,7 @@ export class Address extends Doc {
|
|||||||
const country = doc?.country as string | undefined;
|
const country = doc?.country as string | undefined;
|
||||||
switch (country) {
|
switch (country) {
|
||||||
case 'India':
|
case 'India':
|
||||||
return Object.keys(stateCodeMap).map(titleCase).sort();
|
return Object.values(codeStateMap).sort();
|
||||||
default:
|
default:
|
||||||
return [] as string[];
|
return [] as string[];
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ export abstract class Invoice extends Transactional {
|
|||||||
account?: string;
|
account?: string;
|
||||||
currency?: string;
|
currency?: string;
|
||||||
netTotal?: Money;
|
netTotal?: Money;
|
||||||
|
grandTotal?: Money;
|
||||||
baseGrandTotal?: Money;
|
baseGrandTotal?: Money;
|
||||||
outstandingAmount?: Money;
|
outstandingAmount?: Money;
|
||||||
exchangeRate?: number;
|
exchangeRate?: number;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { FormulaMap, ListsMap } from 'fyo/model/types';
|
import { FormulaMap, ListsMap } from 'fyo/model/types';
|
||||||
import { Address as BaseAddress } from 'models/baseModels/Address/Address';
|
import { Address as BaseAddress } from 'models/baseModels/Address/Address';
|
||||||
import { stateCodeMap } from 'regional/in';
|
import { codeStateMap } from 'regional/in';
|
||||||
import { titleCase } from 'utils';
|
|
||||||
|
|
||||||
export class Address extends BaseAddress {
|
export class Address extends BaseAddress {
|
||||||
formulas: FormulaMap = {
|
formulas: FormulaMap = {
|
||||||
@ -30,7 +29,7 @@ export class Address extends BaseAddress {
|
|||||||
|
|
||||||
pos: {
|
pos: {
|
||||||
formula: async () => {
|
formula: async () => {
|
||||||
const stateList = Object.keys(stateCodeMap).map(titleCase).sort();
|
const stateList = Object.values(codeStateMap).sort();
|
||||||
const state = this.state as string;
|
const state = this.state as string;
|
||||||
if (stateList.includes(state)) {
|
if (stateList.includes(state)) {
|
||||||
return state;
|
return state;
|
||||||
@ -44,7 +43,7 @@ export class Address extends BaseAddress {
|
|||||||
static lists: ListsMap = {
|
static lists: ListsMap = {
|
||||||
...BaseAddress.lists,
|
...BaseAddress.lists,
|
||||||
pos: () => {
|
pos: () => {
|
||||||
return Object.keys(stateCodeMap).map(titleCase).sort();
|
return Object.values(codeStateMap).sort();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@ import { Party as BaseParty } from 'models/baseModels/Party/Party';
|
|||||||
import { GSTType } from './types';
|
import { GSTType } from './types';
|
||||||
|
|
||||||
export class Party extends BaseParty {
|
export class Party extends BaseParty {
|
||||||
|
gstin?: string;
|
||||||
|
gstType?: GSTType;
|
||||||
|
|
||||||
async beforeSync() {
|
async beforeSync() {
|
||||||
const gstin = this.get('gstin') as string | undefined;
|
const gstin = this.get('gstin') as string | undefined;
|
||||||
const gstType = this.get('gstType') as GSTType;
|
const gstType = this.get('gstType') as GSTType;
|
||||||
|
@ -1,39 +1,38 @@
|
|||||||
// prettier-ignore
|
export const codeStateMap = {
|
||||||
export const stateCodeMap = {
|
'01': 'Jammu and Kashmir',
|
||||||
'JAMMU AND KASHMIR': '01',
|
'02': 'Himachal Pradesh',
|
||||||
'HIMACHAL PRADESH': '02',
|
'03': 'Punjab',
|
||||||
'PUNJAB': '03',
|
'04': 'Chandigarh',
|
||||||
'CHANDIGARH': '04',
|
'05': 'Uttarakhand',
|
||||||
'UTTARAKHAND': '05',
|
'06': 'Haryana',
|
||||||
'HARYANA': '06',
|
'07': 'Delhi',
|
||||||
'DELHI': '07',
|
'08': 'Rajasthan',
|
||||||
'RAJASTHAN': '08',
|
'09': 'Uttar Pradesh',
|
||||||
'UTTAR PRADESH': '09',
|
'10': 'Bihar',
|
||||||
'BIHAR': '10',
|
'11': 'Sikkim',
|
||||||
'SIKKIM': '11',
|
'12': 'Arunachal Pradesh',
|
||||||
'ARUNACHAL PRADESH': '12',
|
'13': 'Nagaland',
|
||||||
'NAGALAND': '13',
|
'14': 'Manipur',
|
||||||
'MANIPUR': '14',
|
'15': 'Mizoram',
|
||||||
'MIZORAM': '15',
|
'16': 'Tripura',
|
||||||
'TRIPURA': '16',
|
'17': 'Meghalaya',
|
||||||
'MEGHALAYA': '17',
|
'18': 'Assam',
|
||||||
'ASSAM': '18',
|
'19': 'West Bengal',
|
||||||
'WEST BENGAL': '19',
|
'20': 'Jharkhand',
|
||||||
'JHARKHAND': '20',
|
'21': 'Odisha',
|
||||||
'ODISHA': '21',
|
'22': 'Chattisgarh',
|
||||||
'CHATTISGARH': '22',
|
'23': 'Madhya Pradesh',
|
||||||
'MADHYA PRADESH': '23',
|
'24': 'Gujarat',
|
||||||
'GUJARAT': '24',
|
'26': 'Dadra and Nagar Haveli and Daman and Diu',
|
||||||
'DADRA AND NAGAR HAVELI AND DAMAN AND DIU': '26',
|
'27': 'Maharashtra',
|
||||||
'MAHARASHTRA': '27',
|
'29': 'Karnataka',
|
||||||
'KARNATAKA': '29',
|
'30': 'Goa',
|
||||||
'GOA': '30',
|
'31': 'Lakshadweep',
|
||||||
'LAKSHADWEEP': '31',
|
'32': 'Kerala',
|
||||||
'KERALA': '32',
|
'33': 'Tamil Nadu',
|
||||||
'TAMIL NADU': '33',
|
'34': 'Puducherry',
|
||||||
'PUDUCHERRY': '34',
|
'35': 'Andaman and Nicobar Islands',
|
||||||
'ANDAMAN AND NICOBAR ISLANDS': '35',
|
'36': 'Telangana',
|
||||||
'TELANGANA': '36',
|
'37': 'Andhra Pradesh',
|
||||||
'ANDHRA PRADESH': '37',
|
'38': 'Ladakh',
|
||||||
'LADAKH': '38',
|
} as Record<string, string>;
|
||||||
};
|
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
import { fyo } from 'src/initFyo';
|
|
||||||
import { stateCodeMap } from '../../accounting/gst';
|
|
||||||
import { convertPesaValuesToFloat } from '../../src/utils';
|
|
||||||
|
|
||||||
class BaseGSTR {
|
|
||||||
async getCompleteReport(gstrType, filters) {
|
|
||||||
if (['GSTR-1', 'GSTR-2'].includes(gstrType)) {
|
|
||||||
const place = filters.place;
|
|
||||||
delete filters.place;
|
|
||||||
let entries = await fyo.db.getAll({
|
|
||||||
doctype: gstrType === 'GSTR-1' ? 'SalesInvoice' : 'PurchaseInvoice',
|
|
||||||
filters,
|
|
||||||
});
|
|
||||||
filters.place = place;
|
|
||||||
|
|
||||||
let tableData = [];
|
|
||||||
for (let entry of entries) {
|
|
||||||
entry.doctype =
|
|
||||||
gstrType === 'GSTR-1' ? 'SalesInvoice' : 'PurchaseInvoice';
|
|
||||||
const row = await this.getRow(entry);
|
|
||||||
tableData.push(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Object.keys(filters).length != 0) {
|
|
||||||
tableData = tableData.filter((row) => {
|
|
||||||
if (filters.account) return row.account === filters.account;
|
|
||||||
if (filters.transferType)
|
|
||||||
return row.transferType === filters.transferType;
|
|
||||||
if (filters.place) return row.place === filters.place;
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
tableData.forEach(convertPesaValuesToFloat);
|
|
||||||
return tableData;
|
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async getRow(ledgerEntry) {
|
|
||||||
ledgerEntry = await fyo.doc.getDoc(ledgerEntry.doctype, ledgerEntry.name);
|
|
||||||
|
|
||||||
const row = {};
|
|
||||||
const { gstin } = fyo.AccountingSettings;
|
|
||||||
|
|
||||||
let party = await fyo.doc.getDoc(
|
|
||||||
'Party',
|
|
||||||
ledgerEntry.customer || ledgerEntry.supplier
|
|
||||||
);
|
|
||||||
|
|
||||||
if (party.address) {
|
|
||||||
let addressDetails = await fyo.doc.getDoc('Address', party.address);
|
|
||||||
row.place = addressDetails.pos || '';
|
|
||||||
}
|
|
||||||
|
|
||||||
row.gstin = party.gstin;
|
|
||||||
row.partyName = ledgerEntry.customer || ledgerEntry.supplier;
|
|
||||||
row.invNo = ledgerEntry.name;
|
|
||||||
row.invDate = ledgerEntry.date;
|
|
||||||
row.rate = 0;
|
|
||||||
row.inState =
|
|
||||||
gstin && gstin.substring(0, 2) === stateCodeMap[row.place?.toUpperCase()];
|
|
||||||
row.reverseCharge = !party.gstin ? 'Y' : 'N';
|
|
||||||
|
|
||||||
ledgerEntry.taxes?.forEach((tax) => {
|
|
||||||
row.rate += tax.rate;
|
|
||||||
const taxAmt = ledgerEntry.netTotal.percent(tax.rate);
|
|
||||||
|
|
||||||
switch (tax.account) {
|
|
||||||
case 'IGST': {
|
|
||||||
row.igstAmt = taxAmt;
|
|
||||||
row.inState = false;
|
|
||||||
}
|
|
||||||
case 'CGST':
|
|
||||||
row.cgstAmt = taxAmt;
|
|
||||||
case 'SGST':
|
|
||||||
row.sgstAmt = taxAmt;
|
|
||||||
case 'Nil Rated':
|
|
||||||
row.nilRated = true;
|
|
||||||
case 'Exempt':
|
|
||||||
row.exempt = true;
|
|
||||||
case 'Non GST':
|
|
||||||
row.nonGST = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
row.invAmt = ledgerEntry.grandTotal;
|
|
||||||
row.taxVal = ledgerEntry.netTotal;
|
|
||||||
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BaseGSTR;
|
|
340
reports/GoodsAndServiceTax/BaseGSTR.ts
Normal file
340
reports/GoodsAndServiceTax/BaseGSTR.ts
Normal file
@ -0,0 +1,340 @@
|
|||||||
|
import { t } from 'fyo';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
import { Invoice } from 'models/baseModels/Invoice/Invoice';
|
||||||
|
import { Party } from 'models/regionalModels/in/Party';
|
||||||
|
import { ModelNameEnum } from 'models/types';
|
||||||
|
import { codeStateMap } from 'regional/in';
|
||||||
|
import { Report } from 'reports/Report';
|
||||||
|
import { ColumnField, ReportData, ReportRow } from 'reports/types';
|
||||||
|
import { Field, OptionField } from 'schemas/types';
|
||||||
|
import { isNumeric } from 'src/utils';
|
||||||
|
import { GSTRRow, GSTRType, TransferType, TransferTypeEnum } from './types';
|
||||||
|
|
||||||
|
export abstract class BaseGSTR extends Report {
|
||||||
|
place?: string;
|
||||||
|
toDate?: string;
|
||||||
|
fromDate?: string;
|
||||||
|
transferType?: TransferType;
|
||||||
|
usePagination: boolean = true;
|
||||||
|
|
||||||
|
abstract gstrType: GSTRType;
|
||||||
|
|
||||||
|
get transferTypeMap(): Record<string, string> {
|
||||||
|
if (this.gstrType === 'GSTR-2') {
|
||||||
|
return {
|
||||||
|
B2B: 'B2B',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
B2B: 'B2B',
|
||||||
|
B2CL: 'B2C-Large',
|
||||||
|
B2CS: 'B2C-Small',
|
||||||
|
NR: 'Nil Rated, Exempted and Non GST supplies',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get schemaName() {
|
||||||
|
if (this.gstrType === 'GSTR-1') {
|
||||||
|
return ModelNameEnum.SalesInvoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModelNameEnum.PurchaseInvoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
async setReportData(): Promise<void> {
|
||||||
|
const gstrRows = await this.getGstrRows();
|
||||||
|
const filteredRows = this.filterGstrRows(gstrRows);
|
||||||
|
this.reportData = this.getReportDataFromGSTRRows(filteredRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
getReportDataFromGSTRRows(gstrRows: GSTRRow[]): ReportData {
|
||||||
|
const reportData: ReportData = [];
|
||||||
|
for (const row of gstrRows) {
|
||||||
|
const reportRow: ReportRow = { cells: [] };
|
||||||
|
|
||||||
|
for (const { fieldname, fieldtype, width } of this.columns) {
|
||||||
|
const align = isNumeric(fieldtype) ? 'right' : 'left';
|
||||||
|
|
||||||
|
const rawValue = row[fieldname as keyof GSTRRow];
|
||||||
|
let value = '';
|
||||||
|
if (rawValue !== undefined) {
|
||||||
|
value = this.fyo.format(rawValue, fieldtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
reportRow.cells.push({
|
||||||
|
align,
|
||||||
|
rawValue,
|
||||||
|
value,
|
||||||
|
width: width ?? 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
reportData.push(reportRow);
|
||||||
|
}
|
||||||
|
|
||||||
|
return reportData;
|
||||||
|
}
|
||||||
|
|
||||||
|
filterGstrRows(gstrRows: GSTRRow[]) {
|
||||||
|
return gstrRows.filter((row) => {
|
||||||
|
let allow = true;
|
||||||
|
if (this.place) {
|
||||||
|
allow &&= codeStateMap[this.place] === row.place;
|
||||||
|
}
|
||||||
|
this.place;
|
||||||
|
return (allow &&= this.transferFilterFunction(row));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get transferFilterFunction(): (row: GSTRRow) => boolean {
|
||||||
|
if (this.transferType === 'B2B') {
|
||||||
|
return (row) => !!row.gstin;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.transferType === 'B2CL') {
|
||||||
|
return (row) => !row.gstin && !row.inState && row.invAmt >= 250000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.transferType === 'B2CS') {
|
||||||
|
return (row) => !row.gstin && (row.inState || row.invAmt < 250000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.transferType === 'NR') {
|
||||||
|
return (row) => row.rate === 0; // this takes care of both nil rated, exempted goods
|
||||||
|
}
|
||||||
|
|
||||||
|
return (_) => true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEntries() {
|
||||||
|
const date: string[] = [];
|
||||||
|
if (this.toDate) {
|
||||||
|
date.push('<=', this.toDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fromDate) {
|
||||||
|
date.push('>=', this.fromDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (await this.fyo.db.getAllRaw(this.schemaName, {
|
||||||
|
filters: { date, submitted: true, cancelled: false },
|
||||||
|
})) as { name: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGstrRows(): Promise<GSTRRow[]> {
|
||||||
|
const entries = await this.getEntries();
|
||||||
|
const gstrRows: GSTRRow[] = [];
|
||||||
|
for (const entry of entries) {
|
||||||
|
const gstrRow = await this.getGstrRow(entry.name as string);
|
||||||
|
gstrRows.push(gstrRow);
|
||||||
|
}
|
||||||
|
return gstrRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getGstrRow(entryName: string): Promise<GSTRRow> {
|
||||||
|
const entry = (await this.fyo.doc.getDoc(
|
||||||
|
this.schemaName,
|
||||||
|
entryName
|
||||||
|
)) as Invoice;
|
||||||
|
const gstin = (await this.fyo.getValue(
|
||||||
|
ModelNameEnum.AccountingSettings,
|
||||||
|
'gstin'
|
||||||
|
)) as string | null;
|
||||||
|
|
||||||
|
const party = (await this.fyo.doc.getDoc('Party', entry.party!)) as Party;
|
||||||
|
|
||||||
|
let place = '';
|
||||||
|
if (party.address) {
|
||||||
|
const pos = (await this.fyo.getValue(
|
||||||
|
ModelNameEnum.Address,
|
||||||
|
party.address as string,
|
||||||
|
'pos'
|
||||||
|
)) as string | undefined;
|
||||||
|
|
||||||
|
place = pos ?? '';
|
||||||
|
} else if (party.gstin) {
|
||||||
|
const code = party.gstin.slice(0, 2);
|
||||||
|
place = codeStateMap[code] ?? '';
|
||||||
|
}
|
||||||
|
|
||||||
|
let inState = false;
|
||||||
|
if (gstin) {
|
||||||
|
inState = codeStateMap[gstin.slice(0, 2)] === place;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gstrRow: GSTRRow = {
|
||||||
|
gstin: party.gstin ?? '',
|
||||||
|
partyName: entry.party!,
|
||||||
|
invNo: entry.name!,
|
||||||
|
invDate: entry.date!,
|
||||||
|
rate: 0,
|
||||||
|
reverseCharge: !party.gstin ? 'Y' : 'N',
|
||||||
|
inState,
|
||||||
|
place,
|
||||||
|
invAmt: entry.grandTotal?.float ?? 0,
|
||||||
|
taxVal: entry.netTotal?.float ?? 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const tax of entry.taxes ?? []) {
|
||||||
|
gstrRow.rate += tax.rate ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setTaxValuesOnGSTRRow(entry, gstrRow);
|
||||||
|
return gstrRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTaxValuesOnGSTRRow(entry: Invoice, gstrRow: GSTRRow) {
|
||||||
|
for (const tax of entry.taxes ?? []) {
|
||||||
|
const rate = tax.rate ?? 0;
|
||||||
|
gstrRow.rate += rate;
|
||||||
|
const taxAmt = entry.netTotal!.percent(rate).float;
|
||||||
|
|
||||||
|
switch (tax.account) {
|
||||||
|
case 'IGST': {
|
||||||
|
gstrRow.igstAmt = taxAmt;
|
||||||
|
gstrRow.inState = false;
|
||||||
|
}
|
||||||
|
case 'CGST':
|
||||||
|
gstrRow.cgstAmt = taxAmt;
|
||||||
|
case 'SGST':
|
||||||
|
gstrRow.sgstAmt = taxAmt;
|
||||||
|
case 'Nil Rated':
|
||||||
|
gstrRow.nilRated = true;
|
||||||
|
case 'Exempt':
|
||||||
|
gstrRow.exempt = true;
|
||||||
|
case 'Non GST':
|
||||||
|
gstrRow.nonGST = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async setDefaultFilters() {
|
||||||
|
if (!this.toDate) {
|
||||||
|
this.toDate = DateTime.local().toISODate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.fromDate) {
|
||||||
|
this.fromDate = DateTime.local().minus({ months: 3 }).toISODate();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.transferType) {
|
||||||
|
this.transferType = 'B2B';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getFilters(): Field[] {
|
||||||
|
const transferTypeMap = this.transferTypeMap;
|
||||||
|
const options = Object.keys(transferTypeMap).map((k) => ({
|
||||||
|
value: k,
|
||||||
|
label: transferTypeMap[k],
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
fieldtype: 'Select',
|
||||||
|
label: t`Transfer Type`,
|
||||||
|
placeholder: t`Transfer Type`,
|
||||||
|
fieldname: 'transferType',
|
||||||
|
options,
|
||||||
|
} as OptionField,
|
||||||
|
{
|
||||||
|
fieldtype: 'AutoComplete',
|
||||||
|
label: t`Place`,
|
||||||
|
placeholder: t`Place`,
|
||||||
|
fieldname: 'place',
|
||||||
|
options: Object.keys(codeStateMap).map((code) => {
|
||||||
|
return {
|
||||||
|
value: code,
|
||||||
|
label: codeStateMap[code],
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
} as OptionField,
|
||||||
|
{
|
||||||
|
fieldtype: 'Date',
|
||||||
|
label: t`From Date`,
|
||||||
|
placeholder: t`From Date`,
|
||||||
|
fieldname: 'fromDate',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fieldtype: 'Date',
|
||||||
|
label: t`To Date`,
|
||||||
|
placeholder: t`To Date`,
|
||||||
|
fieldname: 'toDate',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
getColumns(): ColumnField[] | Promise<ColumnField[]> {
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
label: t`Party`,
|
||||||
|
fieldtype: 'Data',
|
||||||
|
fieldname: 'partyName',
|
||||||
|
width: 1.5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Invoice No.`,
|
||||||
|
fieldname: 'invNo',
|
||||||
|
fieldtype: 'Data',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Invoice Value`,
|
||||||
|
fieldname: 'invAmt',
|
||||||
|
fieldtype: 'Currency',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Invoice Date`,
|
||||||
|
fieldname: 'invDate',
|
||||||
|
fieldtype: 'Date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Place of supply`,
|
||||||
|
fieldname: 'place',
|
||||||
|
fieldtype: 'Data',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Rate`,
|
||||||
|
fieldname: 'rate',
|
||||||
|
width: 0.5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Taxable Value`,
|
||||||
|
fieldname: 'taxVal',
|
||||||
|
fieldtype: 'Currency',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Reverse Chrg.`,
|
||||||
|
fieldname: 'reverseCharge',
|
||||||
|
fieldtype: 'Data',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Intergrated Tax`,
|
||||||
|
fieldname: 'igstAmt',
|
||||||
|
fieldtype: 'Currency',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`Central Tax`,
|
||||||
|
fieldname: 'cgstAmt',
|
||||||
|
fieldtype: 'Currency',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: t`State Tax`,
|
||||||
|
fieldname: 'sgstAmt',
|
||||||
|
fieldtype: 'Currency',
|
||||||
|
},
|
||||||
|
] as ColumnField[];
|
||||||
|
|
||||||
|
const transferType = this.transferType ?? TransferTypeEnum.B2B;
|
||||||
|
if (transferType === TransferTypeEnum.B2B) {
|
||||||
|
columns.unshift({
|
||||||
|
label: t`GSTIN No.`,
|
||||||
|
fieldname: 'gstin',
|
||||||
|
fieldtype: 'Data',
|
||||||
|
width: 1.5,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
}
|
@ -1,106 +0,0 @@
|
|||||||
import { t } from 'fyo';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { stateCodeMap } from '../../accounting/gst';
|
|
||||||
import { titleCase } from '../../src/utils';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
filterFields: [
|
|
||||||
{
|
|
||||||
fieldtype: 'AutoComplete',
|
|
||||||
label: t`Place`,
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`Place`,
|
|
||||||
fieldname: 'place',
|
|
||||||
getList: () => Object.keys(stateCodeMap).map(titleCase).sort(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Date',
|
|
||||||
label: t`From Date`,
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`From Date`,
|
|
||||||
fieldname: 'fromDate',
|
|
||||||
default: () => DateTime.local().minus({ months: 3 }).toISODate(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Date',
|
|
||||||
label: t`To Date`,
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`To Date`,
|
|
||||||
fieldname: 'toDate',
|
|
||||||
default: () => DateTime.local().toISODate(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
getColumns({ filters }) {
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
label: t`Party`,
|
|
||||||
fieldtype: 'Data',
|
|
||||||
fieldname: 'partyName',
|
|
||||||
width: 1.5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Invoice No.`,
|
|
||||||
fieldname: 'invNo',
|
|
||||||
fieldtype: 'Data',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Invoice Value`,
|
|
||||||
fieldname: 'invAmt',
|
|
||||||
fieldtype: 'Currency',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Invoice Date`,
|
|
||||||
fieldname: 'invDate',
|
|
||||||
fieldtype: 'Date',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Place of supply`,
|
|
||||||
fieldname: 'place',
|
|
||||||
fieldtype: 'Data',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Rate`,
|
|
||||||
fieldname: 'rate',
|
|
||||||
fieldtype: 'Data',
|
|
||||||
width: 0.5,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Taxable Value`,
|
|
||||||
fieldname: 'taxVal',
|
|
||||||
fieldtype: 'Currency',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Reverse Chrg.`,
|
|
||||||
fieldname: 'reverseCharge',
|
|
||||||
fieldtype: 'Data',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Intergrated Tax`,
|
|
||||||
fieldname: 'igstAmt',
|
|
||||||
fieldtype: 'Currency',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Central Tax`,
|
|
||||||
fieldname: 'cgstAmt',
|
|
||||||
fieldtype: 'Currency',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`State Tax`,
|
|
||||||
fieldname: 'sgstAmt',
|
|
||||||
fieldtype: 'Currency',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const transferType = filters.transferType || 'B2B';
|
|
||||||
if (transferType === 'B2B') {
|
|
||||||
columns.unshift({
|
|
||||||
label: t`GSTIN No.`,
|
|
||||||
fieldname: 'gstin',
|
|
||||||
fieldtype: 'Data',
|
|
||||||
width: 1.5,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return columns;
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,32 +0,0 @@
|
|||||||
import BaseGSTR from './BaseGSTR';
|
|
||||||
|
|
||||||
class GSTR1 extends BaseGSTR {
|
|
||||||
async run(params) {
|
|
||||||
if (!Object.keys(params).length) return [];
|
|
||||||
|
|
||||||
let filters = {};
|
|
||||||
filters.cancelled = 0;
|
|
||||||
if (params.toDate || params.fromDate) {
|
|
||||||
filters.date = [];
|
|
||||||
|
|
||||||
if (params.place) filters.place = params.place;
|
|
||||||
if (params.toDate) filters.date.push('<=', params.toDate);
|
|
||||||
if (params.fromDate) filters.date.push('>=', params.fromDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await this.getCompleteReport('GSTR-1', filters);
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const conditions = {
|
|
||||||
'B2B': row => row.gstin,
|
|
||||||
'B2CL': row => !row.gstin && !row.inState && row.invAmt >= 250000,
|
|
||||||
'B2CS': row => !row.gstin && (row.inState || row.invAmt < 250000),
|
|
||||||
'NR': row => (row.rate === 0), // this takes care of both nil rated, exempted goods
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!params.transferType) return data;
|
|
||||||
return data.filter((row) => conditions[params.transferType](row));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GSTR1;
|
|
13
reports/GoodsAndServiceTax/GSTR1.ts
Normal file
13
reports/GoodsAndServiceTax/GSTR1.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Action } from 'fyo/model/types';
|
||||||
|
import { BaseGSTR } from './BaseGSTR';
|
||||||
|
import { GSTRType } from './types';
|
||||||
|
|
||||||
|
export class GSTR1 extends BaseGSTR {
|
||||||
|
static title = 'GSTR1';
|
||||||
|
static reportName = 'gstr-1';
|
||||||
|
|
||||||
|
gstrType: GSTRType = 'GSTR-1';
|
||||||
|
getActions(): Action[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@ -1,49 +0,0 @@
|
|||||||
const title = 'GSTR 1';
|
|
||||||
import { t } from 'fyo';
|
|
||||||
import { generateGstr1Csv, generateGstr1Json } from '../../accounting/gst';
|
|
||||||
import baseConfig from './BaseViewConfig';
|
|
||||||
|
|
||||||
const transferTypeMap = {
|
|
||||||
B2B: 'B2B',
|
|
||||||
B2CL: 'B2C-Large',
|
|
||||||
B2CS: 'B2C-Small',
|
|
||||||
NR: 'Nil Rated, Exempted and Non GST supplies',
|
|
||||||
};
|
|
||||||
|
|
||||||
const transferType = {
|
|
||||||
fieldtype: 'Select',
|
|
||||||
label: t`Transfer Type`,
|
|
||||||
placeholder: t`Transfer Type`,
|
|
||||||
fieldname: 'transferType',
|
|
||||||
options: Object.keys(transferTypeMap),
|
|
||||||
map: transferTypeMap,
|
|
||||||
default: 'B2B',
|
|
||||||
size: 'small',
|
|
||||||
};
|
|
||||||
|
|
||||||
const actions = [
|
|
||||||
{
|
|
||||||
group: t`Export`,
|
|
||||||
label: t`JSON`,
|
|
||||||
type: 'primary',
|
|
||||||
action: async (report, filters) => {
|
|
||||||
generateGstr1Json(report, filters);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
group: t`Export`,
|
|
||||||
label: t`CSV`,
|
|
||||||
type: 'primary',
|
|
||||||
action: async (report, filters) => {
|
|
||||||
generateGstr1Csv(report, filters);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: title,
|
|
||||||
method: 'gstr-1',
|
|
||||||
filterFields: [ transferType, ...baseConfig.filterFields],
|
|
||||||
actions: actions,
|
|
||||||
getColumns: baseConfig.getColumns,
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
import BaseGSTR from './BaseGSTR';
|
|
||||||
|
|
||||||
class GSTR2 extends BaseGSTR {
|
|
||||||
async run(params) {
|
|
||||||
if (!Object.keys(params).length) return [];
|
|
||||||
|
|
||||||
let filters = {};
|
|
||||||
if (params.toDate || params.fromDate) {
|
|
||||||
filters.date = [];
|
|
||||||
if (params.toDate) filters.date.push('<=', params.toDate);
|
|
||||||
if (params.fromDate) filters.date.push('>=', params.fromDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await this.getCompleteReport('GSTR-2', filters);
|
|
||||||
|
|
||||||
// prettier-ignore
|
|
||||||
const conditions = {
|
|
||||||
'B2B': row => row.gstin,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!params.transferType) return data;
|
|
||||||
return data.filter((row) => conditions[params.transferType](row));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default GSTR2;
|
|
13
reports/GoodsAndServiceTax/GSTR2.ts
Normal file
13
reports/GoodsAndServiceTax/GSTR2.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Action } from 'fyo/model/types';
|
||||||
|
import { BaseGSTR } from './BaseGSTR';
|
||||||
|
import { GSTRType } from './types';
|
||||||
|
|
||||||
|
export class GSTR2 extends BaseGSTR {
|
||||||
|
static title = 'GSTR2';
|
||||||
|
static reportName = 'gstr-2';
|
||||||
|
|
||||||
|
gstrType: GSTRType = 'GSTR-2';
|
||||||
|
getActions(): Action[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
@ -1,38 +0,0 @@
|
|||||||
const title = 'GSTR 2';
|
|
||||||
import { t } from 'fyo';
|
|
||||||
import { generateGstr2Csv } from '../../accounting/gst';
|
|
||||||
import baseConfig from './BaseViewConfig';
|
|
||||||
|
|
||||||
const transferTypeMap = {
|
|
||||||
B2B: 'B2B',
|
|
||||||
};
|
|
||||||
|
|
||||||
const transferType = {
|
|
||||||
fieldtype: 'Select',
|
|
||||||
label: t`Transfer Type`,
|
|
||||||
placeholder: t`Transfer Type`,
|
|
||||||
fieldname: 'transferType',
|
|
||||||
options: Object.keys(transferTypeMap),
|
|
||||||
map: transferTypeMap,
|
|
||||||
default: 'B2B',
|
|
||||||
size: 'small',
|
|
||||||
};
|
|
||||||
|
|
||||||
const actions = [
|
|
||||||
{
|
|
||||||
group: t`Export`,
|
|
||||||
label: t`CSV`,
|
|
||||||
type: 'primary',
|
|
||||||
action: async (report, filters) => {
|
|
||||||
generateGstr2Csv(report, filters);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: title,
|
|
||||||
method: 'gstr-2',
|
|
||||||
filterFields: [ transferType, ...baseConfig.filterFields],
|
|
||||||
actions: actions,
|
|
||||||
getColumns: baseConfig.getColumns,
|
|
||||||
};
|
|
28
reports/GoodsAndServiceTax/types.ts
Normal file
28
reports/GoodsAndServiceTax/types.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
export enum TransferTypeEnum {
|
||||||
|
'B2B' = 'B2B',
|
||||||
|
'B2CL' = 'B2C',
|
||||||
|
'B2CS' = 'B2C',
|
||||||
|
'NR' = 'NR',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TransferType = keyof typeof TransferTypeEnum;
|
||||||
|
export type GSTRType = 'GSTR-1' | 'GSTR-2';
|
||||||
|
export interface GSTRRow {
|
||||||
|
gstin: string;
|
||||||
|
partyName: string;
|
||||||
|
invNo: string;
|
||||||
|
invDate: Date;
|
||||||
|
rate: number;
|
||||||
|
reverseCharge: 'Y' | 'N';
|
||||||
|
inState: boolean;
|
||||||
|
place: string;
|
||||||
|
invAmt: number;
|
||||||
|
taxVal: number;
|
||||||
|
igstAmt?: number;
|
||||||
|
cgstAmt?: number;
|
||||||
|
sgstAmt?: number;
|
||||||
|
exempt?: boolean;
|
||||||
|
nonGST?: boolean;
|
||||||
|
nilRated?: boolean;
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
import { BalanceSheet } from './BalanceSheet/BalanceSheet';
|
import { BalanceSheet } from './BalanceSheet/BalanceSheet';
|
||||||
import { GeneralLedger } from './GeneralLedger/GeneralLedger';
|
import { GeneralLedger } from './GeneralLedger/GeneralLedger';
|
||||||
|
import { GSTR1 } from './GoodsAndServiceTax/GSTR1';
|
||||||
|
import { GSTR2 } from './GoodsAndServiceTax/GSTR2';
|
||||||
import { ProfitAndLoss } from './ProfitAndLoss/ProfitAndLoss';
|
import { ProfitAndLoss } from './ProfitAndLoss/ProfitAndLoss';
|
||||||
import { TrialBalance } from './TrialBalance/TrialBalance';
|
import { TrialBalance } from './TrialBalance/TrialBalance';
|
||||||
|
|
||||||
@ -8,4 +10,6 @@ export const reports = {
|
|||||||
ProfitAndLoss,
|
ProfitAndLoss,
|
||||||
BalanceSheet,
|
BalanceSheet,
|
||||||
TrialBalance,
|
TrialBalance,
|
||||||
|
GSTR1,
|
||||||
|
GSTR2,
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { isNumeric } from 'src/utils';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Base',
|
name: 'Base',
|
||||||
props: {
|
props: {
|
||||||
@ -105,9 +107,7 @@ export default {
|
|||||||
parse(value) {
|
parse(value) {
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
isNumeric(df) {
|
isNumeric,
|
||||||
return ['Int', 'Float', 'Currency'].includes(df.fieldtype);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -75,6 +75,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { Report } from 'reports/Report';
|
import { Report } from 'reports/Report';
|
||||||
|
import { FieldTypeEnum } from 'schemas/types';
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue';
|
||||||
import Paginator from '../Paginator.vue';
|
import Paginator from '../Paginator.vue';
|
||||||
import WithScroll from '../WithScroll.vue';
|
import WithScroll from '../WithScroll.vue';
|
||||||
@ -143,6 +144,17 @@ export default defineComponent({
|
|||||||
styles['padding-left'] = '0px';
|
styles['padding-left'] = '0px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!cell.align &&
|
||||||
|
[
|
||||||
|
FieldTypeEnum.Currency,
|
||||||
|
FieldTypeEnum.Int,
|
||||||
|
FieldTypeEnum.Float,
|
||||||
|
].includes(cell.fieldtype)
|
||||||
|
) {
|
||||||
|
styles['text-align'] = 'right';
|
||||||
|
}
|
||||||
|
|
||||||
if (i === this.report.columns.length - 1) {
|
if (i === this.report.columns.length - 1) {
|
||||||
styles['padding-right'] = '0px';
|
styles['padding-right'] = '0px';
|
||||||
}
|
}
|
||||||
|
@ -11,24 +11,18 @@
|
|||||||
<div class="w-1/2 flex flex-col gap-5 mt-8">
|
<div class="w-1/2 flex flex-col gap-5 mt-8">
|
||||||
<!-- Ledgend Item -->
|
<!-- Ledgend Item -->
|
||||||
<div
|
<div
|
||||||
class="flex justify-between items-center text-sm"
|
class="flex items-center text-sm"
|
||||||
v-for="(d, i) in expenses"
|
v-for="(d, i) in expenses"
|
||||||
:key="d.name"
|
:key="d.name"
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="flex items-center"
|
|
||||||
@mouseover="active = i"
|
@mouseover="active = i"
|
||||||
@mouseleave="active = null"
|
@mouseleave="active = null"
|
||||||
>
|
>
|
||||||
<div class="w-3 h-3 rounded-sm flex-shrink-0" :class="d.class" />
|
<div class="w-3 h-3 rounded-sm flex-shrink-0" :class="d.class" />
|
||||||
<p
|
<p class="ml-2 overflow-x-scroll whitespace-nowrap no-scrollbar w-28">
|
||||||
class="ml-2 w-24 overflow-x-scroll whitespace-nowrap no-scrollbar"
|
|
||||||
>
|
|
||||||
{{ d.account }}
|
{{ d.account }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<p class="whitespace-nowrap flex-shrink-0 ml-auto">
|
||||||
<p class="whitespace-nowrap">
|
{{ fyo.format(d?.total ?? 0, 'Currency') }}
|
||||||
{{ fyo.format(d.total, 'Currency') }}
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -116,7 +110,9 @@ export default {
|
|||||||
{ class: 'bg-gray-200', hex: theme.backgroundColor.gray['200'] },
|
{ class: 'bg-gray-200', hex: theme.backgroundColor.gray['200'] },
|
||||||
];
|
];
|
||||||
|
|
||||||
topExpenses = topExpenses.map((d, i) => {
|
topExpenses = topExpenses
|
||||||
|
.filter((e) => e.total > 0)
|
||||||
|
.map((d, i) => {
|
||||||
d.color = shades[i].hex;
|
d.color = shades[i].hex;
|
||||||
d.class = shades[i].class;
|
d.class = shades[i].class;
|
||||||
return d;
|
return d;
|
||||||
|
@ -6,6 +6,7 @@ import { Doc } from 'fyo/model/doc';
|
|||||||
import { isPesa } from 'fyo/utils';
|
import { isPesa } from 'fyo/utils';
|
||||||
import { DuplicateEntryError, LinkValidationError } from 'fyo/utils/errors';
|
import { DuplicateEntryError, LinkValidationError } from 'fyo/utils/errors';
|
||||||
import Money from 'pesa/dist/types/src/money';
|
import Money from 'pesa/dist/types/src/money';
|
||||||
|
import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
|
||||||
|
|
||||||
export function stringifyCircular(
|
export function stringifyCircular(
|
||||||
obj: unknown,
|
obj: unknown,
|
||||||
@ -97,3 +98,17 @@ export function getErrorMessage(e: Error, doc?: Doc): string {
|
|||||||
|
|
||||||
return errorMessage;
|
return errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isNumeric(fieldtype: Field | FieldType): boolean {
|
||||||
|
if (typeof fieldtype !== 'string') {
|
||||||
|
fieldtype = fieldtype?.fieldtype;
|
||||||
|
}
|
||||||
|
|
||||||
|
const numericTypes: FieldType[] = [
|
||||||
|
FieldTypeEnum.Int,
|
||||||
|
FieldTypeEnum.Float,
|
||||||
|
FieldTypeEnum.Currency,
|
||||||
|
];
|
||||||
|
|
||||||
|
return numericTypes.includes(fieldtype);
|
||||||
|
}
|
||||||
|
@ -158,20 +158,18 @@ function getCompleteSidebar(): SidebarConfig {
|
|||||||
name: 'trial-balance',
|
name: 'trial-balance',
|
||||||
route: '/report/TrialBalance',
|
route: '/report/TrialBalance',
|
||||||
},
|
},
|
||||||
/*
|
|
||||||
{
|
{
|
||||||
label: t`GSTR1`,
|
label: t`GSTR1`,
|
||||||
name: 'gstr1',
|
name: 'gstr1',
|
||||||
route: '/report/gstr-1',
|
route: '/report/GSTR1',
|
||||||
hidden: () => fyo.singles.AccountingSettings!.country !== 'India',
|
hidden: () => fyo.singles.AccountingSettings!.country !== 'India',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: t`GSTR2`,
|
label: t`GSTR2`,
|
||||||
name: 'gstr2',
|
name: 'gstr2',
|
||||||
route: '/report/gstr-2',
|
route: '/report/GSTR2',
|
||||||
hidden: () => fyo.singles.AccountingSettings!.country !== 'India',
|
hidden: () => fyo.singles.AccountingSettings!.country !== 'India',
|
||||||
},
|
},
|
||||||
*/
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user