mirror of
https://github.com/frappe/books.git
synced 2024-12-22 02:49:03 +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;
|
||||
}, {} as Record<string, string>);
|
||||
|
||||
export const flow = [
|
||||
0.15, 0.1, 0.25, 0.1, 0.01, 0.01, 0.01, 0.05, 0, 0.15, 0.2, 0.4,
|
||||
];
|
||||
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];
|
||||
export function getFlowConstant(months: number) {
|
||||
// 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 { Doc } from 'fyo/model/doc';
|
||||
import { EmptyMessageMap, FormulaMap, ListsMap } from 'fyo/model/types';
|
||||
import { stateCodeMap } from 'regional/in';
|
||||
import { titleCase } from 'utils';
|
||||
import { codeStateMap } from 'regional/in';
|
||||
import { getCountryInfo } from 'utils/misc';
|
||||
|
||||
export class Address extends Doc {
|
||||
@ -36,7 +35,7 @@ export class Address extends Doc {
|
||||
const country = doc?.country as string | undefined;
|
||||
switch (country) {
|
||||
case 'India':
|
||||
return Object.keys(stateCodeMap).map(titleCase).sort();
|
||||
return Object.values(codeStateMap).sort();
|
||||
default:
|
||||
return [] as string[];
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ export abstract class Invoice extends Transactional {
|
||||
account?: string;
|
||||
currency?: string;
|
||||
netTotal?: Money;
|
||||
grandTotal?: Money;
|
||||
baseGrandTotal?: Money;
|
||||
outstandingAmount?: Money;
|
||||
exchangeRate?: number;
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { FormulaMap, ListsMap } from 'fyo/model/types';
|
||||
import { Address as BaseAddress } from 'models/baseModels/Address/Address';
|
||||
import { stateCodeMap } from 'regional/in';
|
||||
import { titleCase } from 'utils';
|
||||
import { codeStateMap } from 'regional/in';
|
||||
|
||||
export class Address extends BaseAddress {
|
||||
formulas: FormulaMap = {
|
||||
@ -30,7 +29,7 @@ export class Address extends BaseAddress {
|
||||
|
||||
pos: {
|
||||
formula: async () => {
|
||||
const stateList = Object.keys(stateCodeMap).map(titleCase).sort();
|
||||
const stateList = Object.values(codeStateMap).sort();
|
||||
const state = this.state as string;
|
||||
if (stateList.includes(state)) {
|
||||
return state;
|
||||
@ -44,7 +43,7 @@ export class Address extends BaseAddress {
|
||||
static lists: ListsMap = {
|
||||
...BaseAddress.lists,
|
||||
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';
|
||||
|
||||
export class Party extends BaseParty {
|
||||
gstin?: string;
|
||||
gstType?: GSTType;
|
||||
|
||||
async beforeSync() {
|
||||
const gstin = this.get('gstin') as string | undefined;
|
||||
const gstType = this.get('gstType') as GSTType;
|
||||
|
@ -1,39 +1,38 @@
|
||||
// prettier-ignore
|
||||
export const stateCodeMap = {
|
||||
'JAMMU AND KASHMIR': '01',
|
||||
'HIMACHAL PRADESH': '02',
|
||||
'PUNJAB': '03',
|
||||
'CHANDIGARH': '04',
|
||||
'UTTARAKHAND': '05',
|
||||
'HARYANA': '06',
|
||||
'DELHI': '07',
|
||||
'RAJASTHAN': '08',
|
||||
'UTTAR PRADESH': '09',
|
||||
'BIHAR': '10',
|
||||
'SIKKIM': '11',
|
||||
'ARUNACHAL PRADESH': '12',
|
||||
'NAGALAND': '13',
|
||||
'MANIPUR': '14',
|
||||
'MIZORAM': '15',
|
||||
'TRIPURA': '16',
|
||||
'MEGHALAYA': '17',
|
||||
'ASSAM': '18',
|
||||
'WEST BENGAL': '19',
|
||||
'JHARKHAND': '20',
|
||||
'ODISHA': '21',
|
||||
'CHATTISGARH': '22',
|
||||
'MADHYA PRADESH': '23',
|
||||
'GUJARAT': '24',
|
||||
'DADRA AND NAGAR HAVELI AND DAMAN AND DIU': '26',
|
||||
'MAHARASHTRA': '27',
|
||||
'KARNATAKA': '29',
|
||||
'GOA': '30',
|
||||
'LAKSHADWEEP': '31',
|
||||
'KERALA': '32',
|
||||
'TAMIL NADU': '33',
|
||||
'PUDUCHERRY': '34',
|
||||
'ANDAMAN AND NICOBAR ISLANDS': '35',
|
||||
'TELANGANA': '36',
|
||||
'ANDHRA PRADESH': '37',
|
||||
'LADAKH': '38',
|
||||
};
|
||||
export const codeStateMap = {
|
||||
'01': 'Jammu and Kashmir',
|
||||
'02': 'Himachal Pradesh',
|
||||
'03': 'Punjab',
|
||||
'04': 'Chandigarh',
|
||||
'05': 'Uttarakhand',
|
||||
'06': 'Haryana',
|
||||
'07': 'Delhi',
|
||||
'08': 'Rajasthan',
|
||||
'09': 'Uttar Pradesh',
|
||||
'10': 'Bihar',
|
||||
'11': 'Sikkim',
|
||||
'12': 'Arunachal Pradesh',
|
||||
'13': 'Nagaland',
|
||||
'14': 'Manipur',
|
||||
'15': 'Mizoram',
|
||||
'16': 'Tripura',
|
||||
'17': 'Meghalaya',
|
||||
'18': 'Assam',
|
||||
'19': 'West Bengal',
|
||||
'20': 'Jharkhand',
|
||||
'21': 'Odisha',
|
||||
'22': 'Chattisgarh',
|
||||
'23': 'Madhya Pradesh',
|
||||
'24': 'Gujarat',
|
||||
'26': 'Dadra and Nagar Haveli and Daman and Diu',
|
||||
'27': 'Maharashtra',
|
||||
'29': 'Karnataka',
|
||||
'30': 'Goa',
|
||||
'31': 'Lakshadweep',
|
||||
'32': 'Kerala',
|
||||
'33': 'Tamil Nadu',
|
||||
'34': 'Puducherry',
|
||||
'35': 'Andaman and Nicobar Islands',
|
||||
'36': 'Telangana',
|
||||
'37': 'Andhra Pradesh',
|
||||
'38': 'Ladakh',
|
||||
} 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 { GeneralLedger } from './GeneralLedger/GeneralLedger';
|
||||
import { GSTR1 } from './GoodsAndServiceTax/GSTR1';
|
||||
import { GSTR2 } from './GoodsAndServiceTax/GSTR2';
|
||||
import { ProfitAndLoss } from './ProfitAndLoss/ProfitAndLoss';
|
||||
import { TrialBalance } from './TrialBalance/TrialBalance';
|
||||
|
||||
@ -8,4 +10,6 @@ export const reports = {
|
||||
ProfitAndLoss,
|
||||
BalanceSheet,
|
||||
TrialBalance,
|
||||
GSTR1,
|
||||
GSTR2,
|
||||
};
|
||||
|
@ -21,6 +21,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isNumeric } from 'src/utils';
|
||||
|
||||
export default {
|
||||
name: 'Base',
|
||||
props: {
|
||||
@ -105,9 +107,7 @@ export default {
|
||||
parse(value) {
|
||||
return value;
|
||||
},
|
||||
isNumeric(df) {
|
||||
return ['Int', 'Float', 'Currency'].includes(df.fieldtype);
|
||||
},
|
||||
isNumeric,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -75,6 +75,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import { Report } from 'reports/Report';
|
||||
import { FieldTypeEnum } from 'schemas/types';
|
||||
import { defineComponent } from 'vue';
|
||||
import Paginator from '../Paginator.vue';
|
||||
import WithScroll from '../WithScroll.vue';
|
||||
@ -143,6 +144,17 @@ export default defineComponent({
|
||||
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) {
|
||||
styles['padding-right'] = '0px';
|
||||
}
|
||||
|
@ -11,24 +11,18 @@
|
||||
<div class="w-1/2 flex flex-col gap-5 mt-8">
|
||||
<!-- Ledgend Item -->
|
||||
<div
|
||||
class="flex justify-between items-center text-sm"
|
||||
class="flex items-center text-sm"
|
||||
v-for="(d, i) in expenses"
|
||||
:key="d.name"
|
||||
@mouseover="active = i"
|
||||
@mouseleave="active = null"
|
||||
>
|
||||
<div
|
||||
class="flex items-center"
|
||||
@mouseover="active = i"
|
||||
@mouseleave="active = null"
|
||||
>
|
||||
<div class="w-3 h-3 rounded-sm flex-shrink-0" :class="d.class" />
|
||||
<p
|
||||
class="ml-2 w-24 overflow-x-scroll whitespace-nowrap no-scrollbar"
|
||||
>
|
||||
{{ d.account }}
|
||||
</p>
|
||||
</div>
|
||||
<p class="whitespace-nowrap">
|
||||
{{ fyo.format(d.total, 'Currency') }}
|
||||
<div class="w-3 h-3 rounded-sm flex-shrink-0" :class="d.class" />
|
||||
<p class="ml-2 overflow-x-scroll whitespace-nowrap no-scrollbar w-28">
|
||||
{{ d.account }}
|
||||
</p>
|
||||
<p class="whitespace-nowrap flex-shrink-0 ml-auto">
|
||||
{{ fyo.format(d?.total ?? 0, 'Currency') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -116,11 +110,13 @@ export default {
|
||||
{ class: 'bg-gray-200', hex: theme.backgroundColor.gray['200'] },
|
||||
];
|
||||
|
||||
topExpenses = topExpenses.map((d, i) => {
|
||||
d.color = shades[i].hex;
|
||||
d.class = shades[i].class;
|
||||
return d;
|
||||
});
|
||||
topExpenses = topExpenses
|
||||
.filter((e) => e.total > 0)
|
||||
.map((d, i) => {
|
||||
d.color = shades[i].hex;
|
||||
d.class = shades[i].class;
|
||||
return d;
|
||||
});
|
||||
|
||||
this.expenses = topExpenses;
|
||||
},
|
||||
|
@ -6,6 +6,7 @@ import { Doc } from 'fyo/model/doc';
|
||||
import { isPesa } from 'fyo/utils';
|
||||
import { DuplicateEntryError, LinkValidationError } from 'fyo/utils/errors';
|
||||
import Money from 'pesa/dist/types/src/money';
|
||||
import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
|
||||
|
||||
export function stringifyCircular(
|
||||
obj: unknown,
|
||||
@ -97,3 +98,17 @@ export function getErrorMessage(e: Error, doc?: Doc): string {
|
||||
|
||||
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',
|
||||
route: '/report/TrialBalance',
|
||||
},
|
||||
/*
|
||||
{
|
||||
label: t`GSTR1`,
|
||||
name: 'gstr1',
|
||||
route: '/report/gstr-1',
|
||||
route: '/report/GSTR1',
|
||||
hidden: () => fyo.singles.AccountingSettings!.country !== 'India',
|
||||
},
|
||||
{
|
||||
label: t`GSTR2`,
|
||||
name: 'gstr2',
|
||||
route: '/report/gstr-2',
|
||||
route: '/report/GSTR2',
|
||||
hidden: () => fyo.singles.AccountingSettings!.country !== 'India',
|
||||
},
|
||||
*/
|
||||
],
|
||||
},
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user