mirror of
https://github.com/frappe/books.git
synced 2024-12-23 03:19:01 +00:00
Multicurrency
This commit is contained in:
parent
562b4759e0
commit
3192cb556a
@ -41,6 +41,16 @@ module.exports = {
|
||||
required: 0
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'numberFormat',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'symbol',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'fullname',
|
||||
label: 'Name',
|
||||
|
53
models/doctype/Currency/Currency.js
Normal file
53
models/doctype/Currency/Currency.js
Normal file
@ -0,0 +1,53 @@
|
||||
module.exports = {
|
||||
name: 'Currency',
|
||||
label: 'Currency',
|
||||
doctype: 'DocType',
|
||||
isSingle: 0,
|
||||
keywordFields: ['name', 'symbol'],
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'name',
|
||||
label: 'Currency Name',
|
||||
fieldtype: 'Data',
|
||||
required: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'fraction',
|
||||
label: 'Fraction',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
fieldname: 'fractionUnits',
|
||||
label: 'Fraction Units',
|
||||
fieldtype: 'Int'
|
||||
},
|
||||
{
|
||||
label: 'Smallest Currency Fraction Value',
|
||||
fieldname: 'smallestValue',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Symbol',
|
||||
fieldname: 'symbol',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
fieldname: 'numberFormat',
|
||||
fieldtype: 'Select',
|
||||
label: 'Number Format',
|
||||
options: [
|
||||
'',
|
||||
'#,###.##',
|
||||
'#.###,##',
|
||||
'# ###.##',
|
||||
'# ###,##',
|
||||
"#'###.##",
|
||||
'#, ###.##',
|
||||
'#,##,###.##',
|
||||
'#,###.###',
|
||||
'#.###',
|
||||
'#,###'
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
@ -29,6 +29,16 @@ module.exports = {
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'currency',
|
||||
label: 'Currency',
|
||||
fieldtype: 'Link',
|
||||
target: 'Currency',
|
||||
formula: async doc => {
|
||||
const { currency } = await frappe.getSingle('AccountingSettings');
|
||||
return currency;
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'customer',
|
||||
label: 'Customer',
|
||||
|
@ -3,6 +3,16 @@ const frappe = require('frappejs');
|
||||
const LedgerPosting = require('../../../accounting/ledgerPosting');
|
||||
|
||||
module.exports = class PaymentServer extends BaseDocument {
|
||||
async change({ changed }) {
|
||||
if (changed === 'for') {
|
||||
this.amount = 0;
|
||||
for (let paymentReference of this.for) {
|
||||
this.amount += frappe.parseNumber(paymentReference.amount);
|
||||
}
|
||||
this.amount = frappe.format(this.amount, 'Currency');
|
||||
}
|
||||
}
|
||||
|
||||
async getPosting() {
|
||||
let entries = new LedgerPosting({ reference: this, party: this.party });
|
||||
await entries.debit(this.paymentAccount, this.amount);
|
||||
@ -23,12 +33,18 @@ module.exports = class PaymentServer extends BaseDocument {
|
||||
if (outstandingAmount === null) {
|
||||
outstandingAmount = grandTotal;
|
||||
}
|
||||
if (0 >= this.amount || this.amount > outstandingAmount) {
|
||||
if (
|
||||
0 >= frappe.parseNumber(this.amount) ||
|
||||
frappe.parseNumber(this.amount) >
|
||||
frappe.parseNumber(outstandingAmount)
|
||||
) {
|
||||
frappe.call({
|
||||
method: 'show-dialog',
|
||||
args: {
|
||||
title: 'Invalid Payment Entry',
|
||||
message: `Payment amount is greater than 0 and less than Outstanding amount (${outstandingAmount})`
|
||||
message: `Payment amount (${
|
||||
this.amount
|
||||
}) should be greater than 0 and less than Outstanding amount (${outstandingAmount})`
|
||||
}
|
||||
});
|
||||
throw new Error();
|
||||
|
@ -48,6 +48,21 @@ module.exports = {
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'currency',
|
||||
label: 'Customer Currency',
|
||||
fieldtype: 'Link',
|
||||
target: 'Currency',
|
||||
hidden: 1,
|
||||
formula: doc => doc.getFrom('Party', doc.supplier, 'currency')
|
||||
},
|
||||
{
|
||||
fieldname: 'exchangeRate',
|
||||
label: 'Exchange Rate',
|
||||
fieldtype: 'Float',
|
||||
placeholder: '1 USD = [?] INR',
|
||||
hidden: doc => !doc.isForeignTransaction()
|
||||
},
|
||||
{
|
||||
fieldname: 'items',
|
||||
label: 'Items',
|
||||
@ -56,10 +71,20 @@ module.exports = {
|
||||
required: true
|
||||
},
|
||||
{
|
||||
fieldname: 'netTotal',
|
||||
label: 'Net Total',
|
||||
fieldname: 'baseNetTotal',
|
||||
label: 'Net Total (INR)',
|
||||
fieldtype: 'Currency',
|
||||
formula: doc => doc.getSum('items', 'amount'),
|
||||
formula: async doc => await doc.getBaseNetTotal(),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'netTotal',
|
||||
label: 'Net Total (USD)',
|
||||
fieldtype: 'Currency',
|
||||
hidden: doc => !doc.isForeignTransaction(),
|
||||
formula: async doc =>
|
||||
await doc.formatIntoCustomerCurrency(doc.getSum('items', 'amount')),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
@ -74,9 +99,9 @@ module.exports = {
|
||||
<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'>{{ row.account }} ({{row.rate}}%)</div>
|
||||
<div class='col-6 text-right'>
|
||||
{{frappe.format(row.amount, 'Currency')}}
|
||||
{{ row.amount }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,10 +109,19 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'grandTotal',
|
||||
label: 'Grand Total',
|
||||
fieldname: 'baseGrandTotal',
|
||||
label: 'Grand Total (INR)',
|
||||
fieldtype: 'Currency',
|
||||
formula: doc => doc.getGrandTotal(),
|
||||
formula: async doc => await doc.getBaseGrandTotal(),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'grandTotal',
|
||||
label: 'Grand Total (USD)',
|
||||
fieldtype: 'Currency',
|
||||
hidden: doc => !doc.isForeignTransaction(),
|
||||
formula: async doc => await doc.getGrandTotal(),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
@ -107,7 +141,10 @@ module.exports = {
|
||||
layout: [
|
||||
// section 1
|
||||
{
|
||||
columns: [{ fields: ['supplier', 'account'] }, { fields: ['date'] }]
|
||||
columns: [
|
||||
{ fields: ['supplier', 'account'] },
|
||||
{ fields: ['date', 'exchangeRate'] }
|
||||
]
|
||||
},
|
||||
|
||||
// section 2
|
||||
@ -117,7 +154,17 @@ module.exports = {
|
||||
|
||||
// section 3
|
||||
{
|
||||
columns: [{ fields: ['netTotal', 'taxes', 'grandTotal'] }]
|
||||
columns: [
|
||||
{
|
||||
fields: [
|
||||
'baseNetTotal',
|
||||
'netTotal',
|
||||
'taxes',
|
||||
'baseGrandTotal',
|
||||
'grandTotal'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// section 4
|
||||
@ -141,7 +188,7 @@ module.exports = {
|
||||
{
|
||||
referenceType: form.doc.doctype,
|
||||
referenceName: form.doc.name,
|
||||
amount: form.doc.grandTotal
|
||||
amount: form.doc.outstandingAmount
|
||||
}
|
||||
];
|
||||
payment.on('afterInsert', async () => {
|
||||
|
@ -5,15 +5,23 @@ const LedgerPosting = require('../../../accounting/ledgerPosting');
|
||||
module.exports = class PurchaseInvoiceServer extends PurchaseInvoice {
|
||||
async getPosting() {
|
||||
let entries = new LedgerPosting({ reference: this, party: this.supplier });
|
||||
await entries.credit(this.account, this.grandTotal);
|
||||
await entries.credit(this.account, this.baseGrandTotal);
|
||||
|
||||
for (let item of this.items) {
|
||||
await entries.debit(item.account, item.amount);
|
||||
const baseItemAmount = frappe.format(
|
||||
frappe.parseNumber(item.amount) * this.exchangeRate,
|
||||
'Currency'
|
||||
);
|
||||
await entries.debit(item.account, baseItemAmount);
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (let tax of this.taxes) {
|
||||
await entries.debit(tax.account, tax.amount);
|
||||
const baseTaxAmount = frappe.format(
|
||||
frappe.parseNumber(tax.amount) * this.exchangeRate,
|
||||
'Currency'
|
||||
);
|
||||
await entries.debit(tax.account, baseTaxAmount);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
@ -32,6 +40,11 @@ module.exports = class PurchaseInvoiceServer extends PurchaseInvoice {
|
||||
return [];
|
||||
}
|
||||
|
||||
async beforeInsert() {
|
||||
const entries = await this.getPosting();
|
||||
await entries.validateEntries();
|
||||
}
|
||||
|
||||
async afterSubmit() {
|
||||
const entries = await this.getPosting();
|
||||
await entries.post();
|
||||
@ -39,7 +52,7 @@ module.exports = class PurchaseInvoiceServer extends PurchaseInvoice {
|
||||
'PurchaseInvoice',
|
||||
this.name,
|
||||
'outstandingAmount',
|
||||
this.grandTotal
|
||||
this.baseGrandTotal
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,11 @@ module.exports = {
|
||||
fieldtype: 'Currency',
|
||||
readOnly: 1,
|
||||
disabled: true,
|
||||
formula: (row, doc) => row.quantity * row.rate
|
||||
formula: async (row, doc) => {
|
||||
return await doc.formatIntoCustomerCurrency(
|
||||
row.quantity * frappe.parseNumber(row.rate)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'taxAmount',
|
||||
|
@ -47,18 +47,29 @@ module.exports = {
|
||||
target: 'Account',
|
||||
formula: doc => doc.getFrom('Party', doc.customer, 'defaultAccount'),
|
||||
getFilters: (query, control) => {
|
||||
if (query)
|
||||
return {
|
||||
keywords: ['like', query],
|
||||
isGroup: 0,
|
||||
accountType: 'Receivable'
|
||||
};
|
||||
if (!query) return { isGroup: 0, accountType: 'Receivable' };
|
||||
return {
|
||||
keywords: ['like', query],
|
||||
isGroup: 0,
|
||||
accountType: 'Receivable'
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'currency',
|
||||
label: 'Customer Currency',
|
||||
fieldtype: 'Link',
|
||||
target: 'Currency',
|
||||
hidden: 1,
|
||||
formula: doc => doc.getFrom('Party', doc.customer, 'currency')
|
||||
},
|
||||
{
|
||||
fieldname: 'exchangeRate',
|
||||
label: 'Exchange Rate',
|
||||
fieldtype: 'Float',
|
||||
placeholder: '1 USD = [?] INR',
|
||||
hidden: doc => !doc.isForeignTransaction()
|
||||
},
|
||||
{
|
||||
fieldname: 'items',
|
||||
label: 'Items',
|
||||
@ -67,10 +78,20 @@ module.exports = {
|
||||
required: true
|
||||
},
|
||||
{
|
||||
fieldname: 'netTotal',
|
||||
label: 'Net Total',
|
||||
fieldname: 'baseNetTotal',
|
||||
label: 'Net Total (INR)',
|
||||
fieldtype: 'Currency',
|
||||
formula: doc => frappe.format(doc.getSum('items', 'amount'), 'Currency'),
|
||||
formula: async doc => await doc.getBaseNetTotal(),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'netTotal',
|
||||
label: 'Net Total (USD)',
|
||||
fieldtype: 'Currency',
|
||||
hidden: doc => !doc.isForeignTransaction(),
|
||||
formula: async doc =>
|
||||
await doc.formatIntoCustomerCurrency(doc.getSum('items', 'amount')),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
@ -87,7 +108,7 @@ module.exports = {
|
||||
<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')}}
|
||||
{{ row.amount }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -95,10 +116,19 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'grandTotal',
|
||||
label: 'Grand Total',
|
||||
fieldname: 'baseGrandTotal',
|
||||
label: 'Grand Total (INR)',
|
||||
fieldtype: 'Currency',
|
||||
formula: doc => frappe.format(doc.getGrandTotal(), 'Currency'),
|
||||
formula: async doc => await doc.getBaseGrandTotal(),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'grandTotal',
|
||||
label: 'Grand Total (USD)',
|
||||
fieldtype: 'Currency',
|
||||
hidden: doc => !doc.isForeignTransaction(),
|
||||
formula: async doc => await doc.getGrandTotal(),
|
||||
disabled: true,
|
||||
readOnly: 1
|
||||
},
|
||||
@ -118,7 +148,10 @@ module.exports = {
|
||||
layout: [
|
||||
// section 1
|
||||
{
|
||||
columns: [{ fields: ['customer', 'account'] }, { fields: ['date'] }]
|
||||
columns: [
|
||||
{ fields: ['customer', 'account'] },
|
||||
{ fields: ['date', 'exchangeRate'] }
|
||||
]
|
||||
},
|
||||
|
||||
// section 2
|
||||
@ -128,7 +161,17 @@ module.exports = {
|
||||
|
||||
// section 3
|
||||
{
|
||||
columns: [{ fields: ['netTotal', 'taxes', 'grandTotal'] }]
|
||||
columns: [
|
||||
{
|
||||
fields: [
|
||||
'baseNetTotal',
|
||||
'netTotal',
|
||||
'taxes',
|
||||
'baseGrandTotal',
|
||||
'grandTotal'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// section 4
|
||||
@ -152,7 +195,7 @@ module.exports = {
|
||||
{
|
||||
referenceType: form.doc.doctype,
|
||||
referenceName: form.doc.name,
|
||||
amount: form.doc.grandTotal
|
||||
amount: form.doc.outstandingAmount
|
||||
}
|
||||
];
|
||||
payment.on('afterInsert', async () => {
|
||||
|
@ -2,15 +2,98 @@ const BaseDocument = require('frappejs/model/document');
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = class SalesInvoice extends BaseDocument {
|
||||
async change({ changed }) {
|
||||
if (changed === 'items' || changed === 'exchangeRate') {
|
||||
const companyCurrency = frappe.AccountingSettings.currency;
|
||||
if (this.currency.length && this.currency !== companyCurrency) {
|
||||
for (let item of this.items) {
|
||||
if (item.rate && this.exchangeRate) {
|
||||
const itemRate = await this.getFrom('Item', item.item, 'rate');
|
||||
|
||||
item.rate = frappe.parseNumber(itemRate) / this.exchangeRate;
|
||||
if (item.quantity) {
|
||||
item.amount = item.rate * item.quantity;
|
||||
}
|
||||
item.amount = await this.formatIntoCustomerCurrency(item.amount);
|
||||
item.rate = await this.formatIntoCustomerCurrency(item.rate);
|
||||
}
|
||||
}
|
||||
this.netTotal = await this.formatIntoCustomerCurrency(this.netTotal);
|
||||
this.grandTotal = await this.formatIntoCustomerCurrency(
|
||||
this.grandTotal
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (changed === 'customer' || changed === 'supplier') {
|
||||
this.currency = await this.getFrom('Party', this[changed], 'currency');
|
||||
this.exchangeRate = await this.getExchangeRate();
|
||||
}
|
||||
}
|
||||
|
||||
async formatIntoCustomerCurrency(value) {
|
||||
const companyCurrency = frappe.AccountingSettings.currency;
|
||||
if (this.currency.length && this.currency !== companyCurrency) {
|
||||
const { numberFormat, symbol } = await this.getCustomerCurrencyInfo();
|
||||
return frappe.format(value, {
|
||||
fieldtype: 'Currency',
|
||||
currencyInfo: { numberFormat, symbol }
|
||||
});
|
||||
} else {
|
||||
return frappe.format(value, 'Currency');
|
||||
}
|
||||
}
|
||||
|
||||
isForeignTransaction() {
|
||||
return this.currency
|
||||
? this.currency !== frappe.AccountingSettings.currency
|
||||
? 1
|
||||
: 0
|
||||
: 0;
|
||||
}
|
||||
|
||||
async getCustomerCurrencyInfo() {
|
||||
if (this.numberFormat || this.symbol) {
|
||||
return { numberFormat: this.numberFormat, symbol: this.symbol };
|
||||
}
|
||||
const { numberFormat, symbol } = await frappe.getDoc(
|
||||
'Currency',
|
||||
this.currency
|
||||
);
|
||||
this.numberFormat = numberFormat;
|
||||
this.symbol = symbol;
|
||||
|
||||
return { numberFormat, symbol };
|
||||
}
|
||||
|
||||
async getExchangeRate() {
|
||||
const companyCurrency = frappe.AccountingSettings.currency;
|
||||
return this.currency === companyCurrency ? 1.0 : undefined;
|
||||
}
|
||||
|
||||
async getBaseNetTotal() {
|
||||
if (this.isForeignTransaction()) {
|
||||
return frappe.format(
|
||||
this.getSum('items', 'amount') * (this.exchangeRate || 0),
|
||||
'Currency'
|
||||
);
|
||||
} else {
|
||||
return await this.formatIntoCustomerCurrency(
|
||||
this.getSum('items', 'amount')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getRowTax(row) {
|
||||
if (row.tax) {
|
||||
let tax = await this.getTax(row.tax);
|
||||
let taxAmount = [];
|
||||
for (let d of tax.details || []) {
|
||||
const amt = (frappe.parseNumber(row.amount) * d.rate) / 100;
|
||||
taxAmount.push({
|
||||
account: d.account,
|
||||
rate: d.rate,
|
||||
amount: (row.amount * d.rate) / 100
|
||||
amount: await this.formatIntoCustomerCurrency(amt)
|
||||
});
|
||||
}
|
||||
return JSON.stringify(taxAmount);
|
||||
@ -25,7 +108,7 @@ module.exports = class SalesInvoice extends BaseDocument {
|
||||
return this._taxes[tax];
|
||||
}
|
||||
|
||||
makeTaxSummary() {
|
||||
async makeTaxSummary() {
|
||||
if (!this.taxes) this.taxes = [];
|
||||
|
||||
// reset tax amount
|
||||
@ -45,7 +128,12 @@ module.exports = class SalesInvoice extends BaseDocument {
|
||||
for (let taxDetail of this.taxes) {
|
||||
if (taxDetail.account === rowTaxDetail.account) {
|
||||
taxDetail.rate = rowTaxDetail.rate;
|
||||
taxDetail.amount += rowTaxDetail.amount;
|
||||
taxDetail.amount =
|
||||
frappe.parseNumber(taxDetail.amount) +
|
||||
frappe.parseNumber(rowTaxDetail.amount);
|
||||
taxDetail.amount = await this.formatIntoCustomerCurrency(
|
||||
taxDetail.amount
|
||||
);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
@ -66,16 +154,29 @@ module.exports = class SalesInvoice extends BaseDocument {
|
||||
this.taxes = this.taxes.filter(d => d.amount);
|
||||
}
|
||||
|
||||
getGrandTotal() {
|
||||
this.makeTaxSummary();
|
||||
async getGrandTotal() {
|
||||
await this.makeTaxSummary();
|
||||
let grandTotal = frappe.parseNumber(this.netTotal);
|
||||
if (this.taxes) {
|
||||
for (let row of this.taxes) {
|
||||
grandTotal += row.amount;
|
||||
grandTotal += frappe.parseNumber(row.amount);
|
||||
}
|
||||
}
|
||||
grandTotal = Math.floor(grandTotal * 100) / 100;
|
||||
|
||||
return grandTotal;
|
||||
return await this.formatIntoCustomerCurrency(grandTotal);
|
||||
}
|
||||
|
||||
async getBaseGrandTotal() {
|
||||
await this.makeTaxSummary();
|
||||
let baseGrandTotal = frappe.parseNumber(this.baseNetTotal);
|
||||
if (this.taxes) {
|
||||
for (let row of this.taxes) {
|
||||
baseGrandTotal += frappe.parseNumber(row.amount) * this.exchangeRate;
|
||||
}
|
||||
}
|
||||
baseGrandTotal = Math.floor(baseGrandTotal * 100) / 100;
|
||||
|
||||
return frappe.format(baseGrandTotal, 'Currency');
|
||||
}
|
||||
};
|
||||
|
@ -4,7 +4,28 @@ const LedgerPosting = require('../../../accounting/ledgerPosting');
|
||||
module.exports = class SalesInvoiceServer extends SalesInvoice {
|
||||
async getPosting() {
|
||||
let entries = new LedgerPosting({ reference: this, party: this.customer });
|
||||
await entries.debit(this.account, this.grandTotal);
|
||||
await entries.debit(this.account, this.baseGrandTotal);
|
||||
|
||||
if (this.isForeignTransaction()) {
|
||||
for (let item of this.items) {
|
||||
const baseItemAmount = frappe.format(
|
||||
frappe.parseNumber(item.amount) * this.exchangeRate,
|
||||
'Currency'
|
||||
);
|
||||
await entries.credit(item.account, baseItemAmount);
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (let tax of this.taxes) {
|
||||
const baseTaxAmount = frappe.format(
|
||||
frappe.parseNumber(tax.amount) * this.exchangeRate,
|
||||
'Currency'
|
||||
);
|
||||
await entries.credit(tax.account, baseTaxAmount);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
for (let item of this.items) {
|
||||
await entries.credit(item.account, item.amount);
|
||||
@ -31,6 +52,11 @@ module.exports = class SalesInvoiceServer extends SalesInvoice {
|
||||
return [];
|
||||
}
|
||||
|
||||
async beforeInsert() {
|
||||
const entries = await this.getPosting();
|
||||
await entries.validateEntries();
|
||||
}
|
||||
|
||||
async afterSubmit() {
|
||||
const entries = await this.getPosting();
|
||||
await entries.post();
|
||||
@ -38,7 +64,7 @@ module.exports = class SalesInvoiceServer extends SalesInvoice {
|
||||
'SalesInvoice',
|
||||
this.name,
|
||||
'outstandingAmount',
|
||||
this.grandTotal
|
||||
this.baseGrandTotal
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,108 +1,128 @@
|
||||
<template>
|
||||
<div :style="$.font" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters pl-5 pr-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-6">
|
||||
<company-address />
|
||||
</div>
|
||||
<div :style="$.regularFontSize" class="col-6 text-right">
|
||||
<h2 :style="$.headerFontColor">INVOICE</h2>
|
||||
<p :style="$.paraStyle"><strong>{{ doc.name }}</strong></p>
|
||||
<p :style="$.paraStyle">{{ frappe.format(doc.date, 'Date') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-6 mt-1">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
</div>
|
||||
<div :style="$.regularFontSize" class="row pl-5 pr-5 mt-5">
|
||||
<div class="col-12">
|
||||
<table class="table p-0">
|
||||
<thead>
|
||||
<tr :style="$.showBorderBottom">
|
||||
<th :style="$.hideBorderTop" class="text-left pl-0" style="width: 10%">{{ _("NO") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left" style="width: 60%">{{ _("ITEM") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left pl-0" style="width: 20%">{{ _("RATE") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left" style="width: 15%">{{ _("QTY") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-right pr-1" style="width: 20%">{{ _("AMOUNT") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in doc.items" :key="row.idx">
|
||||
<td class="text-left pl-1">{{ row.idx + 1 }}</td>
|
||||
<td class="text-left">{{ row.item }}</td>
|
||||
<td class="text-left pl-0">{{ frappe.format(row.rate, 'Currency') }}</td>
|
||||
<td class="text-left">{{ row.quantity }}</td>
|
||||
<td class="text-right pr-1">{{ frappe.format(row.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="$.bold" class="text-left pl-0">SUBTOTAL</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ frappe.format(doc.netTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="$.bold" class="text-left pl-0">{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ frappe.format(tax.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.mediumFontSize, $.showBorderTop]" class="text-left pl-0">TOTAL</td>
|
||||
<td :style="[$.bold, $.mediumFontSize, $.showBorderTop]" class="text-right pr-1" style="color: green;">
|
||||
{{ frappe.format(doc.grandTotal, 'Currency') }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showBorderBottom]" ><td :style="$.hideBorderTop" class="pl-0">NOTICE</td></tr>
|
||||
<tr><td class="pl-0">{{ doc.terms }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="$.font" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters pl-5 pr-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-6">
|
||||
<company-address />
|
||||
</div>
|
||||
<div :style="$.regularFontSize" class="col-6 text-right">
|
||||
<h2 :style="$.headerFontColor">INVOICE</h2>
|
||||
<p :style="$.paraStyle">
|
||||
<strong>{{ doc.name }}</strong>
|
||||
</p>
|
||||
<p :style="$.paraStyle">{{ frappe.format(doc.date, 'Date') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-6 mt-1">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
</div>
|
||||
<div :style="$.regularFontSize" class="row pl-5 pr-5 mt-5">
|
||||
<div class="col-12">
|
||||
<table class="table p-0">
|
||||
<thead>
|
||||
<tr :style="$.showBorderBottom">
|
||||
<th :style="$.hideBorderTop" class="text-left pl-0" style="width: 10%">{{ _("NO") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left" style="width: 50%">{{ _("ITEM") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left pl-0" style="width: 15%">{{ _("RATE") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left" style="width: 10%">{{ _("QTY") }}</th>
|
||||
<th
|
||||
:style="$.hideBorderTop"
|
||||
class="text-right pr-1"
|
||||
style="width: 30%"
|
||||
>{{ _("AMOUNT") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in doc.items" :key="row.idx">
|
||||
<td class="text-left pl-1">{{ row.idx + 1 }}</td>
|
||||
<td class="text-left">{{ row.item }}</td>
|
||||
<td class="text-left pl-0">{{ row.rate }}</td>
|
||||
<td class="text-left">{{ row.quantity }}</td>
|
||||
<td class="text-right pr-1">{{ row.amount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="$.bold" class="text-left pl-0">SUBTOTAL</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ doc.netTotal }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td
|
||||
colspan="2"
|
||||
:style="$.bold"
|
||||
class="text-left pl-0"
|
||||
>{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ tax.amount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td
|
||||
colspan="2"
|
||||
:style="[$.bold, $.mediumFontSize, $.showBorderTop]"
|
||||
class="text-left pl-0"
|
||||
>TOTAL</td>
|
||||
<td
|
||||
:style="[$.bold, $.mediumFontSize, $.showBorderTop]"
|
||||
class="text-right pr-1"
|
||||
style="color: green;"
|
||||
>{{ doc.grandTotal }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showBorderBottom]">
|
||||
<td :style="$.hideBorderTop" class="pl-0">NOTICE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pl-0">{{ doc.terms }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Styles from './InvoiceStyles';
|
||||
import CompanyAddress from './CompanyAddress';
|
||||
import CustomerAddress from './CustomerAddress';
|
||||
export default {
|
||||
name: 'InvoiceTemplate1',
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
name: 'InvoiceTemplate1',
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
},
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
data() {
|
||||
return {
|
||||
$: Styles
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
data() {
|
||||
return {
|
||||
$: Styles
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.headerFontColor.color = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.showBorderTop.borderTop = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.headerFontColor.color = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.showBorderTop.borderTop = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,117 +1,132 @@
|
||||
<template>
|
||||
<div :style="[$.regularFontSize, $.font]" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters p-5" :style="$.headerColor">
|
||||
<div class="col-8 text-left">
|
||||
<h1>INVOICE</h1>
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<company-address />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row p-5 mt-4">
|
||||
<div class="col-4">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p :style="[$.bold, $.mediumFontSize]">Invoice Number</p>
|
||||
<p :style="$.paraStyle">{{ doc.name }}</p><br>
|
||||
<p :style="[$.bold, $.mediumFontSize]">Date</p>
|
||||
<p :style="$.paraStyle">{{doc.date}}</p>
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<p :style="[$.bold, $.mediumFontSize]">Invoice Total</p>
|
||||
<h2 :style="$.fontColor">{{ frappe.format(doc.grandTotal, 'Currency') }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-3">
|
||||
<div class="col-12">
|
||||
<table class="table table-borderless p-0">
|
||||
<thead>
|
||||
<tr :style="[$.showBorderTop, $.fontColor]">
|
||||
<th class="text-left pl-0" style="width: 10%">{{ _("NO") }}</th>
|
||||
<th class="text-left" style="width: 60%">{{ _("ITEM") }}</th>
|
||||
<th class="text-left pl-0" style="width: 20%">{{ _("RATE") }}</th>
|
||||
<th class="text-left" style="width: 15%">{{ _("QTY") }}</th>
|
||||
<th class="text-right pr-1" style="width: 20%">{{ _("AMOUNT") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr :style="$.showBorderBottom" v-for="row in doc.items" :key="row.idx">
|
||||
<td class="text-left pl-1">{{ row.idx + 1 }}</td>
|
||||
<td class="text-left">{{ row.item }}</td>
|
||||
<td class="text-left pl-0">{{ frappe.format(row.rate, 'Currency') }}</td>
|
||||
<td class="text-left">{{ row.quantity }}</td>
|
||||
<td class="text-right pr-1">{{ frappe.format(row.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr><td colspan="5" style="padding: 4%"></td></tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.fontColor]" class="text-left pl-0">SUBTOTAL</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ frappe.format(doc.netTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.fontColor]" class="text-left pl-0">{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ frappe.format(tax.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.fontColor, $.mediumFontSize]" class="text-left pl-0">TOTAL</td>
|
||||
<td :style="[$.bold, $.mediumFontSize]" class="text-right pr-1">{{ frappe.format(doc.grandTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-5">
|
||||
<div class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showNoticeBorderBottom]" ><td :style="$.hideBorderTop" class="pl-0">NOTICE</td></tr>
|
||||
<tr><td class="pl-0">{{ doc.terms }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="[$.regularFontSize, $.font]" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters p-5" :style="$.headerColor">
|
||||
<div class="col-8 text-left">
|
||||
<h1>INVOICE</h1>
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<company-address />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row p-5 mt-4">
|
||||
<div class="col-4">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p :style="[$.bold, $.mediumFontSize]">Invoice Number</p>
|
||||
<p :style="$.paraStyle">{{ doc.name }}</p>
|
||||
<br />
|
||||
<p :style="[$.bold, $.mediumFontSize]">Date</p>
|
||||
<p :style="$.paraStyle">{{doc.date}}</p>
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<p :style="[$.bold, $.mediumFontSize]">Invoice Total</p>
|
||||
<h2 :style="$.fontColor">{{ doc.grandTotal }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-3">
|
||||
<div class="col-12">
|
||||
<table class="table table-borderless p-0">
|
||||
<thead>
|
||||
<tr :style="[$.showBorderTop, $.fontColor]">
|
||||
<th class="text-left pl-0" style="width: 10%">{{ _("NO") }}</th>
|
||||
<th class="text-left" style="width: 50%">{{ _("ITEM") }}</th>
|
||||
<th class="text-left pl-0" style="width: 15%">{{ _("RATE") }}</th>
|
||||
<th class="text-left" style="width: 10%">{{ _("QTY") }}</th>
|
||||
<th class="text-right pr-1" style="width: 30%">{{ _("AMOUNT") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr :style="$.showBorderBottom" v-for="row in doc.items" :key="row.idx">
|
||||
<td class="text-left pl-1">{{ row.idx + 1 }}</td>
|
||||
<td class="text-left">{{ row.item }}</td>
|
||||
<td class="text-left pl-0">{{ row.rate }}</td>
|
||||
<td class="text-left">{{ row.quantity }}</td>
|
||||
<td class="text-right pr-1">{{ row.amount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5" style="padding: 4%"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.fontColor]" class="text-left pl-0">SUBTOTAL</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ doc.netTotal }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td
|
||||
colspan="2"
|
||||
:style="[$.bold, $.fontColor]"
|
||||
class="text-left pl-0"
|
||||
>{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ tax.amount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td
|
||||
colspan="2"
|
||||
:style="[$.bold, $.fontColor, $.mediumFontSize]"
|
||||
class="text-left pl-0"
|
||||
>TOTAL</td>
|
||||
<td :style="[$.bold, $.mediumFontSize]" class="text-right pr-1">{{ doc.grandTotal }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-5">
|
||||
<div class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showNoticeBorderBottom]">
|
||||
<td :style="$.hideBorderTop" class="pl-0">NOTICE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pl-0">{{ doc.terms }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Styles from './InvoiceStyles';
|
||||
import CompanyAddress from './CompanyAddress';
|
||||
import CustomerAddress from './CustomerAddress';
|
||||
export default {
|
||||
name: 'InvoicePrint',
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
name: 'InvoicePrint',
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
$: Styles
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
$: Styles,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.fontColor.color = this.themeColor;
|
||||
this.$.headerColor.backgroundColor = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = '0.1rem solid #e0e0d1';
|
||||
this.$.showNoticeBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.showBorderTop.borderTop = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.fontColor.color = this.themeColor;
|
||||
this.$.headerColor.backgroundColor = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = '0.1rem solid #e0e0d1';
|
||||
this.$.showNoticeBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.showBorderTop.borderTop = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -1,113 +1,145 @@
|
||||
<template>
|
||||
<div :style="[$.regularFontSize, $.font]" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6" :style="$.bgColor"></div>
|
||||
<div class="col-4 text-center" style="vertical-align: middle">
|
||||
<h1>INVOICE</h1>
|
||||
</div>
|
||||
<div class="col-2" :style="$.bgColor"></div>
|
||||
</div>
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6 text-left pl-5">
|
||||
<company-address />
|
||||
</div>
|
||||
<div class="col-6 pr-5 text-right">
|
||||
<p :style="[$.bold, $.paraStyle, $.mediumFontSize]">{{ doc.name }}</p>
|
||||
<p :style="$.paraStyle">{{ frappe.format(doc.date, 'Date') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6 text-left pl-5">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
<div class="col-6"></div>
|
||||
</div>
|
||||
<div class="row mt-5 no-gutters">
|
||||
<div class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td :style="[$.bold, $.showBorderRight, $.tablePadding]" style="width: 15" class="pl-5">{{ _("NO") }}</td>
|
||||
<td :style="[$.bold, $.showBorderRight, $.tablePadding]" style="width: 40%">{{ _("ITEM") }}</td>
|
||||
<td class="text-left" :style="[$.bold, $.showBorderRight, $.tablePadding]" style="width: 20%">{{ _("RATE") }}</td>
|
||||
<td :style="[$.bold, $.showBorderRight, $.tablePadding]" style="width: 10%">{{ _("QTY") }}</td>
|
||||
<td class="text-right pr-5" :style="[$.bold, $.tablePadding]" style="width: 20%">{{ _("AMOUNT") }}</td>
|
||||
</tr>
|
||||
<tr v-for="row in doc.items" :key="row.idx">
|
||||
<td :style="$.tablePadding" class="pl-5 pr-5">{{ row.idx + 1 }}</td>
|
||||
<td :style="$.tablePadding">{{ row.item }}</td>
|
||||
<td :style="$.tablePadding" class="text-left">{{ frappe.format(row.rate, 'Currency') }}</td>
|
||||
<td :style="$.tablePadding">{{ row.quantity }}</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ frappe.format(row.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr><td colspan="5" style="padding: 4%"></td></tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td :style="$.tablePadding" colspan="2">SUBTOTAL</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ frappe.format(doc.netTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td :style="$.tablePadding" med colspan="2">{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ frappe.format(tax.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td :style="[$.bold, $.tablePadding, $.mediumFontSize]" colspan="2">TOTAL</td>
|
||||
<td :style="[$.bold, $.tablePadding, $.mediumFontSize]" class="text-right pr-5">{{ frappe.format(doc.grandTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div :style="$.regularFontSize" class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showBorderBottom]" >
|
||||
<td :style="$.hideBorderTop" class="pl-5">NOTICE</td>
|
||||
</tr>
|
||||
<tr><td class="pl-5">{{ doc.terms }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div :style="[$.regularFontSize, $.font]" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6" :style="$.bgColor"></div>
|
||||
<div class="col-4 text-center" style="vertical-align: middle">
|
||||
<h1>INVOICE</h1>
|
||||
</div>
|
||||
<div class="col-2" :style="$.bgColor"></div>
|
||||
</div>
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6 text-left pl-5">
|
||||
<company-address />
|
||||
</div>
|
||||
<div class="col-6 pr-5 text-right">
|
||||
<p :style="[$.bold, $.paraStyle, $.mediumFontSize]">{{ doc.name }}</p>
|
||||
<p :style="$.paraStyle">{{ frappe.format(doc.date, 'Date') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6 text-left pl-5">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
<div class="col-6"></div>
|
||||
</div>
|
||||
<div class="row mt-5 no-gutters">
|
||||
<div class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
:style="[$.bold, $.showBorderRight, $.tablePadding]"
|
||||
style="width: 15"
|
||||
class="pl-5"
|
||||
>{{ _("NO") }}</td>
|
||||
<td
|
||||
:style="[$.bold, $.showBorderRight, $.tablePadding]"
|
||||
style="width: 40%"
|
||||
>{{ _("ITEM") }}</td>
|
||||
<td
|
||||
class="text-left"
|
||||
:style="[$.bold, $.showBorderRight, $.tablePadding]"
|
||||
style="width: 20%"
|
||||
>{{ _("RATE") }}</td>
|
||||
<td
|
||||
:style="[$.bold, $.showBorderRight, $.tablePadding]"
|
||||
style="width: 10%"
|
||||
>{{ _("QTY") }}</td>
|
||||
<td
|
||||
class="text-right pr-5"
|
||||
:style="[$.bold, $.tablePadding]"
|
||||
style="width: 20%"
|
||||
>{{ _("AMOUNT") }}</td>
|
||||
</tr>
|
||||
<tr v-for="row in doc.items" :key="row.idx">
|
||||
<td :style="$.tablePadding" class="pl-5 pr-5">{{ row.idx + 1 }}</td>
|
||||
<td :style="$.tablePadding">{{ row.item }}</td>
|
||||
<td
|
||||
:style="$.tablePadding"
|
||||
class="text-left"
|
||||
>{{ frappe.format(row.rate, 'Currency') }}</td>
|
||||
<td :style="$.tablePadding">{{ row.quantity }}</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ row.amount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="5" style="padding: 4%"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td :style="$.tablePadding" colspan="2">SUBTOTAL</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ doc.netTotal }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td
|
||||
:style="$.tablePadding"
|
||||
med
|
||||
colspan="2"
|
||||
>{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ tax.amount }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td :style="[$.bold, $.tablePadding, $.mediumFontSize]" colspan="2">TOTAL</td>
|
||||
<td
|
||||
:style="[$.bold, $.tablePadding, $.mediumFontSize]"
|
||||
class="text-right pr-5"
|
||||
>{{ doc.grandTotal }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div :style="$.regularFontSize" class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showBorderBottom]">
|
||||
<td :style="$.hideBorderTop" class="pl-5">NOTICE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="pl-5">{{ doc.terms }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Styles from './InvoiceStyles';
|
||||
import CompanyAddress from './CompanyAddress';
|
||||
import CustomerAddress from './CustomerAddress';
|
||||
export default {
|
||||
name: 'InvoicePrint',
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
name: 'InvoicePrint',
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
$: Styles
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
$: Styles,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.bgColor.backgroundColor = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.bgColor.backgroundColor = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -59,7 +59,11 @@ module.exports = {
|
||||
fieldtype: 'Currency',
|
||||
readOnly: 1,
|
||||
disabled: true,
|
||||
formula: (row, doc) => row.quantity * row.rate
|
||||
formula: async (row, doc) => {
|
||||
return await doc.formatIntoCustomerCurrency(
|
||||
row.quantity * frappe.parseNumber(row.rate)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'taxAmount',
|
||||
|
@ -3,6 +3,7 @@ module.exports = {
|
||||
SetupWizard: require('./doctype/SetupWizard/SetupWizard'),
|
||||
DashboardSettings: require('./doctype/DashboardSettings/DashboardSettings'),
|
||||
DashboardChart: require('./doctype/DashboardChart/DashboardChart'),
|
||||
Currency: require('./doctype/Currency/Currency'),
|
||||
Color: require('./doctype/Color/Color'),
|
||||
Account: require('./doctype/Account/Account.js'),
|
||||
AccountingSettings: require('./doctype/AccountingSettings/AccountingSettings'),
|
||||
|
@ -13,7 +13,7 @@
|
||||
<span
|
||||
style="width: 100%"
|
||||
:class="['Float', 'Currency'].includes(column.fieldtype) ? 'text-right':''"
|
||||
>{{ frappe.format(column.getValue(doc), column.fieldtype || {}) }}</span>
|
||||
>{{ getColumnValue(column, doc) }}</span>
|
||||
</list-cell>
|
||||
</list-row>
|
||||
</div>
|
||||
@ -55,6 +55,15 @@ export default {
|
||||
frappe.listView.on('filterList', this.updateData.bind(this));
|
||||
},
|
||||
methods: {
|
||||
getColumnValue(column, doc) {
|
||||
// Since currency is formatted in customer currency
|
||||
// frappe.format parses it back into company currency
|
||||
if (['Float', 'Currency'].includes(column.fieldtype)) {
|
||||
return column.getValue(doc);
|
||||
} else {
|
||||
return frappe.format(column.getValue(doc), column.fieldtype);
|
||||
}
|
||||
},
|
||||
async setupColumnsAndData() {
|
||||
this.doctype = this.listConfig.doctype;
|
||||
await this.updateData();
|
||||
|
Loading…
Reference in New Issue
Block a user