mirror of
https://github.com/frappe/books.git
synced 2024-11-14 01:14:03 +00:00
refactor naming and added taxes!
This commit is contained in:
parent
650ad31d12
commit
34b72f2297
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,7 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
test.db
|
*test.db
|
||||||
*.log
|
*.log
|
||||||
.cache
|
.cache
|
||||||
/temp
|
/temp
|
7
dist/css/style.css
vendored
7
dist/css/style.css
vendored
@ -6824,7 +6824,6 @@ html {
|
|||||||
.hide {
|
.hide {
|
||||||
display: none !important; }
|
display: none !important; }
|
||||||
.page {
|
.page {
|
||||||
max-width: 500px;
|
|
||||||
padding-bottom: 2rem; }
|
padding-bottom: 2rem; }
|
||||||
.page .page-head {
|
.page .page-head {
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
@ -6839,8 +6838,7 @@ html {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 200px 0px; }
|
padding: 200px 0px; }
|
||||||
.form-body {
|
.form-body {
|
||||||
padding: 1rem;
|
padding: 1rem; }
|
||||||
max-width: 500px; }
|
|
||||||
.form-body .form-control.font-weight-bold {
|
.form-body .form-control.font-weight-bold {
|
||||||
background-color: lightyellow; }
|
background-color: lightyellow; }
|
||||||
.form-body .alert {
|
.form-body .alert {
|
||||||
@ -6888,8 +6886,9 @@ mark {
|
|||||||
margin-top: 0.5rem; }
|
margin-top: 0.5rem; }
|
||||||
.data-table .body-scrollable {
|
.data-table .body-scrollable {
|
||||||
border-bottom: 0px !important; }
|
border-bottom: 0px !important; }
|
||||||
|
.data-table .body-scrollable tr:first-child .data-table-col {
|
||||||
|
border-top: 0px !important; }
|
||||||
.data-table thead td {
|
.data-table thead td {
|
||||||
border-bottom: 0px !important;
|
|
||||||
background-color: #e9ecef !important; }
|
background-color: #e9ecef !important; }
|
||||||
.data-table .data-table-col .edit-cell {
|
.data-table .data-table-col .edit-cell {
|
||||||
padding: 0px !important; }
|
padding: 0px !important; }
|
||||||
|
2337
dist/js/bundle.js
vendored
2337
dist/js/bundle.js
vendored
File diff suppressed because it is too large
Load Diff
14
models/doctype/Account/AccountDocument.js
Normal file
14
models/doctype/Account/AccountDocument.js
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const frappe = require('frappejs');
|
||||||
|
const BaseDocument = require('frappejs/model/document');
|
||||||
|
|
||||||
|
module.exports = class Account extends BaseDocument {
|
||||||
|
async validate() {
|
||||||
|
if (!this.account_type) {
|
||||||
|
if (this.parent_account) {
|
||||||
|
this.account_type = await frappe.db.getValue('Account', this.parent_account, 'account_type');
|
||||||
|
} else {
|
||||||
|
this.account_type = 'Asset';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
15
models/doctype/Account/AccountForm.js
Normal file
15
models/doctype/Account/AccountForm.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
const BaseForm = require('frappejs/client/view/form');
|
||||||
|
|
||||||
|
module.exports = class AccountForm extends BaseForm {
|
||||||
|
make() {
|
||||||
|
super.make();
|
||||||
|
|
||||||
|
// override controller event
|
||||||
|
this.controls['parent_account'].getFilters = (query) => {
|
||||||
|
return {
|
||||||
|
keywords: ["like", query],
|
||||||
|
name: ["!=", this.doc.name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
models/doctype/Account/AccountList.js
Normal file
10
models/doctype/Account/AccountList.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const BaseList = require('frappejs/client/view/list');
|
||||||
|
|
||||||
|
module.exports = class AccountList extends BaseList {
|
||||||
|
getFields() {
|
||||||
|
return ['name', 'account_type'];
|
||||||
|
}
|
||||||
|
getRowHTML(data) {
|
||||||
|
return `<a href="#edit/account/${data.name}">${data.name} (${data.account_type})</a>`;
|
||||||
|
}
|
||||||
|
}
|
68
models/doctype/Invoice/invoiceDocument.js
Normal file
68
models/doctype/Invoice/invoiceDocument.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
const BaseDocument = require('frappejs/model/document');
|
||||||
|
const frappe = require('frappejs');
|
||||||
|
|
||||||
|
module.exports = class Invoice extends BaseDocument {
|
||||||
|
async getRowTax(row) {
|
||||||
|
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});
|
||||||
|
}
|
||||||
|
return JSON.stringify(taxAmount);
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async getTax(tax) {
|
||||||
|
if (!this._taxes) this._taxes = {};
|
||||||
|
if (!this._taxes[tax]) this._taxes[tax] = await frappe.getDoc('Tax', tax);
|
||||||
|
return this._taxes[tax];
|
||||||
|
}
|
||||||
|
makeTaxSummary() {
|
||||||
|
if (!this.taxes) this.taxes = [];
|
||||||
|
|
||||||
|
// reset tax amount
|
||||||
|
this.taxes.map(d => d.amount = 0);
|
||||||
|
|
||||||
|
// calculate taxes
|
||||||
|
for (let row of this.items) {
|
||||||
|
if (row.taxAmount) {
|
||||||
|
let taxAmount = JSON.parse(row.taxAmount);
|
||||||
|
for (let rowTaxDetail of taxAmount) {
|
||||||
|
let found = false;
|
||||||
|
|
||||||
|
// check if added in summary
|
||||||
|
for (let taxDetail of this.taxes) {
|
||||||
|
if (taxDetail.account === rowTaxDetail.account) {
|
||||||
|
taxDetail.amount += rowTaxDetail.amount;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new row
|
||||||
|
if (!found) {
|
||||||
|
this.taxes.push({
|
||||||
|
account: rowTaxDetail.account,
|
||||||
|
rate: rowTaxDetail.rate,
|
||||||
|
amount: rowTaxDetail.amount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear no taxes
|
||||||
|
this.taxes = this.taxes.filter(d => d.amount);
|
||||||
|
}
|
||||||
|
getGrandTotal() {
|
||||||
|
this.makeTaxSummary();
|
||||||
|
let grandTotal = this.netTotal;
|
||||||
|
if (this.taxes) {
|
||||||
|
for (let row of this.taxes) {
|
||||||
|
grandTotal += row.amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return grandTotal;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
const BaseList = require('frappejs/client/view/list');
|
const BaseList = require('frappejs/client/view/list');
|
||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
|
|
||||||
class InvoiceList extends BaseList {
|
module.exports = class InvoiceList extends BaseList {
|
||||||
getFields() {
|
getFields() {
|
||||||
return ['name', 'customer', 'total'];
|
return ['name', 'customer', 'total'];
|
||||||
}
|
}
|
||||||
@ -11,7 +11,3 @@ class InvoiceList extends BaseList {
|
|||||||
<div class="col-4 text-muted text-right">${frappe.format(data.total, {fieldtype:"Currency"})}</div>`;
|
<div class="col-4 text-muted text-right">${frappe.format(data.total, {fieldtype:"Currency"})}</div>`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
List: InvoiceList
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
{
|
module.exports = {
|
||||||
"name": "Invoice Item",
|
"name": "InvoiceItem",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"isSingle": 0,
|
"isSingle": 0,
|
||||||
"isChild": 1,
|
"isChild": 1,
|
||||||
@ -16,7 +16,7 @@
|
|||||||
"fieldname": "description",
|
"fieldname": "description",
|
||||||
"label": "Description",
|
"label": "Description",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
"formula": "doc.getFrom('Item', row.item, 'description')",
|
formula: (row, doc) => doc.getFrom('Item', row.item, 'description'),
|
||||||
"required": 1
|
"required": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -30,14 +30,27 @@
|
|||||||
"label": "Rate",
|
"label": "Rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"required": 1,
|
"required": 1,
|
||||||
"formula": "doc.getFrom('Item', row.item, 'rate')"
|
formula: (row, doc) => doc.getFrom('Item', row.item, 'rate')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tax",
|
||||||
|
"label": "Tax",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"target": "Tax"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "amount",
|
"fieldname": "amount",
|
||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"disabled": 1,
|
"disabled": 1,
|
||||||
"formula": "row.quantity * row.rate"
|
formula: (row, doc) => row.quantity * row.rate
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "taxAmount",
|
||||||
|
"label": "Tax Amount",
|
||||||
|
"hidden": 1,
|
||||||
|
"fieldtype": "Text",
|
||||||
|
formula: (row, doc) => doc.getRowTax(row)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,15 +1,16 @@
|
|||||||
{
|
module.exports = {
|
||||||
"name": "Invoice Settings",
|
"name": "InvoiceSettings",
|
||||||
|
"label": "Invoice Settings",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"isSingle": 1,
|
"isSingle": 1,
|
||||||
"isChild": 0,
|
"isChild": 0,
|
||||||
"keywordFields": [],
|
"keywordFields": [],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"fieldname": "number_series",
|
"fieldname": "numberSeries",
|
||||||
"label": "Number Series",
|
"label": "Number Series",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"target": "Number Series",
|
"target": "NumberSeries",
|
||||||
"required": 1
|
"required": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
22
models/doctype/Tax/Tax.js
Normal file
22
models/doctype/Tax/Tax.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
module.exports = {
|
||||||
|
"name": "Tax",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"isSingle": 0,
|
||||||
|
"isChild": 0,
|
||||||
|
"keywordFields": ["name"],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "name",
|
||||||
|
"label": "Name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"required": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "details",
|
||||||
|
"label": "Details",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"childtype": "TaxDetail",
|
||||||
|
"required": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
23
models/doctype/TaxDetail/TaxDetail.js
Normal file
23
models/doctype/TaxDetail/TaxDetail.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
module.exports = {
|
||||||
|
"name": "TaxDetail",
|
||||||
|
"label": "Tax Detail",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"isSingle": 0,
|
||||||
|
"isChild": 1,
|
||||||
|
"keywordFields": [],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "account",
|
||||||
|
"label": "Tax Account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"target": "Account",
|
||||||
|
"required": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "rate",
|
||||||
|
"label": "Rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"required": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
28
models/doctype/TaxSummary/TaxSummary.js
Normal file
28
models/doctype/TaxSummary/TaxSummary.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
module.exports = {
|
||||||
|
"name": "TaxSummary",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"isSingle": 0,
|
||||||
|
"isChild": 1,
|
||||||
|
"keywordFields": [],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "account",
|
||||||
|
"label": "Tax Account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"target": "Account",
|
||||||
|
"required": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "rate",
|
||||||
|
"label": "Rate",
|
||||||
|
"fieldtype": "Float",
|
||||||
|
"required": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amount",
|
||||||
|
"label": "Amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"required": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,26 +1,36 @@
|
|||||||
const frappe = require('frappejs');
|
|
||||||
const BaseMeta = require('frappejs/model/meta');
|
|
||||||
const BaseDocument = require('frappejs/model/document');
|
|
||||||
|
|
||||||
class AccountMeta extends BaseMeta {
|
|
||||||
setupMeta() {
|
|
||||||
Object.assign(this, require('./account.json'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Account extends BaseDocument {
|
|
||||||
async validate() {
|
|
||||||
if (!this.account_type) {
|
|
||||||
if (this.parent_account) {
|
|
||||||
this.account_type = await frappe.db.getValue('Account', this.parent_account, 'account_type');
|
|
||||||
} else {
|
|
||||||
this.account_type = 'Asset';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Document: Account,
|
"name": "Account",
|
||||||
Meta: AccountMeta
|
"doctype": "DocType",
|
||||||
};
|
"documentClass": require("./AccountDocument.js"),
|
||||||
|
"isSingle": 0,
|
||||||
|
"keywordFields": [
|
||||||
|
"name",
|
||||||
|
"account_type"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "name",
|
||||||
|
"label": "Account Name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"required": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "parent_account",
|
||||||
|
"label": "Parent Account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"target": "Account"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "account_type",
|
||||||
|
"label": "Account Type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": [
|
||||||
|
"Asset",
|
||||||
|
"Liability",
|
||||||
|
"Equity",
|
||||||
|
"Income",
|
||||||
|
"Expense"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Account",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"isSingle": 0,
|
|
||||||
"keywordFields": [
|
|
||||||
"name",
|
|
||||||
"account_type"
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldname": "name",
|
|
||||||
"label": "Account Name",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"required": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "parent_account",
|
|
||||||
"label": "Parent Account",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"target": "Account"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "account_type",
|
|
||||||
"label": "Account Type",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"options": [
|
|
||||||
"Asset",
|
|
||||||
"Liability",
|
|
||||||
"Equity",
|
|
||||||
"Income",
|
|
||||||
"Expense"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
const BaseList = require('frappejs/client/view/list');
|
|
||||||
const BaseForm = require('frappejs/client/view/form');
|
|
||||||
|
|
||||||
class AccountList extends BaseList {
|
|
||||||
getFields() {
|
|
||||||
return ['name', 'account_type'];
|
|
||||||
}
|
|
||||||
getRowHTML(data) {
|
|
||||||
return `<a href="#edit/account/${data.name}">${data.name} (${data.account_type})</a>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AccountForm extends BaseForm {
|
|
||||||
make() {
|
|
||||||
super.make();
|
|
||||||
|
|
||||||
// override controller event
|
|
||||||
this.controls['parent_account'].getFilters = (query) => {
|
|
||||||
return {
|
|
||||||
keywords: ["like", query],
|
|
||||||
name: ["!=", this.doc.name]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Form: AccountForm,
|
|
||||||
List: AccountList
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Document</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src="js/bundle.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,16 +1,17 @@
|
|||||||
const BaseMeta = require('frappejs/model/meta');
|
|
||||||
const BaseDocument = require('frappejs/model/document');
|
|
||||||
|
|
||||||
class CustomerMeta extends BaseMeta {
|
|
||||||
setupMeta() {
|
|
||||||
Object.assign(this, require('./customer.json'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Customer extends BaseDocument {
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Document: Customer,
|
"name": "Customer",
|
||||||
Meta: CustomerMeta
|
"doctype": "DocType",
|
||||||
};
|
"isSingle": 0,
|
||||||
|
"istable": 0,
|
||||||
|
"keywordFields": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "name",
|
||||||
|
"label": "Name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"required": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Customer",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"isSingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"keywordFields": [
|
|
||||||
"name"
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldname": "name",
|
|
||||||
"label": "Name",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"required": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,16 +1,53 @@
|
|||||||
const BaseMeta = require('frappejs/model/meta');
|
|
||||||
const BaseDocument = require('frappejs/model/document');
|
|
||||||
|
|
||||||
class InvoiceMeta extends BaseMeta {
|
|
||||||
setupMeta() {
|
|
||||||
Object.assign(this, require('./invoice.json'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Invoice extends BaseDocument {
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Document: Invoice,
|
"name": "Invoice",
|
||||||
Meta: InvoiceMeta
|
"doctype": "DocType",
|
||||||
};
|
"documentClass": require("./InvoiceDocument.js"),
|
||||||
|
"isSingle": 0,
|
||||||
|
"istable": 0,
|
||||||
|
"keywordFields": ["name", "customer"],
|
||||||
|
"settings": "InvoiceSettings",
|
||||||
|
"showTitle": true,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "date",
|
||||||
|
"label": "Date",
|
||||||
|
"fieldtype": "Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "customer",
|
||||||
|
"label": "Customer",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"target": "Customer",
|
||||||
|
"required": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "items",
|
||||||
|
"label": "Items",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"childtype": "InvoiceItem",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "netTotal",
|
||||||
|
"label": "Total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
formula: (doc) => doc.getSum('items', 'amount'),
|
||||||
|
"disabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "taxes",
|
||||||
|
"label": "Taxes",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"childtype": "TaxSummary",
|
||||||
|
"disabled": true,
|
||||||
|
"template": `<div></div>`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "grandTotal",
|
||||||
|
"label": "Total",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
formula: (doc) => doc.getGrandTotal(),
|
||||||
|
"disabled": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Invoice",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"isSingle": 0,
|
|
||||||
"istable": 0,
|
|
||||||
"keywordFields": [],
|
|
||||||
"settings": "Invoice Settings",
|
|
||||||
"showTitle": true,
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldname": "date",
|
|
||||||
"label": "Date",
|
|
||||||
"fieldtype": "Date"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "customer",
|
|
||||||
"label": "Customer",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"target": "Customer",
|
|
||||||
"required": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "items",
|
|
||||||
"label": "Items",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"childtype": "Invoice Item",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "total",
|
|
||||||
"label": "Total",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"formula": "doc.getSum('items', 'amount')",
|
|
||||||
"required": true,
|
|
||||||
"disabled": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
const BaseMeta = require('frappejs/model/meta');
|
|
||||||
const BaseDocument = require('frappejs/model/document');
|
|
||||||
|
|
||||||
class InvoiceItemMeta extends BaseMeta {
|
|
||||||
setupMeta() {
|
|
||||||
Object.assign(this, require('./invoice_item.json'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InvoiceItem extends BaseDocument {
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Document: InvoiceItem,
|
|
||||||
Meta: InvoiceItemMeta
|
|
||||||
};
|
|
@ -1,16 +0,0 @@
|
|||||||
const BaseMeta = require('frappejs/model/meta');
|
|
||||||
const BaseDocument = require('frappejs/model/document');
|
|
||||||
|
|
||||||
class InvoiceSettingsMeta extends BaseMeta {
|
|
||||||
setupMeta() {
|
|
||||||
Object.assign(this, require('./invoice_settings.json'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InvoiceSettings extends BaseDocument {
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
Document: InvoiceSettings,
|
|
||||||
Meta: InvoiceSettingsMeta
|
|
||||||
};
|
|
@ -1,16 +1,45 @@
|
|||||||
const BaseMeta = require('frappejs/model/meta');
|
|
||||||
const BaseDocument = require('frappejs/model/document');
|
|
||||||
|
|
||||||
class ItemMeta extends BaseMeta {
|
|
||||||
setupMeta() {
|
|
||||||
Object.assign(this, require('./item.json'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Item extends BaseDocument {
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Document: Item,
|
"name": "Item",
|
||||||
Meta: ItemMeta
|
"doctype": "DocType",
|
||||||
};
|
"isSingle": 0,
|
||||||
|
"keywordFields": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "name",
|
||||||
|
"label": "Item Name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"required": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"label": "Description",
|
||||||
|
"fieldtype": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "unit",
|
||||||
|
"label": "Unit",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"options": [
|
||||||
|
"No",
|
||||||
|
"Kg",
|
||||||
|
"Gram",
|
||||||
|
"Hour",
|
||||||
|
"Day"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "tax",
|
||||||
|
"label": "Tax",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"target": "Tax"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "rate",
|
||||||
|
"label": "Rate",
|
||||||
|
"fieldtype": "Currency"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Item",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"isSingle": 0,
|
|
||||||
"keywordFields": [
|
|
||||||
"name",
|
|
||||||
"description"
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldname": "name",
|
|
||||||
"label": "Item Name",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"required": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "description",
|
|
||||||
"label": "Description",
|
|
||||||
"fieldtype": "Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "unit",
|
|
||||||
"label": "Unit",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"options": [
|
|
||||||
"No",
|
|
||||||
"Kg",
|
|
||||||
"Gram",
|
|
||||||
"Hour",
|
|
||||||
"Day"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "rate",
|
|
||||||
"label": "Rate",
|
|
||||||
"fieldtype": "Currency"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
11
models/index.js
Normal file
11
models/index.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = {
|
||||||
|
Account: require('./doctype/Account/Account.js'),
|
||||||
|
Customer: require('./doctype/Customer/Customer.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'),
|
||||||
|
Tax: require('./doctype/Tax/Tax.js'),
|
||||||
|
TaxDetail: require('./doctype/TaxDetail/TaxDetail.js'),
|
||||||
|
TaxSummary: require('./doctype/TaxSummary/TaxSummary.js')
|
||||||
|
}
|
@ -3,6 +3,10 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"test": "mocha tests",
|
||||||
|
"start": "nodemon server.js"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"awesomplete": "^1.1.2",
|
"awesomplete": "^1.1.2",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
|
@ -3,7 +3,7 @@ const path = require('path');
|
|||||||
|
|
||||||
server.start({
|
server.start({
|
||||||
backend: 'sqlite',
|
backend: 'sqlite',
|
||||||
connection_params: {dbPath: 'test.db'},
|
connectionParams: {dbPath: 'test.db'},
|
||||||
static: './',
|
static: './',
|
||||||
models_path: path.resolve(__dirname, './models')
|
models: require('./models')
|
||||||
});
|
});
|
28
src/index.js
28
src/index.js
@ -7,26 +7,20 @@ client.start({
|
|||||||
}).then(() => {
|
}).then(() => {
|
||||||
|
|
||||||
// require modules
|
// require modules
|
||||||
frappe.modules.todo = require('frappejs/models/doctype/todo/todo.js');
|
frappe.registerModels(require('../models'));
|
||||||
frappe.modules.number_series = require('frappejs/models/doctype/number_series/number_series.js');
|
|
||||||
frappe.modules.account = require('../models/doctype/account/account.js');
|
|
||||||
frappe.modules.item = require('../models/doctype/item/item.js');
|
|
||||||
frappe.modules.customer = require('../models/doctype/customer/customer.js');
|
|
||||||
frappe.modules.invoice = require('../models/doctype/invoice/invoice.js');
|
|
||||||
frappe.modules.invoice_item = require('../models/doctype/invoice_item/invoice_item.js');
|
|
||||||
frappe.modules.invoice_settings = require('../models/doctype/invoice_settings/invoice_settings.js');
|
|
||||||
|
|
||||||
frappe.modules.todo_client = require('frappejs/models/doctype/todo/todo_client.js');
|
frappe.registerView('List', 'ToDo', require('frappejs/models/doctype/ToDo/ToDoList.js'));
|
||||||
frappe.modules.account_client = require('../models/doctype/account/account_client.js');
|
frappe.registerView('List', 'Account', require('../models/doctype/Account/AccountList.js'));
|
||||||
frappe.modules.invoice_client = require('../models/doctype/invoice/invoiceClient.js');
|
frappe.registerView('Form', 'Account', require('../models/doctype/Account/AccountForm.js'));
|
||||||
|
frappe.registerView('List', 'Invoice', require('../models/doctype/Invoice/InvoiceList.js'));
|
||||||
|
|
||||||
frappe.desk.menu.addItem('ToDo', '#list/todo');
|
frappe.desk.menu.addItem('ToDo', '#list/ToDo');
|
||||||
frappe.desk.menu.addItem('Accounts', '#list/account');
|
frappe.desk.menu.addItem('Accounts', '#list/Account');
|
||||||
frappe.desk.menu.addItem('Items', '#list/item');
|
frappe.desk.menu.addItem('Items', '#list/Item');
|
||||||
frappe.desk.menu.addItem('Customers', '#list/customer');
|
frappe.desk.menu.addItem('Customers', '#list/Customer');
|
||||||
frappe.desk.menu.addItem('Invoice', '#list/invoice');
|
frappe.desk.menu.addItem('Invoice', '#list/Invoice');
|
||||||
|
|
||||||
frappe.router.default = '#list/todo';
|
frappe.router.default = '#list/ToDo';
|
||||||
|
|
||||||
frappe.router.show(window.location.hash);
|
frappe.router.show(window.location.hash);
|
||||||
});
|
});
|
||||||
|
57
tests/testInvoice.js
Normal file
57
tests/testInvoice.js
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
const assert = require('assert');
|
||||||
|
const frappe = require('frappejs');
|
||||||
|
const helpers = require('frappejs/tests/helpers');
|
||||||
|
const path = require('path');
|
||||||
|
const models = require('../models');
|
||||||
|
|
||||||
|
async function makeFixtures() {
|
||||||
|
if (!(await frappe.db.exists('Customer', 'Test Customer'))) {
|
||||||
|
await frappe.insert({doctype:'Customer', name:'Test Customer'})
|
||||||
|
await frappe.insert({doctype:'Item', name:'Test Item 1', description:'Test Item Description 1', unit:'No', rate: 100})
|
||||||
|
await frappe.insert({doctype:'Item', name:'Test Item 2', description:'Test Item Description 2', unit:'No', rate: 200})
|
||||||
|
await frappe.insert({doctype:'Account', name:'GST', parent_account: 'Liabilities'});
|
||||||
|
await frappe.insert({doctype:'Tax', name:'GST',
|
||||||
|
details: [{account: 'GST', rate:10}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Invoice', () => {
|
||||||
|
before(async function() {
|
||||||
|
await helpers.initSqlite({models: models});
|
||||||
|
await makeFixtures();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show create an invoice', async () => {
|
||||||
|
let invoice = await frappe.insert({
|
||||||
|
doctype:'Invoice',
|
||||||
|
customer: 'Test Customer',
|
||||||
|
items: [
|
||||||
|
{item: 'Test Item 1', quantity: 5},
|
||||||
|
{item: 'Test Item 2', quantity: 7},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(invoice.items[0].amount, 500);
|
||||||
|
assert.equal(invoice.items[1].amount, 1400);
|
||||||
|
assert.equal(invoice.netTotal, 1900);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('show create an invoice with tax', async () => {
|
||||||
|
let invoice = await frappe.insert({
|
||||||
|
doctype:'Invoice',
|
||||||
|
customer: 'Test Customer',
|
||||||
|
items: [
|
||||||
|
{item: 'Test Item 1', quantity: 5, tax: 'GST'},
|
||||||
|
{item: 'Test Item 2', quantity: 7, tax: 'GST'},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(invoice.items[0].amount, 500);
|
||||||
|
assert.equal(invoice.items[1].amount, 1400);
|
||||||
|
assert.equal(invoice.netTotal, 1900);
|
||||||
|
assert.equal(invoice.taxes[0].amount, 190);
|
||||||
|
assert.equal(invoice.grandTotal, 2090);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user