mirror of
https://github.com/frappe/books.git
synced 2024-11-10 07:40:55 +00:00
commit
13e3d46d54
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ Thumbs.db
|
||||
.cache
|
||||
/temp
|
||||
dist
|
||||
Frappe Accounting*
|
@ -19,7 +19,7 @@ module.exports = {
|
||||
})
|
||||
|
||||
frappe.desk.menu.addItem('ToDo', '#list/ToDo');
|
||||
frappe.desk.menu.addItem('Accounts', '#list/Account');
|
||||
frappe.desk.menu.addItem('Chart of Accounts', '#tree/Account');
|
||||
frappe.desk.menu.addItem('Items', '#list/Item');
|
||||
frappe.desk.menu.addItem('Customers', '#list/Customer');
|
||||
frappe.desk.menu.addItem('Invoice', '#list/Invoice');
|
||||
@ -27,7 +27,7 @@ module.exports = {
|
||||
frappe.desk.menu.addItem('Contact', "#list/Contact");
|
||||
frappe.desk.menu.addItem('Settings', () => frappe.desk.showFormModal('SystemSettings'));
|
||||
|
||||
frappe.router.default = '#list/ToDo';
|
||||
frappe.router.default = '#tree/Account';
|
||||
|
||||
frappe.router.show(window.location.hash);
|
||||
|
||||
|
@ -4,6 +4,12 @@ const { writeFile } = require('frappejs/server/utils');
|
||||
const appClient = require('../client');
|
||||
const SetupWizard = require('../setup');
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
require.extensions['.html'] = function (module, filename) {
|
||||
module.exports = fs.readFileSync(filename, 'utf8');
|
||||
};
|
||||
|
||||
(async () => {
|
||||
const configFilePath = path.join(require('os').homedir(), '.config', 'frappe-accounting', 'settings.json');
|
||||
|
||||
@ -20,6 +26,8 @@ const SetupWizard = require('../setup');
|
||||
dbPath,
|
||||
models: require('../models')
|
||||
}).then(() => {
|
||||
|
||||
frappe.syncDoc(require('../fixtures/invoicePrint'));
|
||||
appClient.start();
|
||||
});
|
||||
} else {
|
||||
@ -50,15 +58,20 @@ const SetupWizard = require('../setup');
|
||||
const doc = await frappe.getDoc('AccountingSettings');
|
||||
|
||||
await doc.set('companyName', companyName);
|
||||
await doc.set('file', dbPath);
|
||||
await doc.set('country', country);
|
||||
await doc.set('fullname', name);
|
||||
await doc.set('email', email);
|
||||
await doc.set('abbreviation', abbreviation);
|
||||
await doc.set('bankName', bankName);
|
||||
|
||||
await doc.update();
|
||||
|
||||
// bootstrap Chart of Accounts
|
||||
const importCOA = require('../models/doctype/account/importCOA');
|
||||
const chart = require('../fixtures/standardCOA');
|
||||
await importCOA(chart);
|
||||
|
||||
|
||||
frappe.syncDoc(require('../fixtures/invoicePrint'));
|
||||
appClient.start();
|
||||
})
|
||||
}
|
||||
|
173
fixtures/standardCOA.js
Normal file
173
fixtures/standardCOA.js
Normal file
@ -0,0 +1,173 @@
|
||||
const frappe = require('frappejs');
|
||||
const _ = frappe._.bind(frappe);
|
||||
|
||||
module.exports = {
|
||||
[_("Application of Funds (Assets)")]: {
|
||||
[_("Current Assets")]: {
|
||||
[_("Accounts Receivable")]: {
|
||||
[_("Debtors")]: {
|
||||
"accountType": "Receivable"
|
||||
}
|
||||
},
|
||||
[_("Bank Accounts")]: {
|
||||
"accountType": "Bank",
|
||||
"isGroup": 1
|
||||
},
|
||||
[_("Cash In Hand")]: {
|
||||
[_("Cash")]: {
|
||||
"accountType": "Cash"
|
||||
},
|
||||
"accountType": "Cash"
|
||||
},
|
||||
[_("Loans and Advances (Assets)")]: {
|
||||
"isGroup": 1
|
||||
},
|
||||
[_("Securities and Deposits")]: {
|
||||
[_("Earnest Money")]: {}
|
||||
},
|
||||
[_("Stock Assets")]: {
|
||||
[_("Stock In Hand")]: {
|
||||
"accountType": "Stock"
|
||||
},
|
||||
"accountType": "Stock",
|
||||
},
|
||||
[_("Tax Assets")]: {
|
||||
"isGroup": 1
|
||||
}
|
||||
},
|
||||
[_("Fixed Assets")]: {
|
||||
[_("Capital Equipments")]: {
|
||||
"accountType": "Fixed Asset"
|
||||
},
|
||||
[_("Electronic Equipments")]: {
|
||||
"accountType": "Fixed Asset"
|
||||
},
|
||||
[_("Furnitures and Fixtures")]: {
|
||||
"accountType": "Fixed Asset"
|
||||
},
|
||||
[_("Office Equipments")]: {
|
||||
"accountType": "Fixed Asset"
|
||||
},
|
||||
[_("Plants and Machineries")]: {
|
||||
"accountType": "Fixed Asset"
|
||||
},
|
||||
[_("Buildings")]: {
|
||||
"accountType": "Fixed Asset"
|
||||
},
|
||||
[_("Softwares")]: {
|
||||
"accountType": "Fixed Asset"
|
||||
},
|
||||
[_("Accumulated Depreciation")]: {
|
||||
"accountType": "Accumulated Depreciation"
|
||||
}
|
||||
},
|
||||
[_("Investments")]: {
|
||||
"isGroup": 1
|
||||
},
|
||||
[_("Temporary Accounts")]: {
|
||||
[_("Temporary Opening")]: {
|
||||
"accountType": "Temporary"
|
||||
}
|
||||
},
|
||||
"rootType": "Asset"
|
||||
},
|
||||
[_("Expenses")]: {
|
||||
[_("Direct Expenses")]: {
|
||||
[_("Stock Expenses")]: {
|
||||
[_("Cost of Goods Sold")]: {
|
||||
"accountType": "Cost of Goods Sold"
|
||||
},
|
||||
[_("Expenses Included In Valuation")]: {
|
||||
"accountType": "Expenses Included In Valuation"
|
||||
},
|
||||
[_("Stock Adjustment")]: {
|
||||
"accountType": "Stock Adjustment"
|
||||
}
|
||||
},
|
||||
},
|
||||
[_("Indirect Expenses")]: {
|
||||
[_("Administrative Expenses")]: {},
|
||||
[_("Commission on Sales")]: {},
|
||||
[_("Depreciation")]: {
|
||||
"accountType": "Depreciation"
|
||||
},
|
||||
[_("Entertainment Expenses")]: {},
|
||||
[_("Freight and Forwarding Charges")]: {
|
||||
"accountType": "Chargeable"
|
||||
},
|
||||
[_("Legal Expenses")]: {},
|
||||
[_("Marketing Expenses")]: {
|
||||
"accountType": "Chargeable"
|
||||
},
|
||||
[_("Miscellaneous Expenses")]: {
|
||||
"accountType": "Chargeable"
|
||||
},
|
||||
[_("Office Maintenance Expenses")]: {},
|
||||
[_("Office Rent")]: {},
|
||||
[_("Postal Expenses")]: {},
|
||||
[_("Print and Stationery")]: {},
|
||||
[_("Round Off")]: {
|
||||
"accountType": "Round Off"
|
||||
},
|
||||
[_("Salary")]: {},
|
||||
[_("Sales Expenses")]: {},
|
||||
[_("Telephone Expenses")]: {},
|
||||
[_("Travel Expenses")]: {},
|
||||
[_("Utility Expenses")]: {},
|
||||
[_("Write Off")]: {},
|
||||
[_("Exchange Gain/Loss")]: {},
|
||||
[_("Gain/Loss on Asset Disposal")]: {}
|
||||
},
|
||||
"rootType": "Expense"
|
||||
},
|
||||
[_("Income")]: {
|
||||
[_("Direct Income")]: {
|
||||
[_("Sales")]: {},
|
||||
[_("Service")]: {}
|
||||
},
|
||||
[_("Indirect Income")]: {
|
||||
"isGroup": 1
|
||||
},
|
||||
"rootType": "Income"
|
||||
},
|
||||
[_("Source of Funds (Liabilities)")]: {
|
||||
[_("Current Liabilities")]: {
|
||||
[_("Accounts Payable")]: {
|
||||
[_("Creditors")]: {
|
||||
"accountType": "Payable"
|
||||
},
|
||||
[_("Payroll Payable")]: {},
|
||||
},
|
||||
[_("Stock Liabilities")]: {
|
||||
[_("Stock Received But Not Billed")]: {
|
||||
"accountType": "Stock Received But Not Billed"
|
||||
},
|
||||
},
|
||||
[_("Duties and Taxes")]: {
|
||||
"accountType": "Tax",
|
||||
"isGroup": 1
|
||||
},
|
||||
[_("Loans (Liabilities)")]: {
|
||||
[_("Secured Loans")]: {},
|
||||
[_("Unsecured Loans")]: {},
|
||||
[_("Bank Overdraft Account")]: {},
|
||||
},
|
||||
},
|
||||
"rootType": "Liability"
|
||||
},
|
||||
[_("Equity")]: {
|
||||
[_("Capital Stock")]: {
|
||||
"accountType": "Equity"
|
||||
},
|
||||
[_("Dividends Paid")]: {
|
||||
"accountType": "Equity"
|
||||
},
|
||||
[_("Opening Balance Equity")]: {
|
||||
"accountType": "Equity"
|
||||
},
|
||||
[_("Retained Earnings")]: {
|
||||
"accountType": "Equity"
|
||||
},
|
||||
"rootType": "Equity"
|
||||
}
|
||||
}
|
2
main.js
2
main.js
@ -13,7 +13,7 @@ let mainWindow
|
||||
|
||||
function createWindow () {
|
||||
// Create the browser window.
|
||||
mainWindow = new BrowserWindow({width: 800, height: 600})
|
||||
mainWindow = new BrowserWindow({width: 1024, height: 768})
|
||||
|
||||
// and load the index.html of the app.
|
||||
mainWindow.loadURL(url.format({
|
||||
|
@ -5,7 +5,8 @@ module.exports = {
|
||||
"isSingle": 0,
|
||||
"keywordFields": [
|
||||
"name",
|
||||
"account_type"
|
||||
"rootType",
|
||||
"accountType"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@ -15,7 +16,7 @@ module.exports = {
|
||||
"required": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "parent_account",
|
||||
"fieldname": "parentAccount",
|
||||
"label": "Parent Account",
|
||||
"fieldtype": "Link",
|
||||
"target": "Account",
|
||||
@ -27,8 +28,8 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "account_type",
|
||||
"label": "Account Type",
|
||||
"fieldname": "rootType",
|
||||
"label": "Root Type",
|
||||
"fieldtype": "Select",
|
||||
"options": [
|
||||
"Asset",
|
||||
@ -37,6 +38,37 @@ module.exports = {
|
||||
"Income",
|
||||
"Expense"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fieldname": "accountType",
|
||||
"label": "Account Type",
|
||||
"fieldtype": "Select",
|
||||
"options": [
|
||||
"Accumulated Depreciation",
|
||||
"Bank",
|
||||
"Cash",
|
||||
"Chargeable",
|
||||
"Cost of Goods Sold",
|
||||
"Depreciation",
|
||||
"Equity",
|
||||
"Expense Account",
|
||||
"Expenses Included In Valuation",
|
||||
"Fixed Asset",
|
||||
"Income Account",
|
||||
"Payable",
|
||||
"Receivable",
|
||||
"Round Off",
|
||||
"Stock",
|
||||
"Stock Adjustment",
|
||||
"Stock Received But Not Billed",
|
||||
"Tax",
|
||||
"Temporary"
|
||||
]
|
||||
},
|
||||
{
|
||||
"fieldname": "isGroup",
|
||||
"label": "Is Group",
|
||||
"fieldtype": "Check"
|
||||
}
|
||||
],
|
||||
|
||||
@ -48,10 +80,14 @@ module.exports = {
|
||||
|
||||
listSettings: {
|
||||
getFields(list) {
|
||||
return ['name', 'account_type'];
|
||||
return ['name', 'accountType', 'rootType'];
|
||||
},
|
||||
getRowHTML(list, data) {
|
||||
return `<div class="col-11">${list.getNameHTML(data)} (${data.account_type})</div>`;
|
||||
return `<div class="col-11">${list.getNameHTML(data)} (${data.rootType})</div>`;
|
||||
}
|
||||
},
|
||||
|
||||
treeSettings: {
|
||||
parentField: 'parentAccount'
|
||||
}
|
||||
}
|
@ -3,11 +3,11 @@ 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');
|
||||
if (!this.accountType) {
|
||||
if (this.parentAccount) {
|
||||
this.accountType = await frappe.db.getValue('Account', this.parentAccount, 'accountType');
|
||||
} else {
|
||||
this.account_type = 'Asset';
|
||||
this.accountType = 'Asset';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,14 +23,6 @@ module.exports = {
|
||||
fieldtype: "Account"
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "file",
|
||||
"label": "File",
|
||||
"fieldtype": "Data",
|
||||
"required": 1,
|
||||
"directory": 1
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "country",
|
||||
"label": "Country",
|
||||
@ -53,13 +45,6 @@ module.exports = {
|
||||
"required": 1
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "abbreviation",
|
||||
"label": "Abbreviation",
|
||||
"fieldtype": "Data",
|
||||
"required": 1
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "bankName",
|
||||
"label": "Bank Name",
|
||||
|
50
models/doctype/account/importCOA.js
Normal file
50
models/doctype/account/importCOA.js
Normal file
@ -0,0 +1,50 @@
|
||||
const frappe = require('frappejs');
|
||||
const accountFields = ['accountType', 'rootType', 'isGroup'];
|
||||
|
||||
async function importAccounts(children, parent, rootType, rootAccount) {
|
||||
for (let accountName in children) {
|
||||
const child = children[accountName];
|
||||
|
||||
if (rootAccount) {
|
||||
rootType = child.rootType;
|
||||
}
|
||||
|
||||
if (!accountFields.includes(accountName)) {
|
||||
let isGroup = identifyIsGroup(child);
|
||||
const doc = frappe.newDoc({
|
||||
doctype: 'Account',
|
||||
name: accountName,
|
||||
parentAccount: parent,
|
||||
isGroup,
|
||||
rootType,
|
||||
accountType: child.accountType
|
||||
})
|
||||
|
||||
await doc.insert()
|
||||
|
||||
await importAccounts(child, accountName, rootType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function identifyIsGroup(child) {
|
||||
if (child.isGroup) {
|
||||
return child.isGroup;
|
||||
}
|
||||
|
||||
const keys = Object.keys(child);
|
||||
const children = keys.filter(key => !accountFields.includes(key))
|
||||
|
||||
if (children.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
module.exports = async function importCharts(chart) {
|
||||
if (chart) {
|
||||
await importAccounts(chart, '', '', true)
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,8 @@ module.exports = {
|
||||
"fieldname": "email",
|
||||
"label": "Email",
|
||||
"fieldtype": "Data",
|
||||
"required": 1
|
||||
"required": 1,
|
||||
"inputType": "email"
|
||||
},
|
||||
|
||||
{
|
||||
@ -40,13 +41,6 @@ module.exports = {
|
||||
"required": 1
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "abbreviation",
|
||||
"label": "Abbreviation",
|
||||
"fieldtype": "Data",
|
||||
"required": 1
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "bankName",
|
||||
"label": "Bank Name",
|
||||
@ -73,14 +67,7 @@ module.exports = {
|
||||
|
||||
{
|
||||
title: 'Add your Company',
|
||||
columns: [
|
||||
{
|
||||
fields: ['companyName', 'bankName']
|
||||
},
|
||||
{
|
||||
fields: ['abbreviation']
|
||||
},
|
||||
]
|
||||
fields: ['companyName', 'bankName']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -89,7 +89,29 @@ module.exports = class SetupWizard {
|
||||
}
|
||||
|
||||
nextSlide() {
|
||||
this.showSlide(this.currentIndex + 1);
|
||||
const isValid = this.validateCurrentSlide();
|
||||
frappe.ui.toggleClass(this.formLayout.sections[this.currentIndex], 'was-validated', !isValid);
|
||||
|
||||
if (isValid) {
|
||||
this.showSlide(this.currentIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
validateCurrentSlide() {
|
||||
const fields = this.getFieldsInSlide(this.currentIndex);
|
||||
const inputValidityMap = fields.map(field => this.formLayout.controls[field].input.checkValidity());
|
||||
const isValid = !inputValidityMap.includes(false);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
getFieldsInSlide(index) {
|
||||
const visibleSection = slideConfigs.layout[index];
|
||||
const fieldsInSlide = visibleSection.fields ||
|
||||
visibleSection.columns.reduce(
|
||||
(col, fields) => fields.concat(col.fields), []
|
||||
);
|
||||
|
||||
return fieldsInSlide;
|
||||
}
|
||||
|
||||
activateIndicator(index) {
|
||||
|
@ -8,7 +8,7 @@ async function makeFixtures() {
|
||||
await frappe.insert({doctype:'Party', 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:'Account', name:'GST', parentAccount: 'Liabilities'});
|
||||
await frappe.insert({doctype:'Tax', name:'GST',
|
||||
details: [{account: 'GST', rate:10}]
|
||||
})
|
||||
|
30
www/dist/css/style.css
vendored
30
www/dist/css/style.css
vendored
@ -7499,29 +7499,31 @@ mark {
|
||||
.vertical-margin {
|
||||
margin: 1rem 0px; }
|
||||
.tree {
|
||||
padding: 15px; }
|
||||
padding: 1rem 2rem; }
|
||||
.tree li {
|
||||
list-style: none;
|
||||
margin: 2px 0px; }
|
||||
list-style: none; }
|
||||
ul.tree-children {
|
||||
padding-left: 20px; }
|
||||
padding-left: 2rem; }
|
||||
.tree-link {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 1px; }
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
width: 100%; }
|
||||
.tree-link:hover {
|
||||
background-color: #f8f9fa; }
|
||||
.tree-link .node-parent {
|
||||
color: #6c757d;
|
||||
font-size: 14px;
|
||||
width: 10px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
text-align: center; }
|
||||
.tree-link .node-leaf {
|
||||
color: #ced4da; }
|
||||
.tree-link .node-parent, .tree-link .node-leaf {
|
||||
margin-right: 5px;
|
||||
margin-left: 2px;
|
||||
margin-top: 3px; }
|
||||
.tree-link.active svg {
|
||||
color: #007bff; }
|
||||
padding: 0.5rem; }
|
||||
.tree-link.active a {
|
||||
color: #6c757d; }
|
||||
.tree-hover {
|
||||
@ -7587,3 +7589,5 @@ input[type=file] {
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
z-index: -1; }
|
||||
.was-validated input[type=file]:invalid + button {
|
||||
border-color: #dc3545; }
|
||||
|
3752
www/dist/js/bundle.js
vendored
3752
www/dist/js/bundle.js
vendored
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user