mirror of
https://github.com/frappe/books.git
synced 2024-11-14 01:14:03 +00:00
Purchase Cycle
This commit is contained in:
parent
e4c2bd3dda
commit
466597459a
@ -2,112 +2,126 @@ const frappe = require('frappejs');
|
||||
const utils = require('../../../accounting/utils');
|
||||
|
||||
module.exports = {
|
||||
"name": "Bill",
|
||||
"doctype": "DocType",
|
||||
"documentClass": require('./BillDocument'),
|
||||
"isSingle": 0,
|
||||
"isChild": 0,
|
||||
"isSubmittable": 1,
|
||||
"keywordFields": ["name", "supplier"],
|
||||
"settings": "BillSettings",
|
||||
"showTitle": true,
|
||||
"fields": [
|
||||
name: 'Bill',
|
||||
doctype: 'DocType',
|
||||
documentClass: require('./BillDocument'),
|
||||
isSingle: 0,
|
||||
isChild: 0,
|
||||
isSubmittable: 1,
|
||||
keywordFields: ['name', 'supplier'],
|
||||
settings: 'BillSettings',
|
||||
showTitle: true,
|
||||
fields: [
|
||||
{
|
||||
"fieldname": "date",
|
||||
"label": "Date",
|
||||
"fieldtype": "Date"
|
||||
fieldname: 'date',
|
||||
label: 'Date',
|
||||
fieldtype: 'Date'
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier",
|
||||
"label": "Supplier",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"required": 1,
|
||||
fieldname: 'supplier',
|
||||
label: 'Supplier',
|
||||
fieldtype: 'Link',
|
||||
target: 'Party',
|
||||
required: 1,
|
||||
getFilters: (query, control) => {
|
||||
if (!query) return { supplier: 1 };
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
keywords: ['like', query],
|
||||
supplier: 1
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
fieldname: 'account',
|
||||
label: 'Account',
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
formula: doc => doc.getFrom('Party', doc.supplier, 'defaultAccount'),
|
||||
getFilters: (query, control) => {
|
||||
if (!query) return { isGroup: 0, accountType: 'Payable' };
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
keywords: ['like', query],
|
||||
isGroup: 0,
|
||||
accountType: "Payable"
|
||||
}
|
||||
accountType: 'Payable'
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
"label": "Items",
|
||||
"fieldtype": "Table",
|
||||
"childtype": "BillItem",
|
||||
"required": true
|
||||
fieldname: 'items',
|
||||
label: 'Items',
|
||||
fieldtype: 'Table',
|
||||
childtype: 'BillItem',
|
||||
required: true
|
||||
},
|
||||
{
|
||||
"fieldname": "netTotal",
|
||||
"label": "Net Total",
|
||||
"fieldtype": "Currency",
|
||||
formula: (doc) => doc.getSum('items', 'amount'),
|
||||
"disabled": true
|
||||
fieldname: 'netTotal',
|
||||
label: 'Net Total',
|
||||
fieldtype: 'Currency',
|
||||
formula: doc => doc.getSum('items', 'amount'),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"label": "Taxes",
|
||||
"fieldtype": "Table",
|
||||
"childtype": "TaxSummary",
|
||||
"disabled": true,
|
||||
fieldname: 'taxes',
|
||||
label: 'Taxes',
|
||||
fieldtype: 'Table',
|
||||
childtype: 'TaxSummary',
|
||||
readOnly: 1,
|
||||
template: (doc, row) => {
|
||||
return `<div class='row'>
|
||||
<div class='col-6'><!-- empty left side --></div>
|
||||
<div class='col-6'>${(doc.taxes || []).map(row => {
|
||||
return `<div class='row'>
|
||||
<div class='col-6'>${row.account} (${row.rate}%)</div>
|
||||
<div class='col-6'></div>
|
||||
<div class='col-6'>
|
||||
<div class='row' v-for='row in value'>
|
||||
<div class='col-6'>{{row.account}} ({{row.rate}}%)</div>
|
||||
<div class='col-6 text-right'>
|
||||
${frappe.format(row.amount, 'Currency')}
|
||||
{{frappe.format(row.amount, 'Currency')}}
|
||||
</div>
|
||||
</div>`
|
||||
}).join('')}
|
||||
</div></div>`;
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "grandTotal",
|
||||
"label": "Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
formula: (doc) => doc.getGrandTotal(),
|
||||
"disabled": true
|
||||
fieldname: 'grandTotal',
|
||||
label: 'Grand Total',
|
||||
fieldtype: 'Currency',
|
||||
formula: doc => doc.getGrandTotal(),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"label": "Terms",
|
||||
"fieldtype": "Text"
|
||||
fieldname: 'terms',
|
||||
label: 'Terms',
|
||||
fieldtype: 'Text'
|
||||
},
|
||||
{
|
||||
fieldname: 'outstandingAmount',
|
||||
label: 'Outstanding Amount',
|
||||
fieldtype: 'Currency',
|
||||
hidden: 1
|
||||
}
|
||||
],
|
||||
|
||||
layout: [
|
||||
// section 1
|
||||
{
|
||||
columns: [
|
||||
{ fields: [ "supplier", "account" ] },
|
||||
{ fields: [ "date" ] }
|
||||
]
|
||||
columns: [{ fields: ['supplier', 'account'] }, { fields: ['date'] }]
|
||||
},
|
||||
|
||||
// section 2
|
||||
{ fields: [ "items" ] },
|
||||
{
|
||||
columns: [{ fields: ['items'] }]
|
||||
},
|
||||
|
||||
// section 3
|
||||
{ fields: [ "netTotal", "taxes", "grandTotal" ] },
|
||||
{
|
||||
columns: [{ fields: ['netTotal', 'taxes', 'grandTotal'] }]
|
||||
},
|
||||
|
||||
// section 4
|
||||
{ fields: [ "terms" ] },
|
||||
{
|
||||
columns: [{ fields: ['terms'] }]
|
||||
}
|
||||
],
|
||||
|
||||
links: [
|
||||
@ -116,23 +130,24 @@ module.exports = {
|
||||
condition: form => form.doc.submitted,
|
||||
action: async form => {
|
||||
const payment = await frappe.getNewDoc('Payment');
|
||||
payment.party = form.doc.supplier,
|
||||
payment.account = form.doc.account,
|
||||
payment.for = [{referenceType: form.doc.doctype, referenceName: form.doc.name, amount: form.doc.grandTotal}]
|
||||
const formModal = await frappe.desk.showFormModal('Payment', payment.name);
|
||||
payment.paymentType = 'Pay';
|
||||
payment.party = form.doc.supplier;
|
||||
payment.paymentAccount = form.doc.account;
|
||||
payment.for = [
|
||||
{
|
||||
referenceType: form.doc.doctype,
|
||||
referenceName: form.doc.name,
|
||||
amount: form.doc.grandTotal
|
||||
}
|
||||
];
|
||||
payment.on('afterInsert', async () => {
|
||||
form.$formModal.close();
|
||||
form.$router.push({
|
||||
path: `/edit/Payment/${payment.name}`
|
||||
});
|
||||
});
|
||||
await form.$formModal.open(payment);
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
listSettings: {
|
||||
getFields(list) {
|
||||
return ['name', 'supplier', 'grandTotal', 'submitted'];
|
||||
},
|
||||
|
||||
getRowHTML(list, data) {
|
||||
return `<div class="col-3">${list.getNameHTML(data)}</div>
|
||||
<div class="col-4 text-muted">${data.supplier}</div>
|
||||
<div class="col-4 text-muted text-right">${frappe.format(data.grandTotal, "Currency")}</div>`;
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
28
models/doctype/Bill/BillList.js
Normal file
28
models/doctype/Bill/BillList.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { _ } from 'frappejs/utils';
|
||||
import indicators from 'frappejs/ui/constants/indicators';
|
||||
|
||||
export default {
|
||||
doctype: 'Bill',
|
||||
title: _('Bill'),
|
||||
columns: [
|
||||
'supplier',
|
||||
{
|
||||
label: 'Status',
|
||||
getValue(doc) {
|
||||
if (doc.submitted === 1 && doc.outstandingAmount === 0.0) {
|
||||
return 'Paid';
|
||||
}
|
||||
return 'Pending';
|
||||
},
|
||||
getIndicator(doc) {
|
||||
if (doc.submitted === 1 && doc.outstandingAmount === 0.0) {
|
||||
return indicators.GREEN;
|
||||
}
|
||||
return indicators.ORANGE;
|
||||
}
|
||||
},
|
||||
'grandTotal',
|
||||
'date',
|
||||
'outstandingAmount'
|
||||
]
|
||||
};
|
@ -3,27 +3,57 @@ const Bill = require('./BillDocument');
|
||||
const LedgerPosting = require('../../../accounting/ledgerPosting');
|
||||
|
||||
module.exports = class BillServer extends Bill {
|
||||
getPosting() {
|
||||
let entries = new LedgerPosting({reference: this, party: this.supplier});
|
||||
entries.credit(this.account, this.grandTotal);
|
||||
async getPosting() {
|
||||
let entries = new LedgerPosting({ reference: this, party: this.supplier });
|
||||
await entries.credit(this.account, this.grandTotal);
|
||||
|
||||
for (let item of this.items) {
|
||||
entries.debit(item.account, item.amount);
|
||||
await entries.debit(item.account, item.amount);
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (let tax of this.taxes) {
|
||||
entries.debit(tax.account, tax.amount);
|
||||
await entries.debit(tax.account, tax.amount);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
async getPayments() {
|
||||
let payments = await frappe.db.getAll({
|
||||
doctype: 'PaymentFor',
|
||||
fields: ['parent'],
|
||||
filters: { referenceName: this.name },
|
||||
orderBy: 'name'
|
||||
});
|
||||
if (payments.length != 0) {
|
||||
return payments;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
async afterSubmit() {
|
||||
await this.getPosting().post();
|
||||
const entries = await this.getPosting();
|
||||
await entries.post();
|
||||
await frappe.db.setValue(
|
||||
'Bill',
|
||||
this.name,
|
||||
'outstandingAmount',
|
||||
this.grandTotal
|
||||
);
|
||||
}
|
||||
|
||||
async afterRevert() {
|
||||
await this.getPosting().postReverse();
|
||||
let paymentRefList = await this.getPayments();
|
||||
for (let paymentFor of paymentRefList) {
|
||||
const paymentReference = paymentFor.parent;
|
||||
const payment = await frappe.getDoc('Payment', paymentReference);
|
||||
const paymentEntries = await payment.getPosting();
|
||||
await paymentEntries.postReverse();
|
||||
// To set the payment status as unsubmitted.
|
||||
payment.revert();
|
||||
}
|
||||
}
|
||||
const entries = await this.getPosting();
|
||||
await entries.postReverse();
|
||||
}
|
||||
};
|
||||
|
@ -1,67 +1,72 @@
|
||||
module.exports = {
|
||||
name: "BillItem",
|
||||
doctype: "DocType",
|
||||
name: 'BillItem',
|
||||
doctype: 'DocType',
|
||||
isSingle: 0,
|
||||
isChild: 1,
|
||||
keywordFields: [],
|
||||
layout: 'ratio',
|
||||
fields: [
|
||||
{
|
||||
"fieldname": "item",
|
||||
"label": "Item",
|
||||
"fieldtype": "Link",
|
||||
"target": "Item",
|
||||
"required": 1,
|
||||
fieldname: 'item',
|
||||
label: 'Item',
|
||||
fieldtype: 'Link',
|
||||
target: 'Item',
|
||||
required: 1,
|
||||
width: 2
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"label": "Description",
|
||||
"fieldtype": "Text",
|
||||
fieldname: 'description',
|
||||
label: 'Description',
|
||||
fieldtype: 'Text',
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'description'),
|
||||
hidden: 1
|
||||
},
|
||||
{
|
||||
"fieldname": "quantity",
|
||||
"label": "Quantity",
|
||||
"fieldtype": "Float",
|
||||
"required": 1
|
||||
fieldname: 'quantity',
|
||||
label: 'Quantity',
|
||||
fieldtype: 'Float',
|
||||
required: 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Currency",
|
||||
"required": 1,
|
||||
fieldname: 'rate',
|
||||
label: 'Rate',
|
||||
fieldtype: 'Currency',
|
||||
required: 1,
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'rate')
|
||||
},
|
||||
{
|
||||
fieldname: "account",
|
||||
label: "Account",
|
||||
hidden: 1,
|
||||
fieldtype: "Link",
|
||||
target: "Account",
|
||||
fieldname: 'account',
|
||||
label: 'Account',
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
required: 1,
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'expenseAccount')
|
||||
},
|
||||
{
|
||||
"fieldname": "tax",
|
||||
"label": "Tax",
|
||||
"fieldtype": "Link",
|
||||
"target": "Tax",
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'tax')
|
||||
fieldname: 'tax',
|
||||
label: 'Tax',
|
||||
fieldtype: 'Link',
|
||||
target: 'Tax',
|
||||
formula: (row, doc) => {
|
||||
if (row.tax) return row.tax;
|
||||
return doc.getFrom('Item', row.item, 'tax');
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"disabled": 1,
|
||||
fieldname: 'amount',
|
||||
label: 'Amount',
|
||||
fieldtype: 'Currency',
|
||||
readOnly: 1,
|
||||
disabled: true,
|
||||
formula: (row, doc) => row.quantity * row.rate
|
||||
},
|
||||
{
|
||||
"fieldname": "taxAmount",
|
||||
"label": "Tax Amount",
|
||||
"hidden": 1,
|
||||
"fieldtype": "Text",
|
||||
fieldname: 'taxAmount',
|
||||
label: 'Tax Amount',
|
||||
hidden: 1,
|
||||
readOnly: 1,
|
||||
fieldtype: 'Text',
|
||||
formula: (row, doc) => doc.getRowTax(row)
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -144,6 +144,7 @@ module.exports = {
|
||||
form.doc.submitted && form.doc.outstandingAmount !== 0.0,
|
||||
action: async form => {
|
||||
const payment = await frappe.getNewDoc('Payment');
|
||||
payment.paymentType = 'Recieve';
|
||||
payment.party = form.doc.customer;
|
||||
payment.account = form.doc.account;
|
||||
payment.for = [
|
||||
@ -155,9 +156,9 @@ module.exports = {
|
||||
];
|
||||
payment.on('afterInsert', async () => {
|
||||
form.$formModal.close();
|
||||
|
||||
const _payment = await frappe.getDoc('Payment', payment.name);
|
||||
await _payment.submit();
|
||||
form.$router.push({
|
||||
path: `/edit/Payment/${payment.name}`
|
||||
});
|
||||
});
|
||||
await form.$formModal.open(payment);
|
||||
}
|
||||
|
@ -6,8 +6,12 @@ module.exports = class Invoice extends BaseDocument {
|
||||
if (row.tax) {
|
||||
let tax = await this.getTax(row.tax);
|
||||
let taxAmount = [];
|
||||
for (let d of (tax.details || [])) {
|
||||
taxAmount.push({account: d.account, rate: d.rate, amount: row.amount * d.rate / 100});
|
||||
for (let d of tax.details || []) {
|
||||
taxAmount.push({
|
||||
account: d.account,
|
||||
rate: d.rate,
|
||||
amount: (row.amount * d.rate) / 100
|
||||
});
|
||||
}
|
||||
return JSON.stringify(taxAmount);
|
||||
} else {
|
||||
@ -25,7 +29,10 @@ module.exports = class Invoice extends BaseDocument {
|
||||
if (!this.taxes) this.taxes = [];
|
||||
|
||||
// reset tax amount
|
||||
this.taxes.map(d => { d.amount = 0; d.rate = 0; });
|
||||
this.taxes.map(d => {
|
||||
d.amount = 0;
|
||||
d.rate = 0;
|
||||
});
|
||||
|
||||
// calculate taxes
|
||||
for (let row of this.items) {
|
||||
@ -67,6 +74,7 @@ module.exports = class Invoice extends BaseDocument {
|
||||
grandTotal += row.amount;
|
||||
}
|
||||
}
|
||||
grandTotal = Math.floor(grandTotal * 100) / 100;
|
||||
return grandTotal;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -5,7 +5,8 @@ module.exports = {
|
||||
isChild: 1,
|
||||
keywordFields: [],
|
||||
layout: 'ratio',
|
||||
fields: [{
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'item',
|
||||
label: 'Item',
|
||||
fieldtype: 'Link',
|
||||
@ -39,6 +40,7 @@ module.exports = {
|
||||
hidden: 1,
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
required: 1,
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'incomeAccount')
|
||||
},
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ module.exports = {
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
required: 1,
|
||||
getFilters: (query, control) => {
|
||||
getFilters: query => {
|
||||
return {
|
||||
isGroup: 0,
|
||||
accountType: 'Income Account'
|
||||
@ -39,7 +39,21 @@ module.exports = {
|
||||
fieldname: 'expenseAccount',
|
||||
label: 'Expense Account',
|
||||
fieldtype: 'Link',
|
||||
target: 'Account'
|
||||
target: 'Account',
|
||||
required: 1,
|
||||
getFilters: query => {
|
||||
return {
|
||||
isGroup: 0,
|
||||
accountType: [
|
||||
'in',
|
||||
[
|
||||
'Cost of Goods Sold',
|
||||
'Expense Account',
|
||||
'Stock Received But Not Billed'
|
||||
]
|
||||
]
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'tax',
|
||||
|
@ -13,7 +13,7 @@ module.exports = {
|
||||
fieldname: 'date',
|
||||
label: 'Posting Date',
|
||||
fieldtype: 'Date'
|
||||
// default: (new Date()).toISOString()
|
||||
// default: new Date().toISOString().substring(0, 10)
|
||||
},
|
||||
{
|
||||
fieldname: 'party',
|
||||
@ -24,14 +24,21 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
fieldname: 'account',
|
||||
label: 'Account',
|
||||
label: 'From Account',
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
required: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'paymentType',
|
||||
label: 'Payment Type',
|
||||
fieldtype: 'Select',
|
||||
options: ['Recieve', 'Pay'],
|
||||
required: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'paymentAccount',
|
||||
label: 'Payment Account',
|
||||
label: 'To Account',
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
required: 1,
|
||||
@ -41,11 +48,17 @@ module.exports = {
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'paymentMethod',
|
||||
label: 'Payment Method',
|
||||
fieldtype: 'Select',
|
||||
options: ['', 'Cash', 'Cheque'],
|
||||
required: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'referenceId',
|
||||
label: 'Ref. / Cheque No.',
|
||||
fieldtype: 'Data',
|
||||
default: 'ABC',
|
||||
required: 1 // TODO: UNIQUE
|
||||
},
|
||||
{
|
||||
@ -56,7 +69,10 @@ module.exports = {
|
||||
{
|
||||
fieldname: 'clearanceDate',
|
||||
label: 'Clearance Date',
|
||||
fieldtype: 'Date'
|
||||
fieldtype: 'Date',
|
||||
hidden: doc => {
|
||||
return doc.paymentMethod === 'Cheque' ? 0 : 1;
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'amount',
|
||||
@ -84,18 +100,28 @@ module.exports = {
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
fields: ['date', 'party']
|
||||
fields: ['date', 'account']
|
||||
},
|
||||
{
|
||||
fields: ['account', 'paymentAccount']
|
||||
fields: ['party', 'paymentAccount']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
fields: ['referenceId']
|
||||
fields: ['paymentMethod']
|
||||
},
|
||||
{
|
||||
fields: ['paymentType']
|
||||
},
|
||||
{
|
||||
fields: ['referenceId']
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
fields: ['referenceDate']
|
||||
},
|
||||
|
@ -9,13 +9,19 @@ export default {
|
||||
{
|
||||
label: 'Payment',
|
||||
getValue(doc) {
|
||||
if (doc.submitted === 1 && doc.clearanceDate !== null) {
|
||||
if (
|
||||
doc.submitted === 1 &&
|
||||
(doc.clearanceDate !== null || doc.paymentMethod === 'Cash')
|
||||
) {
|
||||
return 'Reconciled';
|
||||
}
|
||||
return 'Not Reconciled';
|
||||
},
|
||||
getIndicator(doc) {
|
||||
if (doc.submitted === 1 && doc.clearanceDate !== null) {
|
||||
if (
|
||||
doc.submitted === 1 &&
|
||||
(doc.clearanceDate !== null || doc.paymentMethod === 'Cash')
|
||||
) {
|
||||
return indicators.GREEN;
|
||||
}
|
||||
return indicators.ORANGE;
|
||||
@ -27,4 +33,4 @@ export default {
|
||||
'clearanceDate',
|
||||
'name'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -6,30 +6,28 @@ module.exports = class PaymentServer extends BaseDocument {
|
||||
async getPosting() {
|
||||
let entries = new LedgerPosting({ reference: this, party: this.party });
|
||||
await entries.debit(this.paymentAccount, this.amount);
|
||||
|
||||
for (let row of this.for) {
|
||||
await entries.credit(
|
||||
this.account,
|
||||
row.amount,
|
||||
row.referenceType,
|
||||
row.referenceName
|
||||
);
|
||||
await entries.credit(this.account, row.amount);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
async afterSubmit() {
|
||||
for (let row of this.for) {
|
||||
if (row.referenceType === 'Invoice') {
|
||||
const { outstandingAmount } = await frappe.getDoc(
|
||||
'Invoice',
|
||||
if (['Invoice', 'Bill'].includes(row.referenceType)) {
|
||||
const { 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 {
|
||||
await frappe.db.setValue(
|
||||
'Invoice',
|
||||
row.referenceType,
|
||||
row.referenceName,
|
||||
'outstandingAmount',
|
||||
outstandingAmount - this.amount
|
||||
|
41
models/doctype/Tax/RegionalChanges.js
Normal file
41
models/doctype/Tax/RegionalChanges.js
Normal file
@ -0,0 +1,41 @@
|
||||
module.exports = async function generateTaxes(country) {
|
||||
if (country === 'India') {
|
||||
const GSTs = {
|
||||
GST: [28, 18, 12, 6, 5, 3, 0.25, 0],
|
||||
IGST: [28, 18, 12, 6, 5, 3, 0.25, 0],
|
||||
'Exempt-GST': [0],
|
||||
'Exempt-IGST': [0]
|
||||
};
|
||||
let newTax = await frappe.getNewDoc('Tax');
|
||||
for (const type of Object.keys(GSTs)) {
|
||||
for (const percent of GSTs[type]) {
|
||||
if (type === 'GST') {
|
||||
await newTax.set({
|
||||
name: `${type}-${percent}`,
|
||||
details: [
|
||||
{
|
||||
account: 'CGST',
|
||||
rate: percent / 2
|
||||
},
|
||||
{
|
||||
account: 'SGST',
|
||||
rate: percent / 2
|
||||
}
|
||||
]
|
||||
});
|
||||
} else {
|
||||
await newTax.set({
|
||||
name: `${type}-${percent}`,
|
||||
details: [
|
||||
{
|
||||
account: type.toString().split('-')[0],
|
||||
rate: percent
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
await newTax.insert();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
0
models/doctype/Tax/TaxServer.js
Normal file
0
models/doctype/Tax/TaxServer.js
Normal file
@ -2,7 +2,7 @@ let title = 'General Ledger';
|
||||
let filterFields = [
|
||||
{
|
||||
fieldtype: 'Select',
|
||||
options: ['', 'Invoice', 'Payment'],
|
||||
options: ['', 'Invoice', 'Payment', 'Bill'],
|
||||
label: 'Reference Type',
|
||||
fieldname: 'referenceType'
|
||||
},
|
||||
@ -74,7 +74,7 @@ const viewConfig = {
|
||||
label: 'Clear Filters',
|
||||
type: 'secondary',
|
||||
action: async report => {
|
||||
await report.getReportData({});
|
||||
await report.$router.replace(`/report/general-ledger`);
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -17,43 +17,55 @@ class GoodsAndServiceTax {
|
||||
|
||||
let tableData = [];
|
||||
for (let invoice of invoiceNames) {
|
||||
const row = await this.getRow(invoice.name)
|
||||
tableData.push(row)
|
||||
const row = await this.getRow(invoice.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
|
||||
})
|
||||
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;
|
||||
}
|
||||
|
||||
async getRow(name) {
|
||||
let row = {}
|
||||
let invoiceDetails = await frappe.getDoc('Invoice', name);
|
||||
async getRow(invoiceName) {
|
||||
let row = {};
|
||||
let invoiceDetails = await frappe.getDoc('Invoice', invoiceName);
|
||||
let customerDetails = await frappe.getDoc('Party', invoiceDetails.customer);
|
||||
let addressDetails = await frappe.getDoc('Address', customerDetails.address);
|
||||
row.gstin = customerDetails.gstin
|
||||
row.cusName = invoiceDetails.customer
|
||||
row.invNo = invoiceDetails.name
|
||||
row.invDate = invoiceDetails.date
|
||||
row.place = addressDetails.state
|
||||
row.rate = 0
|
||||
if (customerDetails.address) {
|
||||
let addressDetails = await frappe.getDoc(
|
||||
'Address',
|
||||
customerDetails.address
|
||||
);
|
||||
row.place = addressDetails.state || '';
|
||||
}
|
||||
row.gstin = customerDetails.gstin;
|
||||
row.cusName = invoiceDetails.customer;
|
||||
row.invNo = invoiceDetails.name;
|
||||
row.invDate = invoiceDetails.date;
|
||||
|
||||
row.rate = 0;
|
||||
row.transferType = 'In State';
|
||||
invoiceDetails.taxes.forEach(tax => {
|
||||
row.rate += tax.rate
|
||||
if (tax.account === 'IGST') row.transferType = 'Out of State';
|
||||
});
|
||||
row.invAmt = invoiceDetails.grandTotal
|
||||
row.taxAmt = invoiceDetails.netTotal
|
||||
return row
|
||||
row.rate += tax.rate;
|
||||
const taxAmt = (tax.rate * invoiceDetails.netTotal) / 100;
|
||||
if (tax.account === 'IGST') {
|
||||
row.transferType = 'Out of State';
|
||||
row.igstAmt = taxAmt;
|
||||
}
|
||||
if (tax.account === 'CGST') row.cgstAmt = taxAmt;
|
||||
if (tax.account === 'SGST') row.sgstAmt = taxAmt;
|
||||
});
|
||||
row.invAmt = invoiceDetails.grandTotal;
|
||||
row.taxAmt = invoiceDetails.netTotal;
|
||||
return row;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = GoodsAndServiceTax;
|
||||
|
@ -8,7 +8,8 @@ module.exports = class GoodsAndServiceTaxView extends ReportPage {
|
||||
filterFields: [
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
label: 'Transfer Type'
|
||||
label: 'Transfer Type',
|
||||
options: ['In State', 'Out of State']
|
||||
},
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
@ -29,7 +30,8 @@ module.exports = class GoodsAndServiceTaxView extends ReportPage {
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
label: 'GSTIN No.',
|
||||
fieldname: 'gstin',
|
||||
fieldtype: 'Data'
|
||||
@ -47,7 +49,7 @@ module.exports = class GoodsAndServiceTaxView extends ReportPage {
|
||||
{
|
||||
label: 'Invoice Value',
|
||||
fieldname: 'invAmt',
|
||||
fieldtype: 'Data'
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Invoice Date',
|
||||
@ -67,7 +69,22 @@ module.exports = class GoodsAndServiceTaxView extends ReportPage {
|
||||
{
|
||||
label: 'Taxable Amount',
|
||||
fieldname: 'taxAmt',
|
||||
fieldtype: 'Data'
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Intergrated Tax',
|
||||
fieldname: 'igstAmt',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Central Tax',
|
||||
fieldname: 'cgstAmt',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'State Tax',
|
||||
fieldname: 'sgstAmt',
|
||||
fieldtype: 'Currency'
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ const title = 'Goods and Service Tax';
|
||||
module.exports = {
|
||||
title: title,
|
||||
method: 'gst-taxes',
|
||||
filterFields: [{
|
||||
filterFields: [
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
label: 'Transfer Type',
|
||||
fieldname: 'transferType'
|
||||
@ -21,48 +22,76 @@ module.exports = {
|
||||
fieldtype: 'Date',
|
||||
label: 'To Date',
|
||||
fieldname: 'toDate'
|
||||
}],
|
||||
}
|
||||
],
|
||||
|
||||
getColumns() {
|
||||
return [{
|
||||
return [
|
||||
{
|
||||
label: 'GSTIN No.',
|
||||
fieldname: 'gstin',
|
||||
fieldtype: 'Data'
|
||||
fieldtype: 'Data',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'cusName',
|
||||
label: 'Customer Name'
|
||||
label: 'Customer Name',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: 'Invoice No.',
|
||||
fieldname: 'invNo',
|
||||
fieldtype: 'Data'
|
||||
fieldtype: 'Data',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: 'Invoice Value',
|
||||
fieldname: 'invAmt',
|
||||
fieldtype: 'Data'
|
||||
fieldtype: 'Currency',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: 'Invoice Date',
|
||||
fieldname: 'invDate',
|
||||
fieldtype: 'Date'
|
||||
fieldtype: 'Date',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: 'Place of supply',
|
||||
fieldname: 'place',
|
||||
fieldtype: 'Data'
|
||||
fieldtype: 'Data',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: 'Rate',
|
||||
fieldname: 'rate',
|
||||
fieldtype: 'Data'
|
||||
fieldtype: 'Data',
|
||||
width: 80
|
||||
},
|
||||
{
|
||||
label: 'Taxable Amount',
|
||||
fieldname: 'taxAmt',
|
||||
fieldtype: 'Data'
|
||||
fieldtype: 'Currency',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: 'Intergrated Tax',
|
||||
fieldname: 'igstAmt',
|
||||
fieldtype: 'Currency',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: 'Central Tax',
|
||||
fieldname: 'cgstAmt',
|
||||
fieldtype: 'Currency',
|
||||
width: 100
|
||||
},
|
||||
{
|
||||
label: 'State Tax',
|
||||
fieldname: 'sgstAmt',
|
||||
fieldtype: 'Currency',
|
||||
width: 100
|
||||
}
|
||||
];
|
||||
}
|
||||
|
@ -77,62 +77,14 @@ import Toasted from 'vue-toasted';
|
||||
await frappe.call({
|
||||
method: 'import-coa'
|
||||
});
|
||||
|
||||
const generateRegionalTaxes = require('../models/doctype/Tax/RegionalChanges');
|
||||
await generateRegionalTaxes(country);
|
||||
if (country === 'India') {
|
||||
frappe.models.Party = require('../models/doctype/Party/RegionalChanges.js');
|
||||
frappe.models.Party = require('../models/doctype/Party/RegionalChanges');
|
||||
await frappe.db.migrate();
|
||||
await generateGstTaxes();
|
||||
}
|
||||
frappe.events.trigger('show-desk');
|
||||
});
|
||||
async function generateGstTaxes() {
|
||||
const gstPercents = [5, 12, 18, 28];
|
||||
const gstTypes = ['Out of State', 'In State'];
|
||||
let newTax = await frappe.getNewDoc('Tax');
|
||||
for (const type of gstTypes) {
|
||||
for (const percent of gstPercents) {
|
||||
switch (type) {
|
||||
case 'Out of State':
|
||||
await newTax.set({
|
||||
name: `${type}-${percent}`,
|
||||
details: [
|
||||
{
|
||||
account: 'IGST',
|
||||
rate: percent
|
||||
}
|
||||
]
|
||||
});
|
||||
break;
|
||||
case 'In State':
|
||||
await newTax.set({
|
||||
name: `${type}-${percent}`,
|
||||
details: [
|
||||
{
|
||||
account: 'CGST',
|
||||
rate: percent / 2
|
||||
},
|
||||
{
|
||||
account: 'SGST',
|
||||
rate: percent / 2
|
||||
}
|
||||
]
|
||||
});
|
||||
break;
|
||||
}
|
||||
await newTax.insert();
|
||||
}
|
||||
}
|
||||
await newTax.set({
|
||||
name: `Exempt-0`,
|
||||
details: [
|
||||
{
|
||||
account: 'Exempt',
|
||||
rate: 0
|
||||
}
|
||||
]
|
||||
});
|
||||
await newTax.insert();
|
||||
}
|
||||
|
||||
async function connectToLocalDatabase(filepath) {
|
||||
try {
|
||||
|
@ -21,6 +21,7 @@
|
||||
:parentValue="child.name"
|
||||
:doctype="doctype"
|
||||
:currency="currency"
|
||||
:rootType="child.rootType"
|
||||
@updateBalance="updateBalance"
|
||||
/>
|
||||
</div>
|
||||
@ -28,7 +29,7 @@
|
||||
</template>
|
||||
<script>
|
||||
const Branch = {
|
||||
props: ['label', 'parentValue', 'doctype', 'balance', 'currency'],
|
||||
props: ['label', 'parentValue', 'doctype', 'balance', 'currency', 'rootType'],
|
||||
data() {
|
||||
return {
|
||||
expanded: false,
|
||||
@ -50,7 +51,7 @@ const Branch = {
|
||||
},
|
||||
async mounted() {
|
||||
this.settings = frappe.getMeta(this.doctype).treeSettings;
|
||||
if (this.nodeBalance > 0) {
|
||||
if (this.nodeBalance != 0) {
|
||||
this.$emit('updateBalance', this.nodeBalance);
|
||||
}
|
||||
await this.toggleChildren();
|
||||
@ -64,7 +65,7 @@ const Branch = {
|
||||
await this.getChildren();
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
updateBalance(balance) {
|
||||
async updateBalance(balance) {
|
||||
this.nodeBalance += balance;
|
||||
this.$emit('updateBalance', this.nodeBalance);
|
||||
},
|
||||
@ -80,7 +81,13 @@ const Branch = {
|
||||
const children = await frappe.db.getAll({
|
||||
doctype: this.doctype,
|
||||
filters,
|
||||
fields: [this.settings.parentField, 'isGroup', 'name', 'balance'],
|
||||
fields: [
|
||||
this.settings.parentField,
|
||||
'isGroup',
|
||||
'name',
|
||||
'balance',
|
||||
'rootType'
|
||||
],
|
||||
orderBy: 'name',
|
||||
order: 'asc'
|
||||
});
|
||||
@ -88,6 +95,7 @@ const Branch = {
|
||||
this.children = children.map(c => {
|
||||
c.label = c.name;
|
||||
c.balance = c.balance;
|
||||
c.rootType = c.rootType;
|
||||
return c;
|
||||
});
|
||||
}
|
||||
|
@ -40,25 +40,6 @@ export default {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async getChildren(parentValue) {
|
||||
let filters = {
|
||||
[this.settings.parentField]: parentValue
|
||||
};
|
||||
|
||||
const children = await frappe.db.getAll({
|
||||
doctype: this.doctype,
|
||||
filters,
|
||||
fields: [this.settings.parentField, 'isGroup', 'name', 'balance'],
|
||||
orderBy: 'name',
|
||||
order: 'asc'
|
||||
});
|
||||
|
||||
return children.map(c => {
|
||||
c.label = c.name;
|
||||
c.balance = c.balance;
|
||||
return c;
|
||||
});
|
||||
},
|
||||
updateBalance(balance) {
|
||||
this.root.balance += balance;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="row no-gutters">
|
||||
<sidebar class="col-2" />
|
||||
<div class="page-container col-10 bg-light">
|
||||
<router-view />
|
||||
<router-view :key="$route.fullPath" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -13,7 +13,7 @@ export default {
|
||||
components: {
|
||||
Sidebar
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.page-container {
|
||||
|
@ -150,13 +150,6 @@ export default {
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
/* FIX: For table cell expanding when active */
|
||||
.table-cell {
|
||||
min-height: 58px;
|
||||
}
|
||||
.table-cell > div {
|
||||
margin-top: 6px;
|
||||
}
|
||||
.table th,
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Invoice from '../../../models/doctype/Invoice/InvoiceList';
|
||||
import Bill from '../../../models/doctype/Bill/BillList';
|
||||
import Customer from '../../../models/doctype/Party/CustomerList';
|
||||
import Supplier from '../../../models/doctype/Party/SupplierList';
|
||||
import Item from '../../../models/doctype/Item/ItemList';
|
||||
@ -9,6 +10,7 @@ import Account from '../../../models/doctype/Account/AccountList';
|
||||
|
||||
export default {
|
||||
Invoice,
|
||||
Bill,
|
||||
Customer,
|
||||
Supplier,
|
||||
Item,
|
||||
|
@ -58,6 +58,10 @@ export default {
|
||||
label: _('Invoice'),
|
||||
route: '/list/Invoice'
|
||||
},
|
||||
{
|
||||
label: _('Bill'),
|
||||
route: '/list/Bill'
|
||||
},
|
||||
{
|
||||
label: _('Journal Entry'),
|
||||
route: '/list/JournalEntry'
|
||||
|
Loading…
Reference in New Issue
Block a user