mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
Reports and Models
- Models - Bill - Quotation (extended from Invoice) - Journal Entry - Reports - Sales Register - Purchase Register
This commit is contained in:
parent
8adb52c57c
commit
17e5187c51
@ -21,7 +21,7 @@ module.exports = class LedgerPosting {
|
||||
if (!this.entryMap[account]) {
|
||||
const entry = {
|
||||
account: account,
|
||||
party: this.party,
|
||||
party: this.party || '',
|
||||
date: this.date || this.reference.date,
|
||||
referenceType: referenceType || this.reference.doctype,
|
||||
referenceName: referenceName || this.reference.name,
|
||||
|
@ -16,13 +16,18 @@ module.exports = {
|
||||
frappe.desk.menu.addItem('Customers', '#list/Customer');
|
||||
frappe.desk.menu.addItem('Quotation', '#list/Quotation');
|
||||
frappe.desk.menu.addItem('Invoice', '#list/Invoice');
|
||||
frappe.desk.menu.addItem('Bill', '#list/Bill');
|
||||
frappe.desk.menu.addItem('Journal Entry', '#list/JournalEntry');
|
||||
frappe.desk.menu.addItem('Address', "#list/Address");
|
||||
frappe.desk.menu.addItem('Contact', "#list/Contact");
|
||||
frappe.desk.menu.addItem('Settings', () => frappe.desk.showFormModal('SystemSettings'));
|
||||
|
||||
// reports
|
||||
frappe.desk.menu.addItem('General Ledger', '#report/general-ledger');
|
||||
frappe.desk.menu.addItem('Profit And Loss', '#report/profit-and-loss');
|
||||
frappe.desk.menu.addItem('Balance Sheet', '#report/balance-sheet');
|
||||
frappe.desk.menu.addItem('Sales Register', '#report/sales-register');
|
||||
frappe.desk.menu.addItem('Purchase Register', '#report/purchase-register');
|
||||
|
||||
frappe.router.default = '#tree/Account';
|
||||
|
||||
|
@ -32,8 +32,7 @@ module.exports = {
|
||||
fieldname: "party",
|
||||
label: "Party",
|
||||
fieldtype: "Link",
|
||||
target: "Party",
|
||||
required: 1
|
||||
target: "Party"
|
||||
},
|
||||
{
|
||||
fieldname: "debit",
|
||||
@ -48,8 +47,7 @@ module.exports = {
|
||||
{
|
||||
fieldname: "againstAccount",
|
||||
label: "Against Account",
|
||||
fieldtype: "Text",
|
||||
required: 0
|
||||
fieldtype: "Text"
|
||||
},
|
||||
{
|
||||
fieldname: "referenceType",
|
||||
|
138
models/doctype/Bill/Bill.js
Normal file
138
models/doctype/Bill/Bill.js
Normal file
@ -0,0 +1,138 @@
|
||||
const frappe = require('frappejs');
|
||||
const utils = require('../../../accounting/utils');
|
||||
|
||||
module.exports = {
|
||||
"name": "Bill",
|
||||
"doctype": "DocType",
|
||||
"documentClass": require('./BillDocument'),
|
||||
"isSingle": 0,
|
||||
"isChild": 0,
|
||||
"isSubmittable": 1,
|
||||
"keywordFields": ["name", "supplier"],
|
||||
"settings": "BillSettings",
|
||||
"showTitle": true,
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "date",
|
||||
"label": "Date",
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier",
|
||||
"label": "Supplier",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"required": 1,
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
supplier: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
isGroup: 0,
|
||||
accountType: "Payable"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
"label": "Items",
|
||||
"fieldtype": "Table",
|
||||
"childtype": "BillItem",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "netTotal",
|
||||
"label": "Net Total",
|
||||
"fieldtype": "Currency",
|
||||
formula: (doc) => doc.getSum('items', 'amount'),
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"label": "Taxes",
|
||||
"fieldtype": "Table",
|
||||
"childtype": "TaxSummary",
|
||||
"disabled": true,
|
||||
template: (doc, row) => {
|
||||
return `<div class='row'>
|
||||
<div class='col-6'><!-- empty left side --></div>
|
||||
<div class='col-6'>${(doc.taxes || []).map(row => {
|
||||
return `<div class='row'>
|
||||
<div class='col-6'>${row.account} (${row.rate}%)</div>
|
||||
<div class='col-6 text-right'>
|
||||
${frappe.format(row.amount, 'Currency')}
|
||||
</div>
|
||||
</div>`
|
||||
}).join('')}
|
||||
</div></div>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "grandTotal",
|
||||
"label": "Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
formula: (doc) => doc.getGrandTotal(),
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"label": "Terms",
|
||||
"fieldtype": "Text"
|
||||
}
|
||||
],
|
||||
|
||||
layout: [
|
||||
// section 1
|
||||
{
|
||||
columns: [
|
||||
{ fields: [ "supplier", "account" ] },
|
||||
{ fields: [ "date" ] }
|
||||
]
|
||||
},
|
||||
|
||||
// section 2
|
||||
{ fields: [ "items" ] },
|
||||
|
||||
// section 3
|
||||
{ fields: [ "netTotal", "taxes", "grandTotal" ] },
|
||||
|
||||
// section 4
|
||||
{ fields: [ "terms" ] },
|
||||
],
|
||||
|
||||
links: [
|
||||
{
|
||||
label: 'Make Payment',
|
||||
condition: form => form.doc.submitted,
|
||||
action: async form => {
|
||||
const payment = await frappe.getNewDoc('Payment');
|
||||
payment.party = form.doc.supplier,
|
||||
payment.account = form.doc.account,
|
||||
payment.for = [{referenceType: form.doc.doctype, referenceName: form.doc.name, amount: form.doc.grandTotal}]
|
||||
const formModal = await frappe.desk.showFormModal('Payment', payment.name);
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
listSettings: {
|
||||
getFields(list) {
|
||||
return ['name', 'supplier', 'grandTotal', 'submitted'];
|
||||
},
|
||||
|
||||
getRowHTML(list, data) {
|
||||
return `<div class="col-3">${list.getNameHTML(data)}</div>
|
||||
<div class="col-4 text-muted">${data.supplier}</div>
|
||||
<div class="col-4 text-muted text-right">${frappe.format(data.grandTotal, "Currency")}</div>`;
|
||||
}
|
||||
}
|
||||
}
|
4
models/doctype/Bill/BillDocument.js
Normal file
4
models/doctype/Bill/BillDocument.js
Normal file
@ -0,0 +1,4 @@
|
||||
const InvoiceDocument = require('../Invoice/InvoiceDocument');
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = class Bill extends InvoiceDocument { }
|
29
models/doctype/Bill/BillServer.js
Normal file
29
models/doctype/Bill/BillServer.js
Normal file
@ -0,0 +1,29 @@
|
||||
const frappe = require('frappejs');
|
||||
const Bill = require('./BillDocument');
|
||||
const LedgerPosting = rootRequire('accounting/ledgerPosting');
|
||||
|
||||
module.exports = class BillServer extends Bill {
|
||||
getPosting() {
|
||||
let entries = new LedgerPosting({reference: this, party: this.supplier});
|
||||
entries.credit(this.account, this.grandTotal);
|
||||
|
||||
for (let item of this.items) {
|
||||
entries.debit(item.account, item.amount);
|
||||
}
|
||||
|
||||
if (this.taxes) {
|
||||
for (let tax of this.taxes) {
|
||||
entries.debit(tax.account, tax.amount);
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
async afterSubmit() {
|
||||
await this.getPosting().post();
|
||||
}
|
||||
|
||||
async afterRevert() {
|
||||
await this.getPosting().postReverse();
|
||||
}
|
||||
}
|
67
models/doctype/BillItem/BillItem.js
Normal file
67
models/doctype/BillItem/BillItem.js
Normal file
@ -0,0 +1,67 @@
|
||||
module.exports = {
|
||||
name: "BillItem",
|
||||
doctype: "DocType",
|
||||
isSingle: 0,
|
||||
isChild: 1,
|
||||
keywordFields: [],
|
||||
layout: 'ratio',
|
||||
fields: [
|
||||
{
|
||||
"fieldname": "item",
|
||||
"label": "Item",
|
||||
"fieldtype": "Link",
|
||||
"target": "Item",
|
||||
"required": 1,
|
||||
width: 2
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"label": "Description",
|
||||
"fieldtype": "Text",
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'description'),
|
||||
hidden: 1
|
||||
},
|
||||
{
|
||||
"fieldname": "quantity",
|
||||
"label": "Quantity",
|
||||
"fieldtype": "Float",
|
||||
"required": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Currency",
|
||||
"required": 1,
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'rate')
|
||||
},
|
||||
{
|
||||
fieldname: "account",
|
||||
label: "Account",
|
||||
hidden: 1,
|
||||
fieldtype: "Link",
|
||||
target: "Account",
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'expenseAccount')
|
||||
},
|
||||
{
|
||||
"fieldname": "tax",
|
||||
"label": "Tax",
|
||||
"fieldtype": "Link",
|
||||
"target": "Tax",
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'tax')
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"disabled": 1,
|
||||
formula: (row, doc) => row.quantity * row.rate
|
||||
},
|
||||
{
|
||||
"fieldname": "taxAmount",
|
||||
"label": "Tax Amount",
|
||||
"hidden": 1,
|
||||
"fieldtype": "Text",
|
||||
formula: (row, doc) => doc.getRowTax(row)
|
||||
}
|
||||
]
|
||||
}
|
18
models/doctype/BillSettings/BillSettings.js
Normal file
18
models/doctype/BillSettings/BillSettings.js
Normal file
@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
"name": "BillSettings",
|
||||
"label": "Bill Settings",
|
||||
"doctype": "DocType",
|
||||
"isSingle": 1,
|
||||
"isChild": 0,
|
||||
"keywordFields": [],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "numberSeries",
|
||||
"label": "Number Series",
|
||||
"fieldtype": "Link",
|
||||
"target": "NumberSeries",
|
||||
"required": 1,
|
||||
"default": "BILL"
|
||||
}
|
||||
]
|
||||
}
|
@ -25,7 +25,13 @@ module.exports = {
|
||||
"label": "Customer",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"required": 1
|
||||
"required": 1,
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
customer: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
@ -35,7 +41,8 @@ module.exports = {
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
isGroup: 0
|
||||
isGroup: 0,
|
||||
accountType: "Receivable"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9,7 +9,7 @@ module.exports = {
|
||||
isSubmittable: 1,
|
||||
keywordFields: ["name"],
|
||||
showTitle: true,
|
||||
settings: "JournalEntrySetting",
|
||||
settings: "JournalEntrySettings",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "date",
|
||||
|
@ -1,20 +1,20 @@
|
||||
const JournalEntry = require('./JournalEntry');
|
||||
const frappe = require('frappejs');
|
||||
const BaseDocument = require('frappejs/model/document');
|
||||
const LedgerPosting = rootRequire('accounting/ledgerPosting');
|
||||
|
||||
module.exports = class JournalEntryServer extends BaseDocument {
|
||||
/**
|
||||
|
||||
getPosting() {
|
||||
let entries = new LedgerPosting({reference: this, party: this.party});
|
||||
entries.debit(this.paymentAccount, this.amount);
|
||||
let entries = new LedgerPosting({reference: this });
|
||||
|
||||
for (let row of this.for) {
|
||||
entries.credit(this.account, row.amount, row.referenceType, row.referenceName);
|
||||
for (let row of this.accounts) {
|
||||
if (row.debit) {
|
||||
entries.debit(row.account, row.debit);
|
||||
} else if (row.credit) {
|
||||
entries.credit(row.account, row.credit);
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
|
||||
}
|
||||
|
||||
async afterSubmit() {
|
||||
@ -24,6 +24,4 @@ module.exports = class JournalEntryServer extends BaseDocument {
|
||||
async afterRevert() {
|
||||
await this.getPosting().postReverse();
|
||||
}
|
||||
|
||||
**/
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,12 @@ module.exports = {
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
"required": 1,
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
isGroup: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "debit",
|
||||
|
@ -1,10 +1,20 @@
|
||||
const deepmerge = require('deepmerge');
|
||||
const model = require('frappejs/model');
|
||||
const Invoice = require('../Invoice/Invoice');
|
||||
|
||||
const Quotation = deepmerge(Invoice, {
|
||||
const Quotation = model.extend(Invoice, {
|
||||
name: "Quotation",
|
||||
label: "Quotation",
|
||||
settings: "QuotationSettings"
|
||||
settings: "QuotationSettings",
|
||||
fields: [
|
||||
{
|
||||
"fieldname": "items",
|
||||
"childtype": "QuotationItem"
|
||||
}
|
||||
],
|
||||
links: []
|
||||
}, {
|
||||
skipFields: ['account'],
|
||||
overrideProps: ['links']
|
||||
});
|
||||
|
||||
module.exports = Quotation;
|
||||
|
3
models/doctype/Quotation/QuotationDocument.js
Normal file
3
models/doctype/Quotation/QuotationDocument.js
Normal file
@ -0,0 +1,3 @@
|
||||
const InvoiceDocument = require('../Invoice/InvoiceDocument');
|
||||
|
||||
module.exports = class Quotation extends InvoiceDocument { }
|
6
models/doctype/QuotationItem/QuotationItem.js
Normal file
6
models/doctype/QuotationItem/QuotationItem.js
Normal file
@ -0,0 +1,6 @@
|
||||
const model = require('frappejs/model');
|
||||
const InvoiceItem = require('../InvoiceItem/InvoiceItem');
|
||||
|
||||
module.exports = model.extend(InvoiceItem, {
|
||||
name: "QuotationItem"
|
||||
});
|
@ -1,11 +1,13 @@
|
||||
const deepmerge = require('deepmerge');
|
||||
const model = require('frappejs/model');
|
||||
const InvoiceSettings = require('../InvoiceSettings/InvoiceSettings');
|
||||
const QuotationSettings = deepmerge(InvoiceSettings, {
|
||||
|
||||
module.exports = model.extend(InvoiceSettings, {
|
||||
"name": "QuotationSettings",
|
||||
"label": "Quotation Settings",
|
||||
"fields": {
|
||||
"default": "INV"
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = QuotationSettings;
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "numberSeries",
|
||||
"default": "QTN"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
@ -15,6 +15,10 @@ module.exports = {
|
||||
InvoiceItem: require('./doctype/InvoiceItem/InvoiceItem.js'),
|
||||
InvoiceSettings: require('./doctype/InvoiceSettings/InvoiceSettings.js'),
|
||||
|
||||
Bill: require('./doctype/Bill/Bill.js'),
|
||||
BillItem: require('./doctype/BillItem/BillItem.js'),
|
||||
BillSettings: require('./doctype/BillSettings/BillSettings.js'),
|
||||
|
||||
Tax: require('./doctype/Tax/Tax.js'),
|
||||
TaxDetail: require('./doctype/TaxDetail/TaxDetail.js'),
|
||||
TaxSummary: require('./doctype/TaxSummary/TaxSummary.js'),
|
||||
@ -27,6 +31,7 @@ module.exports = {
|
||||
JournalEntrySettings: require('./doctype/JournalEntrySettings/JournalEntrySettings.js'),
|
||||
|
||||
Quotation: require('./doctype/Quotation/Quotation.js'),
|
||||
QuotationItem: require('./doctype/QuotationItem/QuotationItem.js'),
|
||||
QuotationSettings: require('./doctype/QuotationSettings/QuotationSettings.js'),
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,4 @@ class BalanceSheet {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function execute(params) {
|
||||
return new BalanceSheet().run(params);
|
||||
}
|
||||
module.exports = BalanceSheet;
|
||||
|
@ -7,10 +7,18 @@ module.exports = class BalanceSheetView extends FinancialStatementsView {
|
||||
title: frappe._('Balance Sheet'),
|
||||
method: 'balance-sheet',
|
||||
filterFields: [
|
||||
{fieldtype: 'Date', label: 'To Date', required: 1},
|
||||
{fieldtype: 'Date', fieldname: 'toDate', label: 'To Date', required: 1},
|
||||
{fieldtype: 'Select', options: ['Monthly', 'Quarterly', 'Half Yearly', 'Yearly'],
|
||||
label: 'Periodicity', fieldname: 'periodicity', default: 'Monthly'}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async setDefaultFilterValues() {
|
||||
const accountingSettings = await frappe.getSingle('AccountingSettings');
|
||||
this.filters.setValue('toDate', accountingSettings.fiscalYearEnd);
|
||||
this.filters.setValue('periodicity', 'Monthly');
|
||||
|
||||
this.run();
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,4 @@ class ProfitAndLoss {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function execute(params) {
|
||||
return new ProfitAndLoss().run(params);
|
||||
}
|
||||
module.exports = ProfitAndLoss;
|
||||
|
@ -7,11 +7,20 @@ module.exports = class ProfitAndLossView extends FinancialStatementsView {
|
||||
title: frappe._('Profit and Loss'),
|
||||
method: 'profit-and-loss',
|
||||
filterFields: [
|
||||
{fieldtype: 'Date', label: 'From Date', required: 1},
|
||||
{fieldtype: 'Date', label: 'To Date', required: 1},
|
||||
{fieldtype: 'Date', fieldname: 'fromDate', label: 'From Date', required: 1},
|
||||
{fieldtype: 'Date', fieldname: 'toDate', label: 'To Date', required: 1},
|
||||
{fieldtype: 'Select', options: ['Monthly', 'Quarterly', 'Half Yearly', 'Yearly'],
|
||||
label: 'Periodicity', fieldname: 'periodicity', default: 'Monthly'}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async setDefaultFilterValues() {
|
||||
const accountingSettings = await frappe.getSingle('AccountingSettings');
|
||||
this.filters.setValue('fromDate', accountingSettings.fiscalYearStart);
|
||||
this.filters.setValue('toDate', accountingSettings.fiscalYearEnd);
|
||||
this.filters.setValue('periodicity', 'Monthly');
|
||||
|
||||
this.run();
|
||||
}
|
||||
}
|
||||
|
43
reports/PurchaseRegister/PurchaseRegister.js
Normal file
43
reports/PurchaseRegister/PurchaseRegister.js
Normal file
@ -0,0 +1,43 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
class PurchaseRegister {
|
||||
async run({ fromDate, toDate }) {
|
||||
const bills = await frappe.db.getAll({
|
||||
doctype: 'Bill',
|
||||
fields: ['name', 'date', 'supplier', 'account', 'netTotal', 'grandTotal'],
|
||||
filters: {
|
||||
date: ['>=', fromDate, '<=', toDate],
|
||||
submitted: 1
|
||||
},
|
||||
orderBy: 'date',
|
||||
order: 'desc'
|
||||
});
|
||||
|
||||
const billNames = bills.map(d => d.name);
|
||||
|
||||
const taxes = await frappe.db.getAll({
|
||||
doctype: 'TaxSummary',
|
||||
fields: ['parent', 'amount'],
|
||||
filters: {
|
||||
parenttype: 'Bill',
|
||||
parent: ['in', billNames]
|
||||
},
|
||||
orderBy: 'name'
|
||||
});
|
||||
|
||||
for (let bill of bills) {
|
||||
bill.totalTax = taxes
|
||||
.filter(tax => tax.parent === bill.name)
|
||||
.reduce((acc, tax) => {
|
||||
if (tax.amount) {
|
||||
acc = acc + tax.amount;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
return { rows: bills };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = PurchaseRegister;
|
24
reports/PurchaseRegister/PurchaseRegisterView.js
Normal file
24
reports/PurchaseRegister/PurchaseRegisterView.js
Normal file
@ -0,0 +1,24 @@
|
||||
const frappe = require('frappejs');
|
||||
const RegisterView = require('../Register/RegisterView');
|
||||
|
||||
module.exports = class PurchaseRegisterView extends RegisterView {
|
||||
constructor() {
|
||||
super({
|
||||
title: frappe._('Purchase Register'),
|
||||
});
|
||||
|
||||
this.method = 'purchase-register';
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return [
|
||||
{ label: 'Bill', fieldname: 'name' },
|
||||
{ label: 'Posting Date', fieldname: 'date' },
|
||||
{ label: 'Supplier', fieldname: 'supplier' },
|
||||
{ label: 'Payable Account', fieldname: 'account' },
|
||||
{ label: 'Net Total', fieldname: 'netTotal', fieldtype: 'Currency' },
|
||||
{ label: 'Total Tax', fieldname: 'totalTax', fieldtype: 'Currency' },
|
||||
{ label: 'Grand Total', fieldname: 'grandTotal', fieldtype: 'Currency' },
|
||||
];
|
||||
}
|
||||
}
|
34
reports/Register/RegisterView.js
Normal file
34
reports/Register/RegisterView.js
Normal file
@ -0,0 +1,34 @@
|
||||
const ReportPage = require('frappejs/client/desk/reportpage');
|
||||
const frappe = require('frappejs');
|
||||
const { DateTime } = require('luxon');
|
||||
const { unique } = require('frappejs/utils');
|
||||
|
||||
module.exports = class RegisterView extends ReportPage {
|
||||
constructor({ title }) {
|
||||
super({
|
||||
title,
|
||||
filterFields: [
|
||||
{fieldtype: 'Date', fieldname: 'fromDate', label: 'From Date', required: 1},
|
||||
{fieldtype: 'Date', fieldname: 'toDate', label: 'To Date', required: 1}
|
||||
]
|
||||
});
|
||||
|
||||
this.datatableOptions = {
|
||||
layout: 'fixed'
|
||||
}
|
||||
}
|
||||
|
||||
async setDefaultFilterValues() {
|
||||
const today = DateTime.local();
|
||||
const oneMonthAgo = today.minus({ months: 1 });
|
||||
|
||||
this.filters.setValue('fromDate', oneMonthAgo.toISODate());
|
||||
this.filters.setValue('toDate', today.toISODate());
|
||||
|
||||
this.run();
|
||||
}
|
||||
|
||||
getRowsForDataTable(data) {
|
||||
return data.rows || [];
|
||||
}
|
||||
}
|
43
reports/SalesRegister/SalesRegister.js
Normal file
43
reports/SalesRegister/SalesRegister.js
Normal file
@ -0,0 +1,43 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
class SalesRegister {
|
||||
async run({ fromDate, toDate }) {
|
||||
const invoices = await frappe.db.getAll({
|
||||
doctype: 'Invoice',
|
||||
fields: ['name', 'date', 'customer', 'account', 'netTotal', 'grandTotal'],
|
||||
filters: {
|
||||
date: ['>=', fromDate, '<=', toDate],
|
||||
submitted: 1
|
||||
},
|
||||
orderBy: 'date',
|
||||
order: 'desc'
|
||||
});
|
||||
|
||||
const invoiceNames = invoices.map(d => d.name);
|
||||
|
||||
const taxes = await frappe.db.getAll({
|
||||
doctype: 'TaxSummary',
|
||||
fields: ['parent', 'amount'],
|
||||
filters: {
|
||||
parenttype: 'Invoice',
|
||||
parent: ['in', invoiceNames]
|
||||
},
|
||||
orderBy: 'name'
|
||||
});
|
||||
|
||||
for (let invoice of invoices) {
|
||||
invoice.totalTax = taxes
|
||||
.filter(tax => tax.parent === invoice.name)
|
||||
.reduce((acc, tax) => {
|
||||
if (tax.amount) {
|
||||
acc = acc + tax.amount;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
return { rows: invoices };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SalesRegister;
|
26
reports/SalesRegister/SalesRegisterView.js
Normal file
26
reports/SalesRegister/SalesRegisterView.js
Normal file
@ -0,0 +1,26 @@
|
||||
const RegisterView = require('../Register/RegisterView');
|
||||
const frappe = require('frappejs');
|
||||
const { DateTime } = require('luxon');
|
||||
const { unique } = require('frappejs/utils');
|
||||
|
||||
module.exports = class SalesRegisterView extends RegisterView {
|
||||
constructor() {
|
||||
super({
|
||||
title: frappe._('Sales Register')
|
||||
});
|
||||
|
||||
this.method = 'sales-register';
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return [
|
||||
{ label: 'Invoice', fieldname: 'name' },
|
||||
{ label: 'Posting Date', fieldname: 'date' },
|
||||
{ label: 'Customer', fieldname: 'customer' },
|
||||
{ label: 'Receivable Account', fieldname: 'account' },
|
||||
{ label: 'Net Total', fieldname: 'netTotal', fieldtype: 'Currency' },
|
||||
{ label: 'Total Tax', fieldname: 'totalTax', fieldtype: 'Currency' },
|
||||
{ label: 'Grand Total', fieldname: 'grandTotal', fieldtype: 'Currency' },
|
||||
];
|
||||
}
|
||||
}
|
@ -20,6 +20,4 @@ class GeneralLedger {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function execute(params) {
|
||||
return new GeneralLedger().run(params);
|
||||
}
|
||||
module.exports = GeneralLedger;
|
||||
|
@ -9,21 +9,37 @@ const ProfitAndLossView = require('./ProfitAndLoss/ProfitAndLossView');
|
||||
const BalanceSheet = require('./BalanceSheet/BalanceSheet');
|
||||
const BalanceSheetView = require('./BalanceSheet/BalanceSheetView');
|
||||
|
||||
const SalesRegister = require('./SalesRegister/SalesRegister');
|
||||
const SalesRegisterView = require('./SalesRegister/SalesRegisterView');
|
||||
|
||||
const PurchaseRegister = require('./PurchaseRegister/PurchaseRegister');
|
||||
const PurchaseRegisterView = require('./PurchaseRegister/PurchaseRegisterView');
|
||||
|
||||
// called on server side
|
||||
function registerReportMethods() {
|
||||
frappe.registerMethod({
|
||||
method: 'general-ledger',
|
||||
handler: args => GeneralLedger(args)
|
||||
handler: getReportData(GeneralLedger)
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'profit-and-loss',
|
||||
handler: args => ProfitAndLoss(args)
|
||||
handler: getReportData(ProfitAndLoss)
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'balance-sheet',
|
||||
handler: args => BalanceSheet(args)
|
||||
handler: getReportData(BalanceSheet)
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'sales-register',
|
||||
handler: getReportData(SalesRegister)
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'purchase-register',
|
||||
handler: getReportData(PurchaseRegister)
|
||||
});
|
||||
}
|
||||
|
||||
@ -49,6 +65,24 @@ function registerReportRoutes() {
|
||||
}
|
||||
await frappe.views.BalanceSheet.show(params);
|
||||
});
|
||||
|
||||
frappe.router.add('report/sales-register', async (params) => {
|
||||
if (!frappe.views.SalesRegister) {
|
||||
frappe.views.SalesRegister = new SalesRegisterView();
|
||||
}
|
||||
await frappe.views.SalesRegister.show(params);
|
||||
});
|
||||
|
||||
frappe.router.add('report/purchase-register', async (params) => {
|
||||
if (!frappe.views.PurchaseRegister) {
|
||||
frappe.views.PurchaseRegister = new PurchaseRegisterView();
|
||||
}
|
||||
await frappe.views.PurchaseRegister.show(params);
|
||||
});
|
||||
}
|
||||
|
||||
function getReportData(ReportClass) {
|
||||
return args => new ReportClass().run(args);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@ -23,7 +23,8 @@ module.exports = {
|
||||
// set server-side modules
|
||||
frappe.models.Invoice.documentClass = require('../models/doctype/Invoice/InvoiceServer.js');
|
||||
frappe.models.Payment.documentClass = require('../models/doctype/Payment/PaymentServer.js');
|
||||
// frappe.models.JournalEntry.documentClass = require('../models/doctype/JournalEntry/JournalEntryServer.js');
|
||||
frappe.models.Bill.documentClass = require('../models/doctype/Bill/BillServer.js');
|
||||
frappe.models.JournalEntry.documentClass = require('../models/doctype/JournalEntry/JournalEntryServer.js');
|
||||
|
||||
frappe.metaCache = {};
|
||||
|
||||
@ -31,6 +32,7 @@ module.exports = {
|
||||
|
||||
// init naming series if missing
|
||||
await naming.createNumberSeries('INV-', 'InvoiceSettings');
|
||||
await naming.createNumberSeries('BILL-', 'BillSettings');
|
||||
await naming.createNumberSeries('PAY-', 'PaymentSettings');
|
||||
await naming.createNumberSeries('JV-', 'JournalEntrySettings');
|
||||
await naming.createNumberSeries('QTN-', 'QuotationSettings');
|
||||
|
345
www/dist/css/style.css
vendored
345
www/dist/css/style.css
vendored
@ -7130,6 +7130,169 @@ div.CodeMirror-dragcursors {
|
||||
/* Help users use markselection to safely style text background */
|
||||
span.CodeMirror-selectedtext {
|
||||
background: none; }
|
||||
.indicator, .indicator-right {
|
||||
background: none;
|
||||
vertical-align: middle; }
|
||||
.indicator::before, .indicator-right::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 8px;
|
||||
background: #dee2e6; }
|
||||
.indicator::before {
|
||||
margin: 0 0.5rem 0 0; }
|
||||
.indicator-right::after {
|
||||
margin: 0 0 0 0.5rem; }
|
||||
.indicator.grey::before, .indicator-right.grey::after {
|
||||
background: #dee2e6; }
|
||||
.indicator.blue::before, .indicator-right.blue::after {
|
||||
background: #007bff; }
|
||||
.indicator.red::before, .indicator-right.red::after {
|
||||
background: #dc3545; }
|
||||
.indicator.green::before, .indicator-right.green::after {
|
||||
background: #28a745; }
|
||||
.indicator.orange::before, .indicator-right.orange::after {
|
||||
background: #fd7e14; }
|
||||
.indicator.purple::before, .indicator-right.purple::after {
|
||||
background: #6f42c1; }
|
||||
.indicator.darkgrey::before, .indicator-right.darkgrey::after {
|
||||
background: #6c757d; }
|
||||
.indicator.black::before, .indicator-right.black::after {
|
||||
background: #343a40; }
|
||||
.indicator.yellow::before, .indicator-right.yellow::after {
|
||||
background: #ffc107; }
|
||||
.modal-header .indicator {
|
||||
float: left;
|
||||
margin-top: 7.5px;
|
||||
margin-right: 3px; }
|
||||
html {
|
||||
font-size: 12px; }
|
||||
.desk-body {
|
||||
border-left: 1px solid #dee2e6;
|
||||
min-height: 100vh; }
|
||||
.desk-center {
|
||||
border-left: 1px solid #dee2e6; }
|
||||
.hide {
|
||||
display: none !important; }
|
||||
.page {
|
||||
padding-bottom: 2rem; }
|
||||
.page .page-nav {
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6; }
|
||||
.page .page-nav .btn {
|
||||
margin-left: 0.5rem; }
|
||||
.page .page-title {
|
||||
font-weight: bold;
|
||||
padding: 2rem;
|
||||
padding-bottom: 0; }
|
||||
.page .page-links {
|
||||
padding: 1rem 2rem; }
|
||||
.page .page-error {
|
||||
text-align: center;
|
||||
padding: 200px 0px; }
|
||||
.form-body {
|
||||
padding: 1rem 2rem; }
|
||||
.form-body .form-check {
|
||||
margin-bottom: 0.5rem; }
|
||||
.form-body .form-check .form-check-input {
|
||||
margin-top: 0.25rem; }
|
||||
.form-body .form-check .form-check-label {
|
||||
margin-left: 0.25rem; }
|
||||
.form-body .form-control.font-weight-bold {
|
||||
background-color: lightyellow; }
|
||||
.form-body .alert {
|
||||
margin-top: 1rem; }
|
||||
.form-inline .form-group {
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 1rem; }
|
||||
.list-search {
|
||||
padding: 1rem 2rem; }
|
||||
.list-body .list-row {
|
||||
padding: 0.5rem 2rem;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
cursor: pointer; }
|
||||
.list-body .list-row .checkbox {
|
||||
margin-right: 0.5rem; }
|
||||
.list-body .list-row a, .list-body .list-row a:hover, .list-body .list-row a:visited, .list-body .list-row a:active {
|
||||
color: #343a40; }
|
||||
.list-body .list-row:hover {
|
||||
background-color: #f8f9fa; }
|
||||
.list-body .list-row.active {
|
||||
background-color: #e9ecef; }
|
||||
.dropdown-item {
|
||||
padding: 0.5rem 1rem; }
|
||||
.bottom-right-float {
|
||||
position: fixed;
|
||||
margin-bottom: 0px;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
max-width: 200px;
|
||||
padding: 0.5rem 1rem; }
|
||||
.desk-menu {
|
||||
background-color: #e9ecef; }
|
||||
.desk-menu .list-row {
|
||||
border-bottom: 1px solid #e9ecef; }
|
||||
.desk-menu .list-row:hover {
|
||||
background-color: #dee2e6; }
|
||||
.desk-menu .list-row.active {
|
||||
background-color: #ced4da; }
|
||||
.print-page {
|
||||
padding: 3rem;
|
||||
line-height: 1.8; }
|
||||
.print-page td, .print-page th {
|
||||
padding: 0.5rem; }
|
||||
.table-page-wrapper {
|
||||
width: 100%;
|
||||
padding: 1rem 2rem; }
|
||||
.filter-toolbar {
|
||||
padding: 1rem 2rem; }
|
||||
.table-wrapper {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem; }
|
||||
.table-toolbar {
|
||||
margin-top: 0.5rem; }
|
||||
.CodeMirror {
|
||||
font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem; }
|
||||
.awesomplete {
|
||||
display: block; }
|
||||
.awesomplete ul {
|
||||
max-height: 150px;
|
||||
overflow: auto; }
|
||||
.awesomplete > ul > li {
|
||||
padding: .75rem .375rem; }
|
||||
.awesomplete > ul > li:hover {
|
||||
background: #dee2e6;
|
||||
color: #212529; }
|
||||
.awesomplete > ul > li[aria-selected="true"] {
|
||||
background: #dee2e6;
|
||||
color: #212529; }
|
||||
.awesomplete > ul > li[aria-selected="true"]:hover {
|
||||
background: #dee2e6;
|
||||
color: #212529; }
|
||||
.awesomplete li[aria-selected="true"] mark, .awesomplete li[aria-selected="false"] mark {
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
padding: 0px; }
|
||||
mark {
|
||||
padding: none;
|
||||
background: inherit; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.btn-sm, .btn-group-sm > .btn {
|
||||
margin: 0.25rem; }
|
||||
.vertical-margin {
|
||||
margin: 1rem 0px; }
|
||||
.tree-body {
|
||||
padding: 1rem 2rem; }
|
||||
f-tree-node {
|
||||
--tree-node-hover: #f8f9fa; }
|
||||
.datatable *, .datatable *::after, .datatable *::before {
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box; }
|
||||
@ -7310,183 +7473,27 @@ span.CodeMirror-selectedtext {
|
||||
left: -999em; }
|
||||
body.dt-resize {
|
||||
cursor: col-resize; }
|
||||
.indicator, .indicator-right {
|
||||
background: none;
|
||||
vertical-align: middle; }
|
||||
.indicator::before, .indicator-right::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 8px;
|
||||
background: #dee2e6; }
|
||||
.indicator::before {
|
||||
margin: 0 0.5rem 0 0; }
|
||||
.indicator-right::after {
|
||||
margin: 0 0 0 0.5rem; }
|
||||
.indicator.grey::before, .indicator-right.grey::after {
|
||||
background: #dee2e6; }
|
||||
.indicator.blue::before, .indicator-right.blue::after {
|
||||
background: #007bff; }
|
||||
.indicator.red::before, .indicator-right.red::after {
|
||||
background: #dc3545; }
|
||||
.indicator.green::before, .indicator-right.green::after {
|
||||
background: #28a745; }
|
||||
.indicator.orange::before, .indicator-right.orange::after {
|
||||
background: #fd7e14; }
|
||||
.indicator.purple::before, .indicator-right.purple::after {
|
||||
background: #6f42c1; }
|
||||
.indicator.darkgrey::before, .indicator-right.darkgrey::after {
|
||||
background: #6c757d; }
|
||||
.indicator.black::before, .indicator-right.black::after {
|
||||
background: #343a40; }
|
||||
.indicator.yellow::before, .indicator-right.yellow::after {
|
||||
background: #ffc107; }
|
||||
.modal-header .indicator {
|
||||
float: left;
|
||||
margin-top: 7.5px;
|
||||
margin-right: 3px; }
|
||||
html {
|
||||
font-size: 12px; }
|
||||
.desk-body {
|
||||
border-left: 1px solid #dee2e6;
|
||||
min-height: 100vh; }
|
||||
.desk-center {
|
||||
border-left: 1px solid #dee2e6; }
|
||||
.hide {
|
||||
display: none !important; }
|
||||
.page {
|
||||
padding-bottom: 2rem; }
|
||||
.page .page-nav {
|
||||
padding: 0.5rem 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-bottom: 1px solid #dee2e6; }
|
||||
.page .page-nav .btn {
|
||||
margin-left: 0.5rem; }
|
||||
.page .page-title {
|
||||
font-weight: bold;
|
||||
padding: 2rem;
|
||||
padding-bottom: 0; }
|
||||
.page .page-links {
|
||||
padding: 1rem 2rem; }
|
||||
.page .page-error {
|
||||
text-align: center;
|
||||
padding: 200px 0px; }
|
||||
.form-body {
|
||||
padding: 1rem 2rem; }
|
||||
.form-body .form-check {
|
||||
margin-bottom: 0.5rem; }
|
||||
.form-body .form-check .form-check-input {
|
||||
margin-top: 0.25rem; }
|
||||
.form-body .form-check .form-check-label {
|
||||
margin-left: 0.25rem; }
|
||||
.form-body .form-control.font-weight-bold {
|
||||
background-color: lightyellow; }
|
||||
.form-body .alert {
|
||||
margin-top: 1rem; }
|
||||
.form-inline .form-group {
|
||||
margin-right: 1rem;
|
||||
margin-bottom: 1rem; }
|
||||
.list-search {
|
||||
padding: 1rem 2rem; }
|
||||
.list-body .list-row {
|
||||
padding: 0.5rem 2rem;
|
||||
border-bottom: 1px solid #e9ecef;
|
||||
cursor: pointer; }
|
||||
.list-body .list-row .checkbox {
|
||||
margin-right: 0.5rem; }
|
||||
.list-body .list-row a, .list-body .list-row a:hover, .list-body .list-row a:visited, .list-body .list-row a:active {
|
||||
color: #343a40; }
|
||||
.list-body .list-row:hover {
|
||||
background-color: #f8f9fa; }
|
||||
.list-body .list-row.active {
|
||||
background-color: #e9ecef; }
|
||||
.dropdown-item {
|
||||
padding: 0.5rem 1rem; }
|
||||
.bottom-right-float {
|
||||
position: fixed;
|
||||
margin-bottom: 0px;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
max-width: 200px;
|
||||
padding: 0.5rem 1rem; }
|
||||
.desk-menu {
|
||||
background-color: #e9ecef; }
|
||||
.desk-menu .list-row {
|
||||
border-bottom: 1px solid #e9ecef; }
|
||||
.desk-menu .list-row:hover {
|
||||
background-color: #dee2e6; }
|
||||
.desk-menu .list-row.active {
|
||||
background-color: #ced4da; }
|
||||
.print-page {
|
||||
padding: 3rem;
|
||||
line-height: 1.8; }
|
||||
.print-page td, .print-page th {
|
||||
padding: 0.5rem; }
|
||||
.table-page-wrapper {
|
||||
width: 100%;
|
||||
padding: 1rem 2rem; }
|
||||
.filter-toolbar {
|
||||
padding: 1rem 2rem; }
|
||||
.table-wrapper {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 2rem; }
|
||||
.table-toolbar {
|
||||
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 {
|
||||
.dt-header {
|
||||
background-color: #e9ecef !important; }
|
||||
.data-table .data-table-cell .edit-cell {
|
||||
padding: 0px !important; }
|
||||
.data-table .data-table-cell .edit-cell input, .data-table .data-table-cell .edit-cell textarea {
|
||||
.dt-cell__edit {
|
||||
padding: 0px; }
|
||||
.dt-cell__edit input, .dt-cell__edit textarea {
|
||||
outline: none;
|
||||
border-radius: none;
|
||||
border: none;
|
||||
margin: none;
|
||||
padding: 0.5rem; }
|
||||
.data-table .data-table-cell .edit-cell .awesomplete > ul {
|
||||
.dt-cell__edit input:focus, .dt-cell__edit textarea:focus {
|
||||
border: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none; }
|
||||
.dt-cell__edit .awesomplete > ul {
|
||||
position: fixed;
|
||||
left: auto;
|
||||
width: auto;
|
||||
min-width: 120px; }
|
||||
.CodeMirror {
|
||||
font-family: "SFMono-Regular",Consolas,"Liberation Mono",Menlo,Courier,monospace;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.5rem; }
|
||||
.awesomplete {
|
||||
display: block; }
|
||||
.awesomplete > ul > li {
|
||||
padding: .75rem .375rem; }
|
||||
.awesomplete > ul > li:hover {
|
||||
background: #dee2e6;
|
||||
color: #212529; }
|
||||
.awesomplete > ul > li[aria-selected="true"] {
|
||||
background: #dee2e6;
|
||||
color: #212529; }
|
||||
.awesomplete > ul > li[aria-selected="true"]:hover {
|
||||
background: #dee2e6;
|
||||
color: #212529; }
|
||||
.awesomplete li[aria-selected="true"] mark, .awesomplete li[aria-selected="false"] mark {
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
padding: 0px; }
|
||||
mark {
|
||||
padding: none;
|
||||
background: inherit; }
|
||||
.align-right {
|
||||
text-align: right; }
|
||||
.align-center {
|
||||
text-align: center; }
|
||||
.btn-sm, .btn-group-sm > .btn {
|
||||
margin: 0.25rem; }
|
||||
.vertical-margin {
|
||||
margin: 1rem 0px; }
|
||||
.tree-body {
|
||||
padding: 1rem 2rem; }
|
||||
f-tree-node {
|
||||
--tree-node-hover: #f8f9fa; }
|
||||
.dt-cell--highlight {
|
||||
background-color: #e9ecef; }
|
||||
.setup-container {
|
||||
margin: 40px auto;
|
||||
padding: 20px 0px;
|
||||
|
383
www/dist/js/bundle.js
vendored
383
www/dist/js/bundle.js
vendored
@ -58972,9 +58972,7 @@ class GeneralLedger {
|
||||
}
|
||||
}
|
||||
|
||||
var GeneralLedger_1 = function execute(params) {
|
||||
return new GeneralLedger().run(params);
|
||||
};
|
||||
var GeneralLedger_1 = GeneralLedger;
|
||||
|
||||
var reportpage = class ReportPage extends page {
|
||||
constructor({title, filterFields = []}) {
|
||||
@ -65181,9 +65179,7 @@ class ProfitAndLoss {
|
||||
}
|
||||
}
|
||||
|
||||
var ProfitAndLoss_1 = function execute(params) {
|
||||
return new ProfitAndLoss().run(params);
|
||||
};
|
||||
var ProfitAndLoss_1 = ProfitAndLoss;
|
||||
|
||||
var FinancialStatementsView_1 = class FinancialStatementsView extends reportpage {
|
||||
constructor(opts) {
|
||||
@ -65287,9 +65283,7 @@ class BalanceSheet {
|
||||
}
|
||||
}
|
||||
|
||||
var BalanceSheet_1 = function execute(params) {
|
||||
return new BalanceSheet().run(params);
|
||||
};
|
||||
var BalanceSheet_1 = BalanceSheet;
|
||||
|
||||
var BalanceSheetView_1 = class BalanceSheetView extends FinancialStatementsView_1 {
|
||||
constructor() {
|
||||
@ -65305,20 +65299,100 @@ var BalanceSheetView_1 = class BalanceSheetView extends FinancialStatementsView_
|
||||
}
|
||||
};
|
||||
|
||||
class SalesRegister {
|
||||
async run({ fromDate, toDate }) {
|
||||
const invoices = await frappejs.db.getAll({
|
||||
doctype: 'Invoice',
|
||||
fields: ['name', 'date', 'customer', 'account', 'netTotal', 'grandTotal'],
|
||||
filters: {
|
||||
date: ['>=', fromDate, '<=', toDate],
|
||||
submitted: 1
|
||||
},
|
||||
orderBy: 'date',
|
||||
order: 'desc'
|
||||
});
|
||||
|
||||
const invoiceNames = invoices.map(d => d.name);
|
||||
|
||||
const taxes = await frappejs.db.getAll({
|
||||
doctype: 'TaxSummary',
|
||||
fields: ['parent', 'amount'],
|
||||
filters: {
|
||||
parenttype: 'Invoice',
|
||||
parent: ['in', invoiceNames]
|
||||
},
|
||||
orderBy: 'name'
|
||||
});
|
||||
|
||||
for (let invoice of invoices) {
|
||||
invoice.totalTax = taxes
|
||||
.filter(tax => tax.parent === invoice.name)
|
||||
.reduce((acc, tax) => {
|
||||
if (tax.amount) {
|
||||
acc = acc + tax.amount;
|
||||
}
|
||||
return acc;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
return { rows: invoices };
|
||||
}
|
||||
}
|
||||
|
||||
var SalesRegister_1 = SalesRegister;
|
||||
|
||||
var SalesRegisterView_1 = class SalesRegisterView extends reportpage {
|
||||
constructor() {
|
||||
super({
|
||||
title: frappejs._('Sales Register'),
|
||||
filterFields: [
|
||||
{fieldtype: 'Date', label: 'From Date', required: 1},
|
||||
{fieldtype: 'Date', label: 'To Date', required: 1}
|
||||
]
|
||||
});
|
||||
|
||||
this.method = 'sales-register';
|
||||
this.datatableOptions = {
|
||||
layout: 'fixed'
|
||||
};
|
||||
}
|
||||
|
||||
getRowsForDataTable(data) {
|
||||
return data.rows || [];
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return [
|
||||
{ label: 'Invoice', fieldname: 'name' },
|
||||
{ label: 'Posting Date', fieldname: 'date' },
|
||||
{ label: 'Customer', fieldname: 'customer' },
|
||||
{ label: 'Receivable Account', fieldname: 'account' },
|
||||
{ label: 'Net Total', fieldname: 'netTotal', fieldtype: 'Currency' },
|
||||
{ label: 'Total Tax', fieldname: 'totalTax', fieldtype: 'Currency' },
|
||||
{ label: 'Grand Total', fieldname: 'grandTotal', fieldtype: 'Currency' },
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
function registerReportMethods() {
|
||||
frappejs.registerMethod({
|
||||
method: 'general-ledger',
|
||||
handler: args => GeneralLedger_1(args)
|
||||
handler: getReportData(GeneralLedger_1)
|
||||
});
|
||||
|
||||
frappejs.registerMethod({
|
||||
method: 'profit-and-loss',
|
||||
handler: args => ProfitAndLoss_1(args)
|
||||
handler: getReportData(ProfitAndLoss_1)
|
||||
});
|
||||
|
||||
frappejs.registerMethod({
|
||||
method: 'balance-sheet',
|
||||
handler: args => BalanceSheet_1(args)
|
||||
handler: getReportData(BalanceSheet_1)
|
||||
});
|
||||
|
||||
frappejs.registerMethod({
|
||||
method: 'sales-register',
|
||||
handler: getReportData(SalesRegister_1)
|
||||
});
|
||||
}
|
||||
|
||||
@ -65344,6 +65418,17 @@ function registerReportRoutes() {
|
||||
}
|
||||
await frappejs.views.BalanceSheet.show(params);
|
||||
});
|
||||
|
||||
frappejs.router.add('report/sales-register', async (params) => {
|
||||
if (!frappejs.views.SalesRegister) {
|
||||
frappejs.views.SalesRegister = new SalesRegisterView_1();
|
||||
}
|
||||
await frappejs.views.SalesRegister.show(params);
|
||||
});
|
||||
}
|
||||
|
||||
function getReportData(ReportClass) {
|
||||
return args => new ReportClass().run(args);
|
||||
}
|
||||
|
||||
var reports = {
|
||||
@ -66171,8 +66256,7 @@ var AccountingLedgerEntry = {
|
||||
fieldname: "party",
|
||||
label: "Party",
|
||||
fieldtype: "Link",
|
||||
target: "Party",
|
||||
required: 1
|
||||
target: "Party"
|
||||
},
|
||||
{
|
||||
fieldname: "debit",
|
||||
@ -66187,8 +66271,7 @@ var AccountingLedgerEntry = {
|
||||
{
|
||||
fieldname: "againstAccount",
|
||||
label: "Against Account",
|
||||
fieldtype: "Text",
|
||||
required: 0
|
||||
fieldtype: "Text"
|
||||
},
|
||||
{
|
||||
fieldname: "referenceType",
|
||||
@ -66578,7 +66661,13 @@ module.exports = {
|
||||
"label": "Customer",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"required": 1
|
||||
"required": 1,
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
customer: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
@ -66588,7 +66677,8 @@ module.exports = {
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
isGroup: 0
|
||||
isGroup: 0,
|
||||
accountType: "Receivable"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -66777,6 +66867,237 @@ var InvoiceSettings = {
|
||||
]
|
||||
};
|
||||
|
||||
var BillDocument = class Bill extends InvoiceDocument { };
|
||||
|
||||
var Bill = createCommonjsModule(function (module) {
|
||||
module.exports = {
|
||||
"name": "Bill",
|
||||
"doctype": "DocType",
|
||||
"isSingle": 0,
|
||||
"isChild": 0,
|
||||
"isSubmittable": 1,
|
||||
"keywordFields": ["name", "supplier"],
|
||||
"settings": "BillSettings",
|
||||
"showTitle": true,
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "date",
|
||||
"label": "Date",
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "supplier",
|
||||
"label": "Supplier",
|
||||
"fieldtype": "Link",
|
||||
"target": "Party",
|
||||
"required": 1,
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
supplier: 1
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "account",
|
||||
"label": "Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
isGroup: 0,
|
||||
accountType: "Payable"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
"label": "Items",
|
||||
"fieldtype": "Table",
|
||||
"childtype": "BillItem",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "netTotal",
|
||||
"label": "Net Total",
|
||||
"fieldtype": "Currency",
|
||||
formula: (doc) => doc.getSum('items', 'amount'),
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"label": "Taxes",
|
||||
"fieldtype": "Table",
|
||||
"childtype": "TaxSummary",
|
||||
"disabled": true,
|
||||
template: (doc, row) => {
|
||||
return `<div class='row'>
|
||||
<div class='col-6'><!-- empty left side --></div>
|
||||
<div class='col-6'>${(doc.taxes || []).map(row => {
|
||||
return `<div class='row'>
|
||||
<div class='col-6'>${row.account} (${row.rate}%)</div>
|
||||
<div class='col-6 text-right'>
|
||||
${frappejs.format(row.amount, 'Currency')}
|
||||
</div>
|
||||
</div>`
|
||||
}).join('')}
|
||||
</div></div>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "grandTotal",
|
||||
"label": "Grand Total",
|
||||
"fieldtype": "Currency",
|
||||
formula: (doc) => doc.getGrandTotal(),
|
||||
"disabled": true
|
||||
},
|
||||
{
|
||||
"fieldname": "terms",
|
||||
"label": "Terms",
|
||||
"fieldtype": "Text"
|
||||
}
|
||||
],
|
||||
|
||||
layout: [
|
||||
// section 1
|
||||
{
|
||||
columns: [
|
||||
{ fields: [ "supplier", "account" ] },
|
||||
{ fields: [ "date" ] }
|
||||
]
|
||||
},
|
||||
|
||||
// section 2
|
||||
{ fields: [ "items" ] },
|
||||
|
||||
// section 3
|
||||
{ fields: [ "netTotal", "taxes", "grandTotal" ] },
|
||||
|
||||
// section 4
|
||||
{ fields: [ "terms" ] },
|
||||
],
|
||||
|
||||
links: [
|
||||
{
|
||||
label: 'Make Payment',
|
||||
condition: form => form.doc.submitted,
|
||||
action: async form => {
|
||||
const payment = await frappejs.getNewDoc('Payment');
|
||||
payment.party = form.doc.supplier, payment.account = form.doc.account, payment.for = [{referenceType: form.doc.doctype, referenceName: form.doc.name, amount: form.doc.grandTotal}];
|
||||
const formModal = await frappejs.desk.showFormModal('Payment', payment.name);
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
listSettings: {
|
||||
getFields(list) {
|
||||
return ['name', 'supplier', 'grandTotal', 'submitted'];
|
||||
},
|
||||
|
||||
getRowHTML(list, data) {
|
||||
return `<div class="col-3">${list.getNameHTML(data)}</div>
|
||||
<div class="col-4 text-muted">${data.supplier}</div>
|
||||
<div class="col-4 text-muted text-right">${frappejs.format(data.grandTotal, "Currency")}</div>`;
|
||||
}
|
||||
},
|
||||
|
||||
documentClass: BillDocument
|
||||
};
|
||||
});
|
||||
|
||||
var Bill_1 = Bill.layout;
|
||||
var Bill_2 = Bill.links;
|
||||
var Bill_3 = Bill.listSettings;
|
||||
var Bill_4 = Bill.documentClass;
|
||||
|
||||
var BillItem = {
|
||||
name: "BillItem",
|
||||
doctype: "DocType",
|
||||
isSingle: 0,
|
||||
isChild: 1,
|
||||
keywordFields: [],
|
||||
layout: 'ratio',
|
||||
fields: [
|
||||
{
|
||||
"fieldname": "item",
|
||||
"label": "Item",
|
||||
"fieldtype": "Link",
|
||||
"target": "Item",
|
||||
"required": 1,
|
||||
width: 2
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"label": "Description",
|
||||
"fieldtype": "Text",
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'description'),
|
||||
hidden: 1
|
||||
},
|
||||
{
|
||||
"fieldname": "quantity",
|
||||
"label": "Quantity",
|
||||
"fieldtype": "Float",
|
||||
"required": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"label": "Rate",
|
||||
"fieldtype": "Currency",
|
||||
"required": 1,
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'rate')
|
||||
},
|
||||
{
|
||||
fieldname: "account",
|
||||
label: "Account",
|
||||
hidden: 1,
|
||||
fieldtype: "Link",
|
||||
target: "Account",
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'expenseAccount')
|
||||
},
|
||||
{
|
||||
"fieldname": "tax",
|
||||
"label": "Tax",
|
||||
"fieldtype": "Link",
|
||||
"target": "Tax",
|
||||
formula: (row, doc) => doc.getFrom('Item', row.item, 'tax')
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
"label": "Amount",
|
||||
"fieldtype": "Currency",
|
||||
"disabled": 1,
|
||||
formula: (row, doc) => row.quantity * row.rate
|
||||
},
|
||||
{
|
||||
"fieldname": "taxAmount",
|
||||
"label": "Tax Amount",
|
||||
"hidden": 1,
|
||||
"fieldtype": "Text",
|
||||
formula: (row, doc) => doc.getRowTax(row)
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var BillSettings = {
|
||||
"name": "BillSettings",
|
||||
"label": "Bill Settings",
|
||||
"doctype": "DocType",
|
||||
"isSingle": 1,
|
||||
"isChild": 0,
|
||||
"keywordFields": [],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "numberSeries",
|
||||
"label": "Number Series",
|
||||
"fieldtype": "Link",
|
||||
"target": "NumberSeries",
|
||||
"required": 1,
|
||||
"default": "BILL"
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var Tax = {
|
||||
"name": "Tax",
|
||||
"doctype": "DocType",
|
||||
@ -67069,7 +67390,7 @@ var JournalEntry = {
|
||||
isSubmittable: 1,
|
||||
keywordFields: ["name"],
|
||||
showTitle: true,
|
||||
settings: "JournalEntrySetting",
|
||||
settings: "JournalEntrySettings",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "date",
|
||||
@ -67142,7 +67463,8 @@ var JournalEntry = {
|
||||
]
|
||||
};
|
||||
|
||||
var JournalEntryAccount = {
|
||||
var JournalEntryAccount = createCommonjsModule(function (module) {
|
||||
module.exports = {
|
||||
name: "JournalEntryAccount",
|
||||
doctype: "DocType",
|
||||
isSingle: 0,
|
||||
@ -67156,6 +67478,12 @@ var JournalEntryAccount = {
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
"required": 1,
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
keywords: ["like", query],
|
||||
isGroup: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "debit",
|
||||
@ -67169,6 +67497,15 @@ var JournalEntryAccount = {
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
var JournalEntryAccount_1 = JournalEntryAccount.name;
|
||||
var JournalEntryAccount_2 = JournalEntryAccount.doctype;
|
||||
var JournalEntryAccount_3 = JournalEntryAccount.isSingle;
|
||||
var JournalEntryAccount_4 = JournalEntryAccount.isChild;
|
||||
var JournalEntryAccount_5 = JournalEntryAccount.keywordFields;
|
||||
var JournalEntryAccount_6 = JournalEntryAccount.layout;
|
||||
var JournalEntryAccount_7 = JournalEntryAccount.fields;
|
||||
|
||||
var JournalEntrySettings = {
|
||||
"name": "JournalEntrySetting",
|
||||
@ -67321,6 +67658,10 @@ var models$2 = {
|
||||
InvoiceItem: InvoiceItem,
|
||||
InvoiceSettings: InvoiceSettings,
|
||||
|
||||
Bill: Bill,
|
||||
BillItem: BillItem,
|
||||
BillSettings: BillSettings,
|
||||
|
||||
Tax: Tax,
|
||||
TaxDetail: TaxDetail,
|
||||
TaxSummary: TaxSummary,
|
||||
@ -67365,6 +67706,7 @@ var client$2 = {
|
||||
frappejs.desk.menu.addItem('Customers', '#list/Customer');
|
||||
frappejs.desk.menu.addItem('Quotation', '#list/Quotation');
|
||||
frappejs.desk.menu.addItem('Invoice', '#list/Invoice');
|
||||
frappejs.desk.menu.addItem('Bill', '#list/Bill');
|
||||
frappejs.desk.menu.addItem('Journal Entry', '#list/JournalEntry');
|
||||
frappejs.desk.menu.addItem('Address', "#list/Address");
|
||||
frappejs.desk.menu.addItem('Contact', "#list/Contact");
|
||||
@ -67372,6 +67714,7 @@ var client$2 = {
|
||||
frappejs.desk.menu.addItem('General Ledger', '#report/general-ledger');
|
||||
frappejs.desk.menu.addItem('Profit And Loss', '#report/profit-and-loss');
|
||||
frappejs.desk.menu.addItem('Balance Sheet', '#report/balance-sheet');
|
||||
frappejs.desk.menu.addItem('Sales Register', '#report/sales-register');
|
||||
|
||||
frappejs.router.default = '#tree/Account';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user