2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

- GSTR1, GSTR2, GSTR3B Reports

- Message Dialog
- Handle Error in Form Submit and Revert
- Report Filter Custom Doc
This commit is contained in:
thefalconx33 2019-07-30 17:32:49 +05:30
parent 472451203c
commit ed164a492e
32 changed files with 1467 additions and 678 deletions

View File

@ -0,0 +1,76 @@
const frappe = require('frappejs');
module.exports = {
name: 'GSTR3B',
doctype: 'DocType',
documentClass: require('./GSTR3BDocument.js'),
print: {
printFormat: 'GSTR3B Print Format'
},
keywordFields: ['name', 'month', 'year'],
fields: [
{
fieldname: 'year',
label: 'Year',
fieldtype: 'Data',
required: 1
},
{
fieldname: 'month',
label: 'Month',
fieldtype: 'Select',
options: [
'',
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
],
required: 1
},
{
fieldname: 'jsonData',
label: 'JSON Data',
fieldtype: 'Code',
formula: doc => doc.getJson(),
required: 1,
disabled: 1,
rows: 15
}
],
layout: [
{
columns: [{ fields: ['year', 'month', 'jsonData'] }]
}
],
links: [
{
label: 'Print PDF',
condition: form => !form.doc._notInserted,
action: async form => {
form.$router.push({
path: `/print/GSTR3B/${form.doc.name}`
});
}
},
{
label: 'Delete',
condition: form => !form.doc._notInserted,
action: async form => {
const doc = await frappe.getDoc('GSTR3B', form.doc.name);
await doc.delete();
form.$router.push({
path: `/list/GSTR3B`
});
}
}
]
};

View File

@ -0,0 +1,147 @@
const BaseDocument = require('frappejs/model/document');
const frappe = require('frappejs');
const { format } = require('./GSTR3BFormat');
module.exports = class GSTR3B extends BaseDocument {
async getData() {
const monthIndex = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
].indexOf(this.month);
const month = monthIndex + 1 > 9 ? monthIndex + 1 : `0${monthIndex + 1}`;
const lastDate = new Date(this.year, monthIndex + 1, 0).getDate();
const filters = {
date: [
'>=',
`${this.year}-${month}-01`,
'<=',
`${this.year}-${month}-${lastDate}`
]
};
const invoices = frappe.db.getAll({
doctype: 'Invoice',
filters,
fields: ['*']
});
const bills = frappe.db.getAll({
doctype: 'Bill',
filters,
fields: ['*']
});
const [gstr1Data, gstr2Data] = await Promise.all([invoices, bills]);
let gstr3bData = [[], []];
for (let ledgerEntry of gstr1Data) {
ledgerEntry.doctype = 'Invoice';
gstr3bData[0].push(await this.makeGSTRow(ledgerEntry));
}
for (let ledgerEntry of gstr2Data) {
ledgerEntry.doctype = 'Bill';
gstr3bData[1].push(await this.makeGSTRow(ledgerEntry));
}
return gstr3bData;
}
async makeGSTRow(ledgerEntry) {
let row = {};
ledgerEntry = await frappe.getDoc(ledgerEntry.doctype, ledgerEntry.name);
let party = await frappe.getDoc(
'Party',
ledgerEntry.customer || ledgerEntry.supplier
);
if (party.address) {
let addressDetails = await frappe.getDoc('Address', party.address);
row.place = addressDetails.state || '';
}
row.gstin = party.gstin;
row.partyName = ledgerEntry.customer || ledgerEntry.supplier;
row.invNo = ledgerEntry.name;
row.invDate = ledgerEntry.date;
row.rate = 0;
row.inState = true;
row.reverseCharge = !party.gstin ? 'Y' : 'N';
ledgerEntry.taxes.forEach(tax => {
row.rate += tax.rate;
const taxAmt = (tax.rate * ledgerEntry.netTotal) / 100;
if (tax.account === 'IGST') row.igstAmt = taxAmt;
if (tax.account === 'IGST') row.inState = false;
if (tax.account === 'CGST') row.cgstAmt = taxAmt;
if (tax.account === 'SGST') row.sgstAmt = taxAmt;
if (tax.account === 'Nil Rated') row.nilRated = true;
if (tax.account === 'Exempt') row.exempt = true;
if (tax.account === 'Non GST') row.nonGST = true;
});
row.invAmt = ledgerEntry.grandTotal;
row.taxVal = ledgerEntry.netTotal;
return row;
}
async createJson(data) {
let jsonData = JSON.parse(JSON.stringify(format));
for (let ledgerEntry of data[0]) {
if (ledgerEntry.rate > 0) {
jsonData['sup_details']['osup_det']['samt'] += ledgerEntry.sgstAmt || 0;
jsonData['sup_details']['osup_det']['camt'] += ledgerEntry.cgstAmt || 0;
jsonData['sup_details']['osup_det']['iamt'] += ledgerEntry.igstAmt || 0;
jsonData['sup_details']['osup_det']['txval'] += ledgerEntry.taxVal;
}
if (ledgerEntry.rate === 0) {
jsonData['sup_details']['osup_zero']['txval'] += ledgerEntry.taxVal;
}
if (ledgerEntry.nilRated || ledgerEntry.exempt) {
jsonData['sup_details']['osup_nil_exmp']['txval'] += ledgerEntry.taxVal;
}
if (ledgerEntry.nonGST) {
jsonData['sup_details']['osup_nongst']['txval'] += ledgerEntry.taxVal;
}
if (!ledgerEntry.inState && !ledgerEntry.gstin) {
jsonData['inter_sup']['unreg_details'].push({
pos: ledgerEntry.place,
txval: ledgerEntry.taxVal,
iAmt: ledgerEntry.igstAmt || 0
});
}
}
for (let ledgerEntry of data[1]) {
if (ledgerEntry.reverseCharge === 'Y') {
jsonData['sup_details']['isup_rev']['samt'] += ledgerEntry.sgstAmt || 0;
jsonData['sup_details']['isup_rev']['camt'] += ledgerEntry.cgstAmt || 0;
jsonData['sup_details']['isup_rev']['iamt'] += ledgerEntry.igstAmt || 0;
jsonData['sup_details']['isup_rev']['txval'] += ledgerEntry.taxVal;
}
if (ledgerEntry.nilRated || ledgerEntry.exempt) {
jsonData['inward_sup']['isup_details'][0][
ledgerEntry.inState ? 'intra' : 'inter'
] += ledgerEntry.taxVal;
}
if (ledgerEntry.nonGST) {
jsonData['inward_sup']['isup_details'][0][
ledgerEntry.inState ? 'intra' : 'inter'
] += ledgerEntry.taxVal;
}
}
return jsonData;
}
async getJson() {
if (this.year && this.month) {
const data = await this.getData();
const json = await this.createJson(data);
return JSON.stringify(json, undefined, 2);
}
}
};

View File

@ -0,0 +1,390 @@
const format = {
gstin: '',
ret_period: '',
inward_sup: {
isup_details: [
{
ty: 'GST',
intra: 0,
inter: 0
},
{
ty: 'NONGST',
inter: 0,
intra: 0
}
]
},
sup_details: {
osup_zero: {
csamt: 0,
txval: 0,
iamt: 0
},
osup_nil_exmp: {
txval: 0
},
osup_det: {
samt: 0,
csamt: 0,
txval: 0,
camt: 0,
iamt: 0
},
isup_rev: {
samt: 0,
csamt: 0,
txval: 0,
camt: 0,
iamt: 0
},
osup_nongst: {
txval: 0
}
},
inter_sup: {
unreg_details: [],
comp_details: [],
uin_details: []
},
itc_elg: {
itc_avl: [
{
csamt: 0,
samt: 0,
ty: 'IMPG',
camt: 0,
iamt: 0
},
{
csamt: 0,
samt: 0,
ty: 'IMPS',
camt: 0,
iamt: 0
},
{
samt: 0,
csamt: 0,
ty: 'ISRC',
camt: 0,
iamt: 0
},
{
ty: 'ISD',
iamt: 0,
camt: 0,
samt: 0,
csamt: 0
},
{
samt: 0,
csamt: 0,
ty: 'OTH',
camt: 0,
iamt: 0
}
],
itc_rev: [
{
ty: 'RUL',
iamt: 0,
camt: 0,
samt: 0,
csamt: 0
},
{
ty: 'OTH',
iamt: 0,
camt: 0,
samt: 0,
csamt: 0
}
],
itc_net: {
samt: 0,
csamt: 0,
camt: 0,
iamt: 0
},
itc_inelg: [
{
ty: 'RUL',
iamt: 0,
camt: 0,
samt: 0,
csamt: 0
},
{
ty: 'OTH',
iamt: 0,
camt: 0,
samt: 0,
csamt: 0
}
]
}
};
function generateHTML(data) {
let template = `
<div class="p-5 m-5" style="font-size: 14px !important">
<div>
<h3 class="text-center">GSTR3B-Form</h3>
<h5>GSTIN: &nbsp ${data.gstin}</h5>
<h5>Period: &nbsp ${data.ret_period}</h5>
</div>
<h5>3.1&nbsp&nbspDetails of Outward Supplies and inward supplies liable to reverse charge</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>Nature Of Supplies</th>
<th>Total Taxable value</th>
<th>Integrated Tax</th>
<th>Central Tax</th>
<th>State/UT Tax</th>
<th>Cess</th>
</tr>
</thead>
<tbody>
<tr>
<td>(a) Outward taxable supplies(other than zero rated, nil rated and exempted</td>
<td class="right">${data.sup_details.osup_det.txval}</td>
<td class="right">${data.sup_details.osup_det.iamt}</td>
<td class="right">${data.sup_details.osup_det.camt}</td>
<td class="right">${data.sup_details.osup_det.samt}</td>
<td class="right">${data.sup_details.osup_det.csamt}</td>
</tr>
<tr>
<td>(b) Outward taxable supplies(zero rated)</td>
<td class="right">${data.sup_details.osup_zero.txval}</td>
<td class="right">${data.sup_details.osup_zero.iamt}</td>
<td style="background-color:#d9d9d9;"></td>
<td style="background-color:#d9d9d9;"></td>
<td class="right">${data.sup_details.osup_zero.csamt}</td>
</tr>
<tr>
<td>(b) Other outward supplies(Nil rated,Exempted)</td>
<td class="right">${data.sup_details.osup_nil_exmp.txval}</td>
<td style="background-color:#d9d9d9;"></td>
<td style="background-color:#d9d9d9;"></td>
<td style="background-color:#d9d9d9;"></td>
<td style="background-color:#d9d9d9;"></td>
<tr>
<td>(d) Inward Supplies(liable to reverse charge</td>
<td class="right">${data.sup_details.isup_rev.txval}</td>
<td class="right">${data.sup_details.isup_rev.iamt}</td>
<td class="right">${data.sup_details.isup_rev.camt}</td>
<td class="right">${data.sup_details.isup_rev.samt}</td>
<td class="right">${data.sup_details.isup_rev.csamt}</td>
</tr>
<tr>
<td>(e) Non-GST outward supplies</td>
<td class="right">${data.sup_details.osup_nongst.txval}</td>
<td style="background-color:#d9d9d9;"></td>
<td style="background-color:#d9d9d9;"></td>
<td style="background-color:#d9d9d9;"></td>
<td style="background-color:#d9d9d9;"></td>
</tr>
</tbody>
</table>
<h5>
3.2&nbsp&nbspOf the supplies shown in 3.1 (a) above, details of inter-State supplies made to unregisterd
persons, composition taxable persons and UIN holders
</h5>
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Place Of Supply (State/UT)</th>
<th>Total Taxable Value</th>
<th>Amount of Integrated Tax</th>
</tr>
</thead>
<tbody>
<tr>
<td>Supplies made to Unregistered Persons</td>
<td class="right">`;
for (let row of data.inter_sup.unreg_details) {
if (row) template += row.pos + '<br>';
}
template += '</td><td class="right">';
for (let row of data.inter_sup.unreg_details) {
if (row) template += row.txval + '<br>';
}
template += '</td><td class="right">';
for (let row of data.inter_sup.unreg_details) {
if (row) template += row.iamt + '<br>';
}
template +=
'</td></tr><tr><td>Supplies made to Composition Taxable Persons</td><td class="right">';
for (let row of data.inter_sup.comp_details) {
if (row) template += row.pos + '<br>';
}
template += '</td><td class="right">';
for (let row of data.inter_sup.comp_details) {
if (row) template += row.txval + '<br>';
}
template += '</td><td class="right">';
for (let row of data.inter_sup.comp_details) {
if (row) template += row.iamt + '<br>';
}
template +=
'</td></tr><tr><td>Supplies made to UIN holders</td><td class="right">';
for (let row of data.inter_sup.uin_details) {
if (row) template += row.pos + '<br>';
}
template += '</td><td class="right">';
for (let row of data.inter_sup.uin_details) {
if (row) template += row.txval + '<br>';
}
template += '</td><td class="right">';
for (let row of data.inter_sup.uin_details) {
if (row) template += row.iamt + '<br>';
}
template += `</td>
</tr>
</tbody>
</table>
<h5>4. &nbsp Eligible ITC</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>Details</th>
<th>Integrated Tax</th>
<th>Central Tax</th>
<th>State/UT tax</th>
<th>Cess</th>
</tr>
</thead>
<tbody>
<tr>
<td><b>(A) ITC Available (whether in full op part)</b></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp (1) Import of goods </td>
<td class="right">${data.itc_elg.itc_avl[0].iamt}</td>
<td class="right">${data.itc_elg.itc_avl[0].camt}</td>
<td class="right">${data.itc_elg.itc_avl[0].samt}</td>
<td class="right">${data.itc_elg.itc_avl[0].csamt}</td>
</tr>
<tr>
<td>&nbsp (2) Import of services</td>
<td class="right">${data.itc_elg.itc_avl[1].iamt}</td>
<td class="right">${data.itc_elg.itc_avl[1].camt}</td>
<td class="right">${data.itc_elg.itc_avl[1].samt}</td>
<td class="right">${data.itc_elg.itc_avl[1].csamt}</td>
</tr>
<tr>
<td>&nbsp (3) Inward supplies liable to reverse charge (other than 1 & 2 above)</td>
<td class="right">${data.itc_elg.itc_avl[2].iamt}</td>
<td class="right">${data.itc_elg.itc_avl[2].camt}</td>
<td class="right">${data.itc_elg.itc_avl[2].samt}</td>
<td class="right">${data.itc_elg.itc_avl[2].csamt}</td>
</tr>
<tr>
<td>&nbsp (4) Inward supplies from ISD</td>
<td class="right">${data.itc_elg.itc_avl[3].iamt}</td>
<td class="right">${data.itc_elg.itc_avl[3].camt}</td>
<td class="right">${data.itc_elg.itc_avl[3].samt}</td>
<td class="right">${data.itc_elg.itc_avl[3].csamt}</td>
</tr>
<tr>
<td>&nbsp (5) All other ITC</td>
<td class="right">${data.itc_elg.itc_avl[4].iamt}</td>
<td class="right">${data.itc_elg.itc_avl[4].camt}</td>
<td class="right">${data.itc_elg.itc_avl[4].samt}</td>
<td class="right">${data.itc_elg.itc_avl[4].csamt}</td>
</tr>
<tr>
<td><b>(B) ITC Reversed</b></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp (1) As per rules 42 & 43 of CGST Rules</td>
<td class="right">${data.itc_elg.itc_rev[0].iamt}</td>
<td class="right">${data.itc_elg.itc_rev[0].camt}</td>
<td class="right">${data.itc_elg.itc_rev[0].samt}</td>
<td class="right">${data.itc_elg.itc_rev[0].csamt}</td>
</tr>
<tr>
<td>&nbsp (2) Others</td>
<td class="right">${data.itc_elg.itc_rev[1].iamt}</td>
<td class="right">${data.itc_elg.itc_rev[1].camt}</td>
<td class="right">${data.itc_elg.itc_rev[1].samt}</td>
<td class="right">${data.itc_elg.itc_rev[1].csamt}</td>
</tr>
<tr>
<td><b>(C) Net ITC Available(A) - (B)</b></td>
<td class="right">${data.itc_elg.itc_net.iamt}</td>
<td class="right">${data.itc_elg.itc_net.camt}</td>
<td class="right">${data.itc_elg.itc_net.samt}</td>
<td class="right">${data.itc_elg.itc_net.csamt}</td>
</tr>
<tr>
<td><b>(D) Ineligible ITC</b></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp (1) As per section 17(5)</td>
<td class="right">${data.itc_elg.itc_inelg[0].iamt}</td>
<td class="right">${data.itc_elg.itc_inelg[0].camt}</td>
<td class="right">${data.itc_elg.itc_inelg[0].samt}</td>
<td class="right">${data.itc_elg.itc_inelg[0].csamt}</td>
</tr>
<tr>
<td>&nbsp (2) Others</td>
<td class="right">${data.itc_elg.itc_inelg[1].iamt}</td>
<td class="right">${data.itc_elg.itc_inelg[1].camt}</td>
<td class="right">${data.itc_elg.itc_inelg[1].samt}</td>
<td class="right">${data.itc_elg.itc_inelg[1].csamt}</td>
</tr>
</tbody>
</table>
<h5>5. &nbsp&nbsp Values of exempt, nil rated and non-GST inward supplies</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>Nature of Supplies</th>
<th>Inter-State Supplies</th>
<th>Intra-State Supplies</th>
</tr>
</thead>
<tbody>
<tr>
<td>From a supplier under composition scheme, Exempt and Nil rated</td>
<td class="right">${data.inward_sup.isup_details[0].inter}</td>
<td class="right">${data.inward_sup.isup_details[0].intra}</td>
</tr>
<tr>
<td>Non GST Inward Supplies</td>
<td class="right">${data.inward_sup.isup_details[1].inter}</td>
<td class="right">${data.inward_sup.isup_details[1].intra}</td>
</tr>
</tbody>
</table>
</div>`;
return template;
}
module.exports = {
format
};

View File

@ -0,0 +1,7 @@
import { _ } from 'frappejs/utils';
export default {
doctype: 'GSTR3B',
title: _('GSTR 3B Report'),
columns: ['year', 'month']
};

View File

@ -0,0 +1,304 @@
<template>
<div>
<div class="row no-gutters">
<div class="col-8 mx-auto text-right mt-4">
<f-button primary @click="$emit('makePDF', $refs.printComponent.innerHTML)">{{ _('PDF') }}</f-button>
</div>
</div>
<div ref="printComponent" class="col-8 bg-white mt-4 mx-auto border shadow">
<div class="print-format" style="padding: 3.5rem;">
<div>
<h3 class="text-center">GSTR3B-Form</h3>
<h5>GSTIN: &nbsp; {{ jsonData.gstin }}</h5>
<h5>Period: &nbsp; {{ jsonData.ret_period }}</h5>
</div>
<h5>3.1&nbsp;&nbsp;Details of Outward Supplies and inward supplies liable to reverse charge</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>Nature Of Supplies</th>
<th>Total Taxable value</th>
<th>Integrated Tax</th>
<th>Central Tax</th>
<th>State/UT Tax</th>
<th>Cess</th>
</tr>
</thead>
<tbody>
<tr>
<td>(a) Outward taxable supplies(other than zero rated, nil rated and exempted</td>
<td class="right">{{ jsonData.sup_details.osup_det.txval }}</td>
<td class="right">{{ jsonData.sup_details.osup_det.iamt }}</td>
<td class="right">{{ jsonData.sup_details.osup_det.camt }}</td>
<td class="right">{{ jsonData.sup_details.osup_det.samt }}</td>
<td class="right">{{ jsonData.sup_details.osup_det.csamt }}</td>
</tr>
<tr>
<td>(b) Outward taxable supplies(zero rated)</td>
<td class="right">{{ jsonData.sup_details.osup_zero.txval }}</td>
<td class="right">{{ jsonData.sup_details.osup_zero.iamt }}</td>
<td class="disabled"></td>
<td class="disabled"></td>
<td class="right">{{ jsonData.sup_details.osup_zero.csamt }}</td>
</tr>
<tr>
<td>(b) Other outward supplies(Nil rated,Exempted)</td>
<td class="right">{{ jsonData.sup_details.osup_nil_exmp.txval }}</td>
<td class="disabled"></td>
<td class="disabled"></td>
<td class="disabled"></td>
<td class="disabled"></td>
</tr>
<tr>
<td>(d) Inward Supplies(liable to reverse charge</td>
<td class="right">{{ jsonData.sup_details.isup_rev.txval }}</td>
<td class="right">{{ jsonData.sup_details.isup_rev.iamt }}</td>
<td class="right">{{ jsonData.sup_details.isup_rev.camt }}</td>
<td class="right">{{ jsonData.sup_details.isup_rev.samt }}</td>
<td class="right">{{ jsonData.sup_details.isup_rev.csamt }}</td>
</tr>
<tr>
<td>(e) Non-GST outward supplies</td>
<td class="right">{{ jsonData.sup_details.osup_nongst.txval }}</td>
<td class="disabled"></td>
<td class="disabled"></td>
<td class="disabled"></td>
<td class="disabled"></td>
</tr>
</tbody>
</table>
<h5>3.2&nbsp;&nbsp;Of the supplies shown in 3.1 (a) above, details of inter-State supplies made to unregisterd persons, composition taxable persons and UIN holders</h5>
<table class="table table-bordered">
<thead>
<tr>
<th></th>
<th>Place Of Supply (State/UT)</th>
<th>Total Taxable Value</th>
<th>Amount of Integrated Tax</th>
</tr>
</thead>
<tbody>
<tr>
<td>Supplies made to Unregistered Persons</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.unreg_details" :key="i">
<p>{{ row.pos }}</p>
</div>
</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.unreg_details" :key="i">
<p>{{ row.txval }}</p>
</div>
</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.unreg_details" :key="i">
<p>{{ row.iamt }}</p>
</div>
</td>
</tr>
<tr>
<td>Suppliies made to Composition Taxable Persons</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.comp_details" :key="i">
<p>{{ row.pos }}</p>
</div>
</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.comp_details" :key="i">
<p>{{ row.txval }}</p>
</div>
</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.comp_details" :key="i">
<p>{{ row.iamt }}</p>
</div>
</td>
</tr>
<tr>
<td>Supplies made to UIN holders</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.uin_details" :key="i">
<p>{{ row.pos }}</p>
</div>
</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.uin_details" :key="i">
<p>{{ row.txval }}</p>
</div>
</td>
<td class="right">
<div v-for="(row, i) in jsonData.inter_sup.uin_details" :key="i">
<p>{{ row.iamt }}</p>
</div>
</td>
</tr>
</tbody>
</table>
<h5>4. &nbsp; Eligible ITC</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>Details</th>
<th>Integrated Tax</th>
<th>Central Tax</th>
<th>State/UT tax</th>
<th>Cess</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<b>(A) ITC Available (whether in full op part)</b>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp; (1) Import of goods</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[0].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[0].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[0].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[0].csamt }}</td>
</tr>
<tr>
<td>&nbsp; (2) Import of services</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[1].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[1].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[1].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[1].csamt }}</td>
</tr>
<tr>
<td>&nbsp; (3) Inward supplies liable to reverse charge (other than 1 & 2 above)</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[2].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[2].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[2].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[2].csamt }}</td>
</tr>
<tr>
<td>&nbsp; (4) Inward supplies from ISD</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[3].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[3].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[3].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[3].csamt }}</td>
</tr>
<tr>
<td>&nbsp; (5) All other ITC</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[4].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[4].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[4].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_avl[4].csamt }}</td>
</tr>
<tr>
<td>
<b>(B) ITC Reversed</b>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp; (1) As per rules 42 & 43 of CGST Rules</td>
<td class="right">{{ jsonData.itc_elg.itc_rev[0].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_rev[0].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_rev[0].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_rev[0].csamt }}</td>
</tr>
<tr>
<td>&nbsp; (2) Others</td>
<td class="right">{{ jsonData.itc_elg.itc_rev[1].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_rev[1].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_rev[1].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_rev[1].csamt }}</td>
</tr>
<tr>
<td>
<b>(C) Net ITC Available(A) - (B)</b>
</td>
<td class="right">{{ jsonData.itc_elg.itc_net.iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_net.camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_net.samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_net.csamt }}</td>
</tr>
<tr>
<td>
<b>(D) Ineligible ITC</b>
</td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>&nbsp; (1) As per section 17(5)</td>
<td class="right">{{ jsonData.itc_elg.itc_inelg[0].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_inelg[0].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_inelg[0].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_inelg[0].csamt }}</td>
</tr>
<tr>
<td>&nbsp; (2) Others</td>
<td class="right">{{ jsonData.itc_elg.itc_inelg[1].iamt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_inelg[1].camt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_inelg[1].samt }}</td>
<td class="right">{{ jsonData.itc_elg.itc_inelg[1].csamt }}</td>
</tr>
</tbody>
</table>
<h5>5. &nbsp;&nbsp; Values of exempt, nil rated and non-GST inward supplies</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>Nature of Supplies</th>
<th>Inter-State Supplies</th>
<th>Intra-State Supplies</th>
</tr>
</thead>
<tbody>
<tr>
<td>From a supplier under composition scheme, Exempt and Nil rated</td>
<td class="right">{{ jsonData.inward_sup.isup_details[0].inter }}</td>
<td class="right">{{ jsonData.inward_sup.isup_details[0].intra }}</td>
</tr>
<tr>
<td>Non GST Inward Supplies</td>
<td class="right">{{ jsonData.inward_sup.isup_details[1].inter }}</td>
<td class="right">{{ jsonData.inward_sup.isup_details[1].intra }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'GSTR3BPrintView',
props: ['doc'],
computed: {
jsonData() {
return JSON.parse(this.doc.jsonData);
}
}
};
</script>
<style>
.print-format {
font-size: 8pt !important;
}
.disabled {
background-color: #d9d9d9;
}
.right {
text-align: right;
}
</style>

View File

@ -0,0 +1,19 @@
const GSTR3B = require('./GSTR3BDocument');
module.exports = class GSTR3BServer extends GSTR3B {
async validate() {
if (this.month.length === 0 || this.year.length != 4) {
frappe.call({
method: 'show_dialog',
args: {
title: 'Invalid Entry',
message: `Month or Year is not valid`
}
});
throw new Error();
}
}
async beforeInsert() {
this.name = `${this.doctype} Report ${this.month} ${this.year}`;
}
};

View File

@ -1,60 +1,89 @@
<template>
<component :themeColor="color" :font="fontFamily" :is="invoiceTemplate" v-if="doc" :doc="doc"/>
<div>
<div class="row no-gutters">
<div v-if="showInvoiceCustomizer" class="col-3 mt-4 mx-auto"></div>
<div class="col-8 mx-auto text-right mt-4">
<f-button primary @click="$emit('send', $refs.printComponent.innerHTML)">{{ _('Send') }}</f-button>
<f-button secondary @click="toggleCustomizer">{{ _('Customize') }}</f-button>
<f-button secondary @click="$emit('makePDF', $refs.printComponent.innerHTML)">{{ _('PDF') }}</f-button>
</div>
</div>
<div class="row no-gutters">
<div v-if="showInvoiceCustomizer" class="col-3 mt-4 mx-auto">
<invoice-customizer
class="border"
style="position: fixed"
@closeInvoiceCustomizer="toggleCustomizer"
@changeColor="changeColor($event)"
@changeTemplate="changeTemplate($event)"
@changeFont="changeFont($event)"
/>
</div>
<div class="col-8 bg-white mt-4 mx-auto border shadow" ref="printComponent">
<component :themeColor="themeColor" :font="font" :is="template" v-if="doc" :doc="doc" />
</div>
</div>
</div>
</template>
<script>
import InvoiceTemplate1 from '@/../models/doctype/Invoice/Templates/InvoiceTemplate1';
import InvoiceTemplate2 from '@/../models/doctype/Invoice/Templates/InvoiceTemplate2';
import InvoiceTemplate3 from '@/../models/doctype/Invoice/Templates/InvoiceTemplate3';
import InvoiceCustomizer from '@/components/InvoiceCustomizer';
const invoiceTemplates = {
'Basic I': InvoiceTemplate1,
'Basic II': InvoiceTemplate2,
'Modern': InvoiceTemplate3
Modern: InvoiceTemplate3
};
export default {
name: 'InvoicePrint',
props: ['doc', 'themeColor', 'template', 'font'],
props: ['doc'],
components: {
InvoiceCustomizer
},
data() {
return {
color: undefined,
fontFamily: undefined,
invoiceTemplate: undefined
showInvoiceCustomizer: false,
themeColor: undefined,
template: undefined,
font: undefined
};
},
watch: {
themeColor: async function() {
await this.loadInvoice();
},
font: async function() {
await this.loadInvoice();
},
template: async function() {
await this.loadInvoice();
}
},
async created() {
await this.loadInvoice();
},
methods: {
async loadInvoice() {
this.color = this.themeColor !== undefined ? this.themeColor : await this.getColor();
this.fontFamily = this.font !== undefined ? this.font : await this.getFont();
let template = this.template !== undefined ? this.template : await this.getTemplate();
let templateFile = invoiceTemplates[template];
this.invoiceTemplate = templateFile;
await this.getTemplate();
await this.getColor();
await this.getFont();
},
async getTemplate() {
let invoiceSettings = await frappe.getDoc('InvoiceSettings');
return invoiceSettings.template;
this.template = invoiceTemplates[invoiceSettings.template];
},
async getColor() {
let invoiceSettings = await frappe.getDoc('InvoiceSettings');
return invoiceSettings.themeColor;
this.themeColor = invoiceSettings.themeColor;
},
async getFont() {
let invoiceSettings = await frappe.getDoc('InvoiceSettings');
return invoiceSettings.font;
this.font = invoiceSettings.font;
},
async toggleCustomizer() {
await this.loadInvoice();
this.showInvoiceCustomizer = !this.showInvoiceCustomizer;
},
async changeColor(color) {
this.themeColor = color;
},
async changeTemplate(template) {
this.template = invoiceTemplates[template];
},
async changeFont(font) {
this.font = font;
}
}
};

View File

@ -12,34 +12,54 @@ module.exports = class PaymentServer extends BaseDocument {
return entries;
}
async afterSubmit() {
for (let row of this.for) {
if (['Invoice', 'Bill'].includes(row.referenceType)) {
let { outstandingAmount, grandTotal } = await frappe.getDoc(
row.referenceType,
row.referenceName
);
if (outstandingAmount === null) {
outstandingAmount = grandTotal;
console.log('Outstanding null');
}
if (this.amount > outstandingAmount) {
console.log('Over Payment');
} else {
console.log('Payment Done');
await frappe.db.setValue(
async beforeSubmit() {
if (this.for.length > 0)
for (let row of this.for) {
if (['Invoice', 'Bill'].includes(row.referenceType)) {
let { outstandingAmount, grandTotal } = await frappe.getDoc(
row.referenceType,
row.referenceName,
'outstandingAmount',
outstandingAmount - this.amount
row.referenceName
);
const entries = await this.getPosting();
await entries.post();
if (outstandingAmount === null) {
outstandingAmount = grandTotal;
}
if (this.amount > outstandingAmount) {
frappe.call({
method: 'show_dialog',
args: {
title: 'Invalid Payment Entry',
message: `Payment amount is greater than Outstanding amount by \
${this.amount - outstandingAmount}`
}
});
throw new Error();
} else {
await frappe.db.setValue(
row.referenceType,
row.referenceName,
'outstandingAmount',
outstandingAmount - this.amount
);
}
}
}
else {
frappe.call({
method: 'show_dialog',
args: {
title: 'Invalid Payment Entry',
message: `No reference for the payment.`
}
});
throw new Error();
}
}
async afterSubmit() {
const entries = await this.getPosting();
await entries.post();
}
async afterRevert() {
const entries = await this.getPosting();
await entries.postReverse();

View File

@ -1,62 +1,64 @@
module.exports = {
models: {
SetupWizard: require('./doctype/SetupWizard/SetupWizard'),
Account: require('./doctype/Account/Account.js'),
AccountingSettings: require('./doctype/AccountingSettings/AccountingSettings'),
CompanySettings: require('./doctype/CompanySettings/CompanySettings'),
AccountingLedgerEntry: require('./doctype/AccountingLedgerEntry/AccountingLedgerEntry.js'),
Party: require('./doctype/Party/Party.js'),
models: {
SetupWizard: require('./doctype/SetupWizard/SetupWizard'),
Account: require('./doctype/Account/Account.js'),
AccountingSettings: require('./doctype/AccountingSettings/AccountingSettings'),
CompanySettings: require('./doctype/CompanySettings/CompanySettings'),
AccountingLedgerEntry: require('./doctype/AccountingLedgerEntry/AccountingLedgerEntry.js'),
Party: require('./doctype/Party/Party.js'),
Payment: require('./doctype/Payment/Payment.js'),
PaymentFor: require('./doctype/PaymentFor/PaymentFor.js'),
PaymentSettings: require('./doctype/PaymentSettings/PaymentSettings.js'),
Payment: require('./doctype/Payment/Payment.js'),
PaymentFor: require('./doctype/PaymentFor/PaymentFor.js'),
PaymentSettings: require('./doctype/PaymentSettings/PaymentSettings.js'),
Item: require('./doctype/Item/Item.js'),
Item: require('./doctype/Item/Item.js'),
Invoice: require('./doctype/Invoice/Invoice.js'),
InvoiceItem: require('./doctype/InvoiceItem/InvoiceItem.js'),
InvoiceSettings: require('./doctype/InvoiceSettings/InvoiceSettings.js'),
Invoice: require('./doctype/Invoice/Invoice.js'),
InvoiceItem: require('./doctype/InvoiceItem/InvoiceItem.js'),
InvoiceSettings: require('./doctype/InvoiceSettings/InvoiceSettings.js'),
Bill: require('./doctype/Bill/Bill.js'),
BillItem: require('./doctype/BillItem/BillItem.js'),
BillSettings: require('./doctype/BillSettings/BillSettings.js'),
Bill: require('./doctype/Bill/Bill.js'),
BillItem: require('./doctype/BillItem/BillItem.js'),
BillSettings: require('./doctype/BillSettings/BillSettings.js'),
Tax: require('./doctype/Tax/Tax.js'),
TaxDetail: require('./doctype/TaxDetail/TaxDetail.js'),
TaxSummary: require('./doctype/TaxSummary/TaxSummary.js'),
Tax: require('./doctype/Tax/Tax.js'),
TaxDetail: require('./doctype/TaxDetail/TaxDetail.js'),
TaxSummary: require('./doctype/TaxSummary/TaxSummary.js'),
Address: require('./doctype/Address/Address.js'),
Contact: require('./doctype/Contact/Contact.js'),
GSTR3B: require('./doctype/GSTR3B/GSTR3B.js'),
JournalEntry: require('./doctype/JournalEntry/JournalEntry.js'),
JournalEntryAccount: require('./doctype/JournalEntryAccount/JournalEntryAccount.js'),
JournalEntrySettings: require('./doctype/JournalEntrySettings/JournalEntrySettings.js'),
Address: require('./doctype/Address/Address.js'),
Contact: require('./doctype/Contact/Contact.js'),
Quotation: require('./doctype/Quotation/Quotation.js'),
QuotationItem: require('./doctype/QuotationItem/QuotationItem.js'),
QuotationSettings: require('./doctype/QuotationSettings/QuotationSettings.js'),
JournalEntry: require('./doctype/JournalEntry/JournalEntry.js'),
JournalEntryAccount: require('./doctype/JournalEntryAccount/JournalEntryAccount.js'),
JournalEntrySettings: require('./doctype/JournalEntrySettings/JournalEntrySettings.js'),
SalesOrder: require('./doctype/SalesOrder/SalesOrder.js'),
SalesOrderItem: require('./doctype/SalesOrderItem/SalesOrderItem.js'),
SalesOrderSettings: require('./doctype/SalesOrderSettings/SalesOrderSettings.js'),
Quotation: require('./doctype/Quotation/Quotation.js'),
QuotationItem: require('./doctype/QuotationItem/QuotationItem.js'),
QuotationSettings: require('./doctype/QuotationSettings/QuotationSettings.js'),
Fulfillment: require('./doctype/Fulfillment/Fulfillment.js'),
FulfillmentItem: require('./doctype/FulfillmentItem/FulfillmentItem.js'),
FulfillmentSettings: require('./doctype/FulfillmentSettings/FulfillmentSettings.js'),
SalesOrder: require('./doctype/SalesOrder/SalesOrder.js'),
SalesOrderItem: require('./doctype/SalesOrderItem/SalesOrderItem.js'),
SalesOrderSettings: require('./doctype/SalesOrderSettings/SalesOrderSettings.js'),
PurchaseOrder: require('./doctype/PurchaseOrder/PurchaseOrder.js'),
PurchaseOrderItem: require('./doctype/PurchaseOrderItem/PurchaseOrderItem.js'),
PurchaseOrderSettings: require('./doctype/PurchaseOrderSettings/PurchaseOrderSettings.js'),
Fulfillment: require('./doctype/Fulfillment/Fulfillment.js'),
FulfillmentItem: require('./doctype/FulfillmentItem/FulfillmentItem.js'),
FulfillmentSettings: require('./doctype/FulfillmentSettings/FulfillmentSettings.js'),
PurchaseReceipt: require('./doctype/PurchaseReceipt/PurchaseReceipt.js'),
PurchaseReceiptItem: require('./doctype/PurchaseReceiptItem/PurchaseReceiptItem.js'),
PurchaseReceiptSettings: require('./doctype/PurchaseReceiptSettings/PurchaseReceiptSettings.js'),
PurchaseOrder: require('./doctype/PurchaseOrder/PurchaseOrder.js'),
PurchaseOrderItem: require('./doctype/PurchaseOrderItem/PurchaseOrderItem.js'),
PurchaseOrderSettings: require('./doctype/PurchaseOrderSettings/PurchaseOrderSettings.js'),
Event: require('./doctype/Event/Event'),
EventSchedule: require('./doctype/EventSchedule/EventSchedule'),
EventSettings: require('./doctype/EventSettings/EventSettings'),
PurchaseReceipt: require('./doctype/PurchaseReceipt/PurchaseReceipt.js'),
PurchaseReceiptItem: require('./doctype/PurchaseReceiptItem/PurchaseReceiptItem.js'),
PurchaseReceiptSettings: require('./doctype/PurchaseReceiptSettings/PurchaseReceiptSettings.js'),
Email: require('./doctype/Email/Email'),
EmailAccount: require('./doctype/EmailAccount/EmailAccount'),
}
}
Event: require('./doctype/Event/Event'),
EventSchedule: require('./doctype/EventSchedule/EventSchedule'),
EventSettings: require('./doctype/EventSettings/EventSettings'),
Email: require('./doctype/Email/Email'),
EmailAccount: require('./doctype/EmailAccount/EmailAccount')
}
};

View File

@ -0,0 +1,68 @@
const frappe = require('frappejs');
class BaseGSTR {
async getCompleteReport(gstrType, filters) {
if (['GSTR-1', 'GSTR-2'].includes(gstrType)) {
let entries = await frappe.db.getAll({
doctype: gstrType === 'GSTR-1' ? 'Invoice' : 'Bill',
filters
});
let tableData = [];
for (let entry of entries) {
entry.doctype = gstrType === 'GSTR-1' ? 'Invoice' : 'Bill';
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;
});
}
return tableData;
} else {
return [];
}
}
async getRow(ledgerEntry) {
let row = {};
ledgerEntry = await frappe.getDoc(ledgerEntry.doctype, ledgerEntry.name);
let party = await frappe.getDoc(
'Party',
ledgerEntry.customer || ledgerEntry.supplier
);
if (party.address) {
let addressDetails = await frappe.getDoc('Address', party.address);
row.place = addressDetails.state || '';
}
row.gstin = party.gstin;
row.partyName = ledgerEntry.customer || ledgerEntry.supplier;
row.invNo = ledgerEntry.name;
row.invDate = ledgerEntry.date;
row.rate = 0;
row.inState = true;
row.reverseCharge = !party.gstin ? 'Y' : 'N';
ledgerEntry.taxes.forEach(tax => {
row.rate += tax.rate;
const taxAmt = (tax.rate * ledgerEntry.netTotal) / 100;
if (tax.account === 'IGST') row.igstAmt = taxAmt;
if (tax.account === 'IGST') row.inState = false;
if (tax.account === 'CGST') row.cgstAmt = taxAmt;
if (tax.account === 'SGST') row.sgstAmt = taxAmt;
if (tax.account === 'Nil Rated') row.nilRated = true;
if (tax.account === 'Exempt') row.exempt = true;
if (tax.account === 'Non GST') row.nonGST = true;
});
row.invAmt = ledgerEntry.grandTotal;
row.taxVal = ledgerEntry.netTotal;
return row;
}
}
module.exports = BaseGSTR;

View File

@ -1,14 +1,5 @@
const title = 'Goods and Service Tax';
module.exports = {
title: title,
method: 'gst-taxes',
filterFields: [
{
fieldtype: 'Select',
label: 'Report Type',
fieldname: 'reportType',
options: ['', 'GSTR-1', 'GSTR-2', 'GSTR-3B']
},
{
fieldtype: 'Select',
label: 'Transfer Type',
@ -35,6 +26,13 @@ module.exports = {
fieldtype: 'Date',
label: 'To Date',
fieldname: 'toDate'
},
{
fieldtype: 'Select',
label: 'Report Type',
fieldname: 'reportType',
options: ['', 'GSTR-1', 'GSTR-2', 'GSTR-3B'],
hidden: 1
}
],
linkFields: [

View File

@ -0,0 +1,26 @@
const BaseGSTR = require('./BaseGSTR');
class GSTR1 extends BaseGSTR {
async run(params) {
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-1', filters);
const conditions = {
B2B: row => row.gstin,
'B2C-Large': row => !row.gstin && !row.inState && row.invAmt >= 250000,
'B2C-Small': row =>
!row.gstin && (row.inState || (row.inState && row.invAmt < 250000))
};
if (!params.transferType) return data;
return data.filter(row => conditions[params.transferType](row));
}
}
module.exports = GSTR1;

View File

@ -0,0 +1,9 @@
const title = 'GSTR 1';
const baseConfig = require('./BaseViewConfig');
module.exports = {
title: title,
method: 'gstr-1',
filterFields: baseConfig.filterFields,
linkFields: baseConfig.linkFields,
getColumns: baseConfig.getColumns
};

View File

@ -0,0 +1,26 @@
const BaseGSTR = require('./BaseGSTR');
class GSTR2 extends BaseGSTR {
async run(params) {
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);
const conditions = {
B2B: row => row.gstin,
'B2C-Large': row => !row.gstin && !row.inState && row.invAmt >= 250000,
'B2C-Small': row =>
!row.gstin && (row.inState || (row.inState && row.invAmt < 250000))
};
if (!params.transferType) return data;
return data.filter(row => conditions[params.transferType](row));
}
}
module.exports = GSTR2;

View File

@ -0,0 +1,9 @@
const title = 'GSTR 2';
const baseConfig = require('./BaseViewConfig');
module.exports = {
title: title,
method: 'gstr-2',
filterFields: baseConfig.filterFields,
linkFields: baseConfig.linkFields,
getColumns: baseConfig.getColumns
};

View File

@ -1,150 +0,0 @@
const frappe = require('frappejs');
class GoodsAndServiceTax {
async run(params) {
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);
}
if (params.reportType === 'GSTR-3B') {
this.getGSTR3bJson(filters);
return [];
}
const data = await this.getCompleteReport(params.reportType, filters);
const conditions = {
B2B: row => row.gstin,
'B2C-Large': row => !row.gstin && !row.inState && row.invAmt >= 250000,
'B2C-Small': row =>
!row.gstin && (row.inState || (row.inState && row.invAmt < 250000))
};
if (!params.transferType) return data;
return data.filter(row => conditions[params.transferType](row));
}
async getCompleteReport(gstrType, filters) {
if (['GSTR-1', 'GSTR-2'].includes(gstrType)) {
let entries = await frappe.db.getAll({
doctype: gstrType === 'GSTR-1' ? 'Invoice' : 'Bill',
filter: filters
});
let tableData = [];
for (let entry of entries) {
const row = await this.getRow({
doctype: gstrType === 'GSTR-1' ? 'Invoice' : 'Bill',
name: entry.name
});
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;
});
}
return tableData;
} else {
return [];
}
}
async getGSTR3bJson(filters) {
const gstr1Data = this.getCompleteReport('GSTR-1', filters);
const gsrt2Data = this.getCompleteReport('GSTR-2', filters);
const gstr3bData = await Promise.all([gstr1Data, gsrt2Data]);
let gstr3bJson = require('./gstr3b.json');
for (let entry of gstr3bData[0]) {
if (entry.rate > 0) {
gstr3bJson['sup_details']['osup_det']['samt'] += entry.sgstAmt || 0;
gstr3bJson['sup_details']['osup_det']['camt'] += entry.cgstAmt || 0;
gstr3bJson['sup_details']['osup_det']['iamt'] += entry.igstAmt || 0;
gstr3bJson['sup_details']['osup_det']['txval'] += entry.taxVal;
}
if (entry.rate === 0) {
gstr3bJson['sup_details']['osup_zero']['txval'] += entry.taxVal;
}
if (entry.nilRated || entry.exempt) {
gstr3bJson['sup_details']['osup_nil_exmp']['txval'] += entry.taxVal;
}
if (entry.nonGST) {
gstr3bJson['sup_details']['osup_nongst']['txval'] += entry.taxVal;
}
if (!entry.inState && !entry.gstin) {
gstr3bJson['inter_sup']['unreg_details'].push = {
place: entry.place,
taxVal: entry.taxVal,
igstAmt: entry.igstAmt
};
}
}
for (let entry of gstr3bData[1]) {
if (entry.reverseCharge === 'Y') {
gstr3bJson['sup_details']['isup_rev']['samt'] += entry.sgstAmt || 0;
gstr3bJson['sup_details']['isup_rev']['camt'] += entry.cgstAmt || 0;
gstr3bJson['sup_details']['isup_rev']['iamt'] += entry.igstAmt || 0;
gstr3bJson['sup_details']['isup_rev']['txval'] += entry.taxVal;
}
if (entry.nilRated || entry.exempt) {
gstr3bJson['inward_sup']['isup_details'][0][
entry.inState ? 'intra' : 'inter'
] += entry.taxVal;
}
if (entry.nonGST) {
gstr3bJson['inward_sup']['isup_details'][0][
entry.inState ? 'intra' : 'inter'
] += entry.taxVal;
}
}
return gstr3bJson;
}
async getRow(entry) {
let row = {};
let entryDetails = await frappe.getDoc(entry.doctype, entry.name);
let party = await frappe.getDoc(
'Party',
entryDetails.customer || entryDetails.supplier
);
if (party.address) {
let addressDetails = await frappe.getDoc('Address', party.address);
row.place = addressDetails.state || '';
}
row.gstin = party.gstin;
row.partyName = entryDetails.customer || entryDetails.supplier;
row.invNo = entryDetails.name;
row.invDate = entryDetails.date;
row.rate = 0;
row.inState = true;
row.reverseCharge = !party.gstin ? 'Y' : 'N';
entryDetails.taxes.forEach(tax => {
row.rate += tax.rate;
const taxAmt = (tax.rate * entryDetails.netTotal) / 100;
if (tax.account === 'IGST') row.igstAmt = taxAmt;
if (tax.account === 'IGST') row.inState = false;
if (tax.account === 'CGST') row.cgstAmt = taxAmt;
if (tax.account === 'SGST') row.sgstAmt = taxAmt;
if (tax.account === 'Nil Rated') row.nilRated = true;
if (tax.account === 'Exempt') row.exempt = true;
if (tax.account === 'Non GST') row.nonGST = true;
});
row.invAmt = entryDetails.grandTotal;
row.taxVal = entryDetails.netTotal;
return row;
}
}
module.exports = GoodsAndServiceTax;

View File

@ -1,91 +0,0 @@
const ReportPage = require('frappejs/client/desk/reportpage');
const frappe = require('frappejs');
module.exports = class GoodsAndServiceTaxView extends ReportPage {
constructor() {
super({
title: frappe._('Goods and Service Tax'),
filterFields: [
{
fieldtype: 'Data',
label: 'Transfer Type',
options: ['In State', 'Out of State']
},
{
fieldtype: 'Data',
label: 'Place'
},
{
fieldtype: 'Date',
label: 'From Date'
},
{
fieldtype: 'Date',
label: 'To Date'
}
]
});
this.method = 'gst-taxes';
}
getColumns() {
return [
{
label: 'GSTIN No.',
fieldname: 'gstin',
fieldtype: 'Data'
},
{
fieldtype: 'Data',
fieldname: 'cusName',
label: 'Customer Name'
},
{
label: 'Invoice No.',
fieldname: 'invNo',
fieldtype: 'Data'
},
{
label: 'Invoice Value',
fieldname: 'invAmt',
fieldtype: 'Currency'
},
{
label: 'Invoice Date',
fieldname: 'invDate',
fieldtype: 'Date'
},
{
label: 'Place of supply',
fieldname: 'place',
fieldtype: 'Data'
},
{
label: 'Rate',
fieldname: 'rate',
fieldtype: 'Data'
},
{
label: 'Taxable Amount',
fieldname: 'taxAmt',
fieldtype: 'Currency'
},
{
label: 'Intergrated Tax',
fieldname: 'igstAmt',
fieldtype: 'Currency'
},
{
label: 'Central Tax',
fieldname: 'cgstAmt',
fieldtype: 'Currency'
},
{
label: 'State Tax',
fieldname: 'sgstAmt',
fieldtype: 'Currency'
}
];
}
};

View File

@ -1,127 +0,0 @@
{
"gstin": "",
"ret_period": "",
"inward_sup": {
"isup_details": [
{
"ty": "GST",
"intra": 0,
"inter": 0
},
{
"ty": "NONGST",
"inter": 0,
"intra": 0
}
]
},
"sup_details": {
"osup_zero": {
"csamt": 0,
"txval": 0,
"iamt": 0
},
"osup_nil_exmp": {
"txval": 0
},
"osup_det": {
"samt": 0,
"csamt": 0,
"txval": 0,
"camt": 0,
"iamt": 0
},
"isup_rev": {
"samt": 0,
"csamt": 0,
"txval": 0,
"camt": 0,
"iamt": 0
},
"osup_nongst": {
"txval": 0
}
},
"inter_sup": {
"unreg_details": [],
"comp_details": [],
"uin_details": []
},
"itc_elg": {
"itc_avl": [
{
"csamt": 0,
"samt": 0,
"ty": "IMPG",
"camt": 0,
"iamt": 0
},
{
"csamt": 0,
"samt": 0,
"ty": "IMPS",
"camt": 0,
"iamt": 0
},
{
"samt": 0,
"csamt": 0,
"ty": "ISRC",
"camt": 0,
"iamt": 0
},
{
"ty": "ISD",
"iamt": 0,
"camt": 0,
"samt": 0,
"csamt": 0
},
{
"samt": 0,
"csamt": 0,
"ty": "OTH",
"camt": 0,
"iamt": 0
}
],
"itc_rev": [
{
"ty": "RUL",
"iamt": 0,
"camt": 0,
"samt": 0,
"csamt": 0
},
{
"ty": "OTH",
"iamt": 0,
"camt": 0,
"samt": 0,
"csamt": 0
}
],
"itc_net": {
"samt": 0,
"csamt": 0,
"camt": 0,
"iamt": 0
},
"itc_inelg": [
{
"ty": "RUL",
"iamt": 0,
"camt": 0,
"samt": 0,
"csamt": 0
},
{
"ty": "OTH",
"iamt": 0,
"camt": 0,
"samt": 0,
"csamt": 0
}
]
}
}

View File

@ -6,65 +6,71 @@ const TrialBalance = require('./TrialBalance/TrialBalance');
const SalesRegister = require('./SalesRegister/SalesRegister');
const PurchaseRegister = require('./PurchaseRegister/PurchaseRegister');
const BankReconciliation = require('./BankReconciliation/BankReconciliation');
const GoodsAndServiceTax = require('./GoodsAndServiceTax/GoodsAndServiceTax');
const GSTR1 = require('./GoodsAndServiceTax/GSTR1');
const GSTR2 = require('./GoodsAndServiceTax/GSTR2');
const AccountsReceivablePayable = require('./AccountsReceivablePayable/AccountsReceivablePayable');
// called on server side
function registerReportMethods() {
const reports = [{
method: 'general-ledger',
class: GeneralLedger
},
{
method: 'profit-and-loss',
class: ProfitAndLoss
},
{
method: 'balance-sheet',
class: BalanceSheet
},
{
method: 'trial-balance',
class: TrialBalance
},
{
method: 'sales-register',
class: SalesRegister
},
{
method: 'purchase-register',
class: PurchaseRegister
},
{
method: 'bank-reconciliation',
class: BankReconciliation
},
{
method: 'gst-taxes',
class: GoodsAndServiceTax
},
];
reports.forEach(report => {
frappe.registerMethod({
method: report.method,
handler: getReportData(report.class)
});
});
const reports = [
{
method: 'general-ledger',
class: GeneralLedger
},
{
method: 'profit-and-loss',
class: ProfitAndLoss
},
{
method: 'balance-sheet',
class: BalanceSheet
},
{
method: 'trial-balance',
class: TrialBalance
},
{
method: 'sales-register',
class: SalesRegister
},
{
method: 'purchase-register',
class: PurchaseRegister
},
{
method: 'bank-reconciliation',
class: BankReconciliation
},
{
method: 'gstr-1',
class: GSTR1
},
{
method: 'gstr-2',
class: GSTR2
}
];
reports.forEach(report => {
frappe.registerMethod({
method: 'accounts-receivable',
handler: args => new AccountsReceivablePayable().run('Receivable', args)
method: report.method,
handler: getReportData(report.class)
});
});
frappe.registerMethod({
method: 'accounts-payable',
handler: args => new AccountsReceivablePayable().run('Payable', args)
});
frappe.registerMethod({
method: 'accounts-receivable',
handler: args => new AccountsReceivablePayable().run('Receivable', args)
});
frappe.registerMethod({
method: 'accounts-payable',
handler: args => new AccountsReceivablePayable().run('Payable', args)
});
}
function getReportData(ReportClass) {
return args => new ReportClass().run(args);
return args => new ReportClass().run(args);
}
module.exports = registerReportMethods
module.exports = registerReportMethods;

View File

@ -1,8 +1,9 @@
module.exports = {
'general-ledger': require('./GeneralLedger/viewConfig'),
'sales-register': require('./SalesRegister/viewConfig'),
'profit-and-loss': require('./ProfitAndLoss/viewConfig'),
'trial-balance': require('./TrialBalance/viewConfig'),
'bank-reconciliation': require('./BankReconciliation/viewConfig'),
'gst-taxes': require('./GoodsAndServiceTax/viewConfig'),
}
'general-ledger': require('./GeneralLedger/viewConfig'),
'sales-register': require('./SalesRegister/viewConfig'),
'profit-and-loss': require('./ProfitAndLoss/viewConfig'),
'trial-balance': require('./TrialBalance/viewConfig'),
'bank-reconciliation': require('./BankReconciliation/viewConfig'),
'gstr-1': require('./GoodsAndServiceTax/GSTR1View'),
'gstr-2': require('./GoodsAndServiceTax/GSTR2View')
};

View File

@ -8,6 +8,7 @@ module.exports = async function postStart() {
frappe.models.Payment.documentClass = require('../models/doctype/Payment/PaymentServer.js');
frappe.models.Bill.documentClass = require('../models/doctype/Bill/BillServer.js');
frappe.models.JournalEntry.documentClass = require('../models/doctype/JournalEntry/JournalEntryServer.js');
frappe.models.GSTR3B.documentClass = require('../models/doctype/GSTR3B/GSTR3BServer.js');
frappe.metaCache = {};
@ -25,4 +26,4 @@ module.exports = async function postStart() {
await naming.createNumberSeries('PREC-', 'PurchaseReceiptSettings');
registerServerMethods();
}
};

View File

@ -9,7 +9,7 @@ module.exports = function registerServerMethods() {
method: 'send-mail',
handler: sender.sendMail
});
frappe.registerMethod({
method: 'import-coa',
async handler() {
@ -20,14 +20,24 @@ module.exports = function registerServerMethods() {
frappe.registerMethod({
method: 'print-pdf',
handler({doctype, name, html}) {
handler({ doctype, name, html }) {
if (frappe.isElectron) {
const path = require('path');
const { getPDFForElectron } = require('frappejs/server/pdf');
const { getSettings } = require('../electron/settings');
const destination = path.resolve('.')
const destination = path.resolve('.');
getPDFForElectron(doctype, name, destination, html);
}
}
})
}
});
frappe.registerMethod({
method: 'show_dialog',
handler({ title, message }) {
frappe.showModal({
modalProps: { title, noFooter: true },
component: require('../src/components/MessageDialog').default,
props: { message }
});
}
});
};

View File

@ -1,25 +1,21 @@
<template>
<div>
<div v-if="doc" class="p-4">
<div class="row">
<div class="col-6 text-center">
<h4>Customizer</h4>
</div>
<div class="col-6 text-right">
<f-button secondary @click="$emit('closeInvoiceCustomizer')">{{ _('Close') }}</f-button>
</div>
</div>
<div class="row">
<div class="col-12 mt-4">
<form-layout
:doc="doc"
:fields="fields"
@updateDoc="saveDoc"
/>
<sketch-picker v-model="color"/>
</div>
</div>
</div>
<div v-if="doc" class="p-4">
<div class="row">
<div class="col-6 text-center">
<h4>Customizer</h4>
</div>
<div class="col-6 text-right">
<f-button secondary @click="saveAndClose">{{ _('Save & Close') }}</f-button>
</div>
</div>
<div class="row">
<div class="col-12 mt-4">
<form-layout :doc="doc" :fields="fields" @updateDoc="saveDoc" />
<sketch-picker v-model="color" />
</div>
</div>
</div>
</div>
</template>
<script>
@ -27,45 +23,49 @@ import FormLayout from 'frappejs/ui/components/Form/FormLayout';
import { Sketch } from 'vue-color';
export default {
name: 'InvoiceCustomizer',
components: {
FormLayout,
'sketch-picker': Sketch
},
data() {
return {
doc: null,
fields: [],
color: null,
}
},
async created() {
this.doc = await frappe.getDoc('InvoiceSettings');
this.color = this.doc.themeColor;
const meta = frappe.getMeta('InvoiceSettings');
this.fields = meta.fields.filter((field) => field.fieldname !== "numberSeries");
},
methods: {
async saveDoc(updatedValue) {
let { fieldname, value } = updatedValue;
if (fieldname === 'template') {
this.$emit('changeTemplate', value);
} else if (fieldname === 'font') {
this.$emit('changeFont', value);
}
await this.doc.update();
}
},
watch: {
color: async function() {
if (this.doc) {
if (this.doc.themeColor != this.color.hex) {
this.doc.themeColor = this.color.hex;
this.$emit('changeColor', this.color.hex);
await this.doc.update();
}
}
}
}
name: 'InvoiceCustomizer',
components: {
FormLayout,
'sketch-picker': Sketch
},
data() {
return {
doc: null,
fields: [],
color: {}
};
},
async created() {
this.doc = await frappe.getDoc('InvoiceSettings');
this.color.hex = this.doc.themeColor;
const meta = frappe.getMeta('InvoiceSettings');
this.fields = meta.fields.filter(
field => field.fieldname !== 'numberSeries'
);
},
methods: {
async saveDoc(updatedValue) {
let { fieldname, value } = updatedValue;
if (fieldname === 'template') {
this.$emit('changeTemplate', value);
} else if (fieldname === 'font') {
this.$emit('changeFont', value);
}
},
async saveAndClose() {
this.doc.themeColor = this.color.hex;
await this.doc.update();
this.$emit('closeInvoiceCustomizer');
}
},
watch: {
color: async function() {
if (this.doc) {
if (this.doc.themeColor != this.color.hex) {
this.$emit('changeColor', this.color.hex);
}
}
}
}
};
</script>

View File

@ -0,0 +1,23 @@
<template>
<div class="p-3 d-flex align-items-center">
<indicator :color="indicatorColor" class="mr-2 ml-1" />
{{ message }}
</div>
</template>
<script>
import indicators from 'frappejs/ui/constants/indicators';
export default {
name: 'MessageDialog',
data() {
return {
indicatorColor: indicators.ORANGE
};
},
props: {
message: {
type: String
}
}
};
</script>

View File

@ -5,7 +5,7 @@
<a v-for="(item, index) in clickableBreadcrumbs" :key="index" :href="item.route">
<h5 class="breadCrumbRoute">
{{ item.title }}
<feather-icon name="arrow-right" style="color: #212529 !important;"></feather-icon>
<feather-icon name="chevron-right" style="color: #212529 !important;"></feather-icon>
</h5>
</a>
<h5 class="breadCrumbRoute">{{ lastBreadcrumb.title }}</h5>

View File

@ -68,7 +68,7 @@ export default {
methods: {
isCurrentRoute(route) {
if (this.activeGroup) return false;
return this.$route.path === route;
return this.$route.fullPath === route;
},
toggleGroup(groupTitle) {
this.groupItems =

View File

@ -124,18 +124,28 @@ export default {
this.$emit('save', this.doc);
} catch (e) {
console.error(e);
return;
throw e;
}
},
async submit() {
this.doc.set('submitted', 1);
await this.save();
try {
await this.save();
} catch (e) {
this.doc.set('submitted', 0);
this.doc.set('_dirty', false);
}
},
async revert() {
this.doc.set('submitted', 0);
await this.save();
try {
await this.save();
} catch (e) {
this.doc.set('submitted', 1);
this.doc._dirty = false;
}
},
print() {

View File

@ -7,6 +7,7 @@ import Payment from '../../../models/doctype/Payment/PaymentList';
import Tax from '../../../models/doctype/Tax/TaxList';
import JournalEntry from '../../../models/doctype/JournalEntry/JournalEntryList';
import Account from '../../../models/doctype/Account/AccountList';
import GSTR3B from '../../../models/doctype/GSTR3B/GSTR3BList';
export default {
Invoice,
@ -17,5 +18,6 @@ export default {
Payment,
Tax,
JournalEntry,
Account
Account,
GSTR3B
};

View File

@ -1,61 +1,50 @@
<template>
<div class="bg-light">
<page-header :title="doctype"/>
<div class="row no-gutters">
<div v-if="showInvoiceCustomizer" class="col-3 mt-4 mx-auto"></div>
<div class="col-8 mx-auto text-right mt-4">
<f-button primary @click="send">{{ _('Send') }}</f-button>
<f-button secondary @click="toggleInvoiceCustomizer">{{ _('Customize') }}</f-button>
<f-button secondary @click="makePDF">{{ _('PDF') }}</f-button>
</div>
</div>
<div class="row no-gutters">
<div v-if="showInvoiceCustomizer" class="col-3 mt-4 mx-auto">
<invoice-customizer
class="border"
style="position: fixed"
@closeInvoiceCustomizer="toggleInvoiceCustomizer"
@changeColor="changeColor($event)"
@changeTemplate="changeTemplate($event)"
@changeFont="changeFont($event)"
/>
</div>
<div ref="printComponent" class="col-8 bg-white mt-4 mx-auto border shadow">
<component
:themeColor="themeColor"
:template="template"
:font="font"
:is="printComponent"
v-if="doc"
:doc="doc"
/>
</div>
</div>
<page-header :breadcrumbs="breadcrumbs" />
<component :is="printComponent" v-if="doc" :doc="doc" @send="send" @makePDF="makePDF" />
</div>
</template>
<script>
import PageHeader from '@/components/PageHeader';
import InvoiceCustomizer from '@/components/InvoiceCustomizer';
import InvoicePrint from '@/../models/doctype/Invoice/InvoicePrint';
import GSTR3BPrintView from '@/../models/doctype/GSTR3B/GSTR3BPrintView';
import EmailSend from '../Email/EmailSend';
const printComponents = {
Invoice: InvoicePrint
Invoice: InvoicePrint,
GSTR3B: GSTR3BPrintView
};
export default {
name: 'PrintView',
props: ['doctype', 'name'],
components: {
PageHeader,
InvoiceCustomizer
PageHeader
},
computed: {
breadcrumbs() {
if (this.doc)
return [
{
title: this.doctype,
route: '#/list/' + this.doctype
},
{
title: this.doc._notInserted
? 'New ' + this.doctype
: this.doc.name,
route: `#/edit/${this.doctype}/${this.name}`
},
{
title: 'Print',
route: ``
}
];
}
},
data() {
return {
doc: undefined,
printComponent: undefined,
themeColor: undefined,
template: undefined,
font: undefined,
showInvoiceCustomizer: false
};
},
@ -64,22 +53,22 @@ export default {
this.printComponent = printComponents[this.doctype];
},
methods: {
makePDF() {
makePDF(html) {
frappe.call({
method: 'print-pdf',
args: {
doctype: this.doctype,
name: this.name,
html: this.$refs.printComponent.innerHTML
html
}
});
},
async send() {
async send(html) {
let doc = await frappe.getNewDoc('Email');
let emailFields = frappe.getMeta('Email').fields;
var file_path = this.name;
doc['fromEmailAddress'] = this.selectedId;
this.makePDF();
this.makePDF(html);
doc['filePath'] = this.name + '.pdf';
this.$modal.show({
component: EmailSend,
@ -88,25 +77,13 @@ export default {
name: doc.name
},
modalProps: {
title: 'Send Invoice',
footerMessage: 'Invoice attached along..'
title: `Send ${this.doctype}`,
footerMessage: `${this.doctype} attached along..`
}
});
doc.on('afterInsert', data => {
this.$modal.hide();
});
},
async toggleInvoiceCustomizer() {
this.showInvoiceCustomizer = !this.showInvoiceCustomizer;
},
changeColor(color) {
this.themeColor = color;
},
changeTemplate(template) {
this.template = template;
},
changeFont(font) {
this.font = font;
}
}
};

View File

@ -2,15 +2,15 @@
<div>
<div class="px-3">
<div class="row pb-4">
<!-- <h4 class="col-6 d-flex">{{ reportConfig.title }}</h4> -->
<page-header :class="linksExists ? 'col-6':'col-12'" :breadcrumbs="breadcrumbs" />
<report-links class="col-6 d-flex pr-0 flex-row-reverse" v-if="linksExists" :links="links"></report-links>
</div>
<div class="row pb-4">
<report-filters
class="col-12 pr-0"
v-if="filtersExists"
:filters="reportConfig.filterFields"
v-if="shouldRenderFields"
:filterFields="reportConfig.filterFields"
:filterDoc="filterDoc"
:filterDefaults="filters"
@change="getReportData"
></report-filters>
@ -33,7 +33,9 @@ export default {
props: ['reportName', 'reportConfig', 'filters'],
data() {
return {
currentFilters: this.filters
currentFilters: this.filters,
filterDoc: undefined,
links: []
};
},
computed: {
@ -49,26 +51,16 @@ export default {
}
];
},
filtersExists() {
return (this.reportConfig.filterFields || []).length;
shouldRenderFields() {
return (this.reportConfig.filterFields || []).length && this.filterDoc;
},
linksExists() {
return (this.reportConfig.linkFields || []).length;
}
},
watch: {
reportName() {
//FIX: Report's data forwards to next consecutively changed report
this.getReportData(this.filters);
}
},
data() {
return {
links: []
};
},
async created() {
this.setLinks();
this.filterDoc = await frappe.newCustomDoc(this.reportConfig.filterFields);
},
methods: {
async getReportData(filters) {
@ -102,7 +94,7 @@ export default {
}
if (this.datatable) {
this.datatable.refresh(rows, columns);
if (rows.length) this.datatable.refresh(rows, columns);
} else {
this.datatable = new DataTable(this.$refs.datatable, {
columns: columns,

View File

@ -70,9 +70,6 @@ export default {
label: _('Payment'),
route: '/list/Payment'
}
// {
// label: _('AccountingLedgerEntry'), route: '/list/AccountingLedgerEntry'
// },
]
},
{
@ -95,8 +92,16 @@ export default {
route: '/report/bank-reconciliation'
},
{
label: _('Goods and Service Tax'),
route: '/report/gst-taxes'
label: _('GSTR 1'),
route: '/report/gstr-1?transferType=B2B'
},
{
label: _('GSTR 2'),
route: '/report/gstr-2?transferType=B2B'
},
{
label: _('GSTR 3B'),
route: '/list/GSTR3B'
}
]
},

View File

@ -7,7 +7,7 @@
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(-12px);
transform: translateX(-5px);
opacity: 0;
}
.slide-fade-move {