mirror of
https://github.com/frappe/books.git
synced 2024-11-09 15:20:56 +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
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
test.db
|
||||
*test.db
|
||||
*.log
|
||||
.cache
|
||||
/temp
|
7
dist/css/style.css
vendored
7
dist/css/style.css
vendored
@ -6824,7 +6824,6 @@ html {
|
||||
.hide {
|
||||
display: none !important; }
|
||||
.page {
|
||||
max-width: 500px;
|
||||
padding-bottom: 2rem; }
|
||||
.page .page-head {
|
||||
padding: 0.5rem 1rem;
|
||||
@ -6839,8 +6838,7 @@ html {
|
||||
text-align: center;
|
||||
padding: 200px 0px; }
|
||||
.form-body {
|
||||
padding: 1rem;
|
||||
max-width: 500px; }
|
||||
padding: 1rem; }
|
||||
.form-body .form-control.font-weight-bold {
|
||||
background-color: lightyellow; }
|
||||
.form-body .alert {
|
||||
@ -6888,8 +6886,9 @@ mark {
|
||||
margin-top: 0.5rem; }
|
||||
.data-table .body-scrollable {
|
||||
border-bottom: 0px !important; }
|
||||
.data-table .body-scrollable tr:first-child .data-table-col {
|
||||
border-top: 0px !important; }
|
||||
.data-table thead td {
|
||||
border-bottom: 0px !important;
|
||||
background-color: #e9ecef !important; }
|
||||
.data-table .data-table-col .edit-cell {
|
||||
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 frappe = require('frappejs');
|
||||
|
||||
class InvoiceList extends BaseList {
|
||||
module.exports = class InvoiceList extends BaseList {
|
||||
getFields() {
|
||||
return ['name', 'customer', 'total'];
|
||||
}
|
||||
@ -10,8 +10,4 @@ class InvoiceList extends BaseList {
|
||||
<div class="col-5 text-muted">${data.customer}</div>
|
||||
<div class="col-4 text-muted text-right">${frappe.format(data.total, {fieldtype:"Currency"})}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
List: InvoiceList
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "Invoice Item",
|
||||
module.exports = {
|
||||
"name": "InvoiceItem",
|
||||
"doctype": "DocType",
|
||||
"isSingle": 0,
|
||||
"isChild": 1,
|
||||
@ -16,7 +16,7 @@
|
||||
"fieldname": "description",
|
||||
"label": "Description",
|
||||
"fieldtype": "Text",
|
||||
"formula": "doc.getFrom('Item', row.item, 'description')",
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'description'),
|
||||
"required": 1
|
||||
},
|
||||
{
|
||||
@ -30,14 +30,27 @@
|
||||
"label": "Rate",
|
||||
"fieldtype": "Currency",
|
||||
"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",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"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 @@
|
||||
{
|
||||
"name": "Invoice Settings",
|
||||
module.exports = {
|
||||
"name": "InvoiceSettings",
|
||||
"label": "Invoice Settings",
|
||||
"doctype": "DocType",
|
||||
"isSingle": 1,
|
||||
"isChild": 0,
|
||||
"keywordFields": [],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "number_series",
|
||||
"fieldname": "numberSeries",
|
||||
"label": "Number Series",
|
||||
"fieldtype": "Link",
|
||||
"target": "Number Series",
|
||||
"target": "NumberSeries",
|
||||
"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 = {
|
||||
Document: Account,
|
||||
Meta: AccountMeta
|
||||
};
|
||||
"name": "Account",
|
||||
"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 = {
|
||||
Document: Customer,
|
||||
Meta: CustomerMeta
|
||||
};
|
||||
"name": "Customer",
|
||||
"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 = {
|
||||
Document: Invoice,
|
||||
Meta: InvoiceMeta
|
||||
};
|
||||
"name": "Invoice",
|
||||
"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 = {
|
||||
Document: Item,
|
||||
Meta: ItemMeta
|
||||
};
|
||||
"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": "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",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "mocha tests",
|
||||
"start": "nodemon server.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"awesomplete": "^1.1.2",
|
||||
"body-parser": "^1.18.2",
|
||||
|
@ -3,7 +3,7 @@ const path = require('path');
|
||||
|
||||
server.start({
|
||||
backend: 'sqlite',
|
||||
connection_params: {dbPath: 'test.db'},
|
||||
connectionParams: {dbPath: 'test.db'},
|
||||
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(() => {
|
||||
|
||||
// require modules
|
||||
frappe.modules.todo = require('frappejs/models/doctype/todo/todo.js');
|
||||
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.registerModels(require('../models'));
|
||||
|
||||
frappe.modules.todo_client = require('frappejs/models/doctype/todo/todo_client.js');
|
||||
frappe.modules.account_client = require('../models/doctype/account/account_client.js');
|
||||
frappe.modules.invoice_client = require('../models/doctype/invoice/invoiceClient.js');
|
||||
frappe.registerView('List', 'ToDo', require('frappejs/models/doctype/ToDo/ToDoList.js'));
|
||||
frappe.registerView('List', 'Account', require('../models/doctype/Account/AccountList.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('Accounts', '#list/account');
|
||||
frappe.desk.menu.addItem('Items', '#list/item');
|
||||
frappe.desk.menu.addItem('Customers', '#list/customer');
|
||||
frappe.desk.menu.addItem('Invoice', '#list/invoice');
|
||||
frappe.desk.menu.addItem('ToDo', '#list/ToDo');
|
||||
frappe.desk.menu.addItem('Accounts', '#list/Account');
|
||||
frappe.desk.menu.addItem('Items', '#list/Item');
|
||||
frappe.desk.menu.addItem('Customers', '#list/Customer');
|
||||
frappe.desk.menu.addItem('Invoice', '#list/Invoice');
|
||||
|
||||
frappe.router.default = '#list/todo';
|
||||
frappe.router.default = '#list/ToDo';
|
||||
|
||||
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