mirror of
https://github.com/frappe/books.git
synced 2024-11-09 15:20:56 +00:00
Winter Sprint: Work (#79)
* init()
* Country-wise Chart of accounts on setup
* Add a sample invoice template
* Some error fixes
* [fix] missing COA
- move importCOA.js from models/doctype/account to models/doctype/Account
* All Account initial balance zero
* setup Bank Reconciliation
* New chart of accounts tree component
* GST taxes added. initialized gst reports.
* [chore] add *.db to .gitignore
* [fix] importCOA path error
* fix error + bank reconciliation fields
* [feat] add gst taxes
* GST report initialized
* GST report finalized
* GST report finalized
* Complete min. reconciliation
* [feat] auto select tax in invoice based on states
* [chore] fix merge changes
* Fix date issue - Make Payment
* Add invoice templates and invoice customizer panel
* Restructure invoice vue components
* update file with fiscal year
* Fix issues in invoice designs
* Add company settings. Dynamic addresses in invoice
* Move invoice styles to different file and add separate components for addresses
* [feat] add export-filtered-data-to-csv to reports
* [feat] add Export Wizard component for customizing export
* Fix invoice customizer position while scrolling. Fix address displayed as undefined in invoice if not found in db
* [chore] change markup for select all chkbox
* Setup config as doctype
* GSTIN bug fix
* Add custom google fonts
* Add Send email footer
* Fix DateTime
* Complete Merge + Resolve
* Complete Merge + Resolve
* [chore] change layout of Export Wizard
* [enh] optimize checkNoneSelected, style export modal footer divider
* Add Tax to SideBar
* Remove extra logs
* [fix] db name section in sidebar showing absolute path instead of dbname in windows i.e. platform=win32
* Country-wise Chart of accounts on setup (#78)
* Country-wise Chart of accounts on setup
* Some error fixes
* All Account initial balance zero
* Update README.md
- updated installation instructions with more detail
* Merge #79 Winter Sprint: Work
https://github.com/frappe/accounting/pull/79
* Revert "Merge #79 Winter Sprint: Work"
This reverts commit 1715116668
.
This commit is contained in:
parent
c7f27601c3
commit
72c6d7f6c7
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
*.db
|
||||
Thumbs.db
|
||||
*test.db
|
||||
*.log
|
||||
.cache
|
||||
/temp
|
||||
@ -11,4 +11,4 @@ Frappe Accounting*
|
||||
build
|
||||
!build/icons
|
||||
!.gitkeep
|
||||
*.db
|
||||
*.db
|
||||
|
@ -20,6 +20,7 @@ function getSettings() {
|
||||
async function saveSettings(settings) {
|
||||
await writeFile(configFilePath, JSON.stringify(settings));
|
||||
}
|
||||
console.log(getSettings());
|
||||
|
||||
module.exports = {
|
||||
getSettings,
|
||||
|
5
email/getConfig.js
Normal file
5
email/getConfig.js
Normal file
@ -0,0 +1,5 @@
|
||||
const frappe = require('frappejs');
|
||||
module.exports = async function getData() {
|
||||
account = await frappe.getDoc('EmailAccount');
|
||||
return account;
|
||||
}
|
46
email/sender.js
Normal file
46
email/sender.js
Normal file
@ -0,0 +1,46 @@
|
||||
const nodemailer = require('nodemailer');
|
||||
const getConfig = require("./getConfig");
|
||||
const validator = require('./validator');
|
||||
|
||||
module.exports = {
|
||||
'sendMail': async function (mailDetails) {
|
||||
if (!validator.validate(mailDetails.fromEmailAddress)) {
|
||||
console.log("INVALID EMAIL");
|
||||
return false;
|
||||
}
|
||||
|
||||
let account = await getConfig();
|
||||
if (mailDetails.fromEmailAddress == account.email) {
|
||||
if (validator.validate(mailDetails.toEmailAddress)) {
|
||||
mailDetails = {
|
||||
from: mailDetails.fromEmailAddress,
|
||||
to: mailDetails.toEmailAddress,
|
||||
replyTo: mailDetails.toEmailAddress,
|
||||
inReplyTo: mailDetails.replyId,
|
||||
references: [mailDetails.replyId],
|
||||
cc: mailDetails.ccEmailAddress,
|
||||
bcc: mailDetails.bccEmailAddress,
|
||||
subject: mailDetails.subject,
|
||||
text: mailDetails.bodyText,
|
||||
attachments: [{
|
||||
filename: 'Invoice.pdf',
|
||||
path: mailDetails.filePath,
|
||||
contentType: 'application/pdf'
|
||||
}],
|
||||
};
|
||||
let transporter = nodemailer.createTransport({
|
||||
service: 'gmail',
|
||||
auth: {
|
||||
user: account.email,
|
||||
pass: account.password,
|
||||
}
|
||||
});
|
||||
transporter.sendMail(mailDetails);
|
||||
return true;
|
||||
} else {
|
||||
console.log("Sender Email Invalid");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
11
email/validator.js
Normal file
11
email/validator.js
Normal file
@ -0,0 +1,11 @@
|
||||
// + Check Confirm Password == Password
|
||||
|
||||
module.exports = {
|
||||
validate: function (email) {
|
||||
var reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
|
||||
if (reg.test(email) == false) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -8,7 +8,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Asia/Kabul"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "12-20",
|
||||
"fiscal_year_end": "12-21"
|
||||
},
|
||||
"Albania": {
|
||||
"code": "al",
|
||||
@ -169,7 +171,9 @@
|
||||
"Australia/Melbourne",
|
||||
"Australia/Perth",
|
||||
"Australia/Sydney"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "07-01",
|
||||
"fiscal_year_end": "06-30"
|
||||
},
|
||||
"Austria": {
|
||||
"code": "at",
|
||||
@ -223,7 +227,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Asia/Dhaka"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "07-01",
|
||||
"fiscal_year_end": "06-30"
|
||||
},
|
||||
"Barbados": {
|
||||
"code": "bb",
|
||||
@ -488,7 +494,9 @@
|
||||
"America/Whitehorse",
|
||||
"America/Winnipeg",
|
||||
"America/Yellowknife"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "04-01",
|
||||
"fiscal_year_end": "03-31"
|
||||
},
|
||||
"Cape Verde": {
|
||||
"code": "cv",
|
||||
@ -639,7 +647,9 @@
|
||||
"number_format": "#.###,##",
|
||||
"timezones": [
|
||||
"America/Costa_Rica"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "10-01",
|
||||
"fiscal_year_end": "09-30"
|
||||
},
|
||||
"Croatia": {
|
||||
"code": "hr",
|
||||
@ -765,7 +775,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Africa/Cairo"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "07-01",
|
||||
"fiscal_year_end": "06-30"
|
||||
},
|
||||
"El Salvador": {
|
||||
"code": "sv",
|
||||
@ -1113,7 +1125,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Asia/Hong_Kong"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "04-01",
|
||||
"fiscal_year_end": "03-31"
|
||||
},
|
||||
"Hungary": {
|
||||
"code": "hu",
|
||||
@ -1150,7 +1164,9 @@
|
||||
"number_format": "#,##,###.##",
|
||||
"timezones": [
|
||||
"Asia/Kolkata"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "04-01",
|
||||
"fiscal_year_end": "03-31"
|
||||
},
|
||||
"Indonesia": {
|
||||
"code": "id",
|
||||
@ -1175,7 +1191,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Asia/Tehran"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "06-23",
|
||||
"fiscal_year_end": "06-22"
|
||||
},
|
||||
"Iraq": {
|
||||
"code": "iq",
|
||||
@ -1232,7 +1250,9 @@
|
||||
"date_format": "dd/mm/yyyy",
|
||||
"timezones": [
|
||||
"Europe/Rome"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "07-01",
|
||||
"fiscal_year_end": "06-30"
|
||||
},
|
||||
"Ivory Coast": {
|
||||
"code": "ci",
|
||||
@ -1714,7 +1734,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Asia/Rangoon"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "04-01",
|
||||
"fiscal_year_end": "03-31"
|
||||
},
|
||||
"Namibia": {
|
||||
"code": "na",
|
||||
@ -1782,7 +1804,9 @@
|
||||
"timezones": [
|
||||
"Pacific/Auckland",
|
||||
"Pacific/Chatham"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "04-01",
|
||||
"fiscal_year_end": "03-31"
|
||||
},
|
||||
"Nicaragua": {
|
||||
"code": "ni",
|
||||
@ -1878,7 +1902,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Asia/Karachi"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "07-01",
|
||||
"fiscal_year_end": "06-30"
|
||||
},
|
||||
"Palau": {
|
||||
"code": "pw",
|
||||
@ -2191,7 +2217,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Asia/Singapore"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "04-01",
|
||||
"fiscal_year_end": "03-31"
|
||||
},
|
||||
"Sint Maarten (Dutch part)": {
|
||||
"code": "sx",
|
||||
@ -2254,7 +2282,9 @@
|
||||
"number_format": "# ###.##",
|
||||
"timezones": [
|
||||
"Africa/Johannesburg"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "03-01",
|
||||
"fiscal_year_end": "02-28"
|
||||
},
|
||||
"South Georgia and the South Sandwich Islands": {
|
||||
"code": "gs",
|
||||
@ -2397,7 +2427,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Asia/Bangkok"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "10-01",
|
||||
"fiscal_year_end": "09-30"
|
||||
},
|
||||
"Timor-Leste": {
|
||||
"code": "tl",
|
||||
@ -2547,7 +2579,9 @@
|
||||
"number_format": "#,###.##",
|
||||
"timezones": [
|
||||
"Europe/London"
|
||||
]
|
||||
],
|
||||
"fiscal_year_start": "04-01",
|
||||
"fiscal_year_end": "03-31"
|
||||
},
|
||||
"United States": {
|
||||
"code": "us",
|
||||
|
176
fixtures/verified/in.json
Normal file
176
fixtures/verified/in.json
Normal file
@ -0,0 +1,176 @@
|
||||
{
|
||||
"country_code": "in",
|
||||
"name": "India - Chart of Accounts",
|
||||
"tree": {
|
||||
"Application of Funds (Assets)": {
|
||||
"Current Assets": {
|
||||
"Accounts Receivable": {
|
||||
"Debtors": {
|
||||
"is_group": 0,
|
||||
"account_type": "Receivable"
|
||||
}
|
||||
},
|
||||
"Bank Accounts": {
|
||||
"account_type": "Bank",
|
||||
"is_group": 1
|
||||
},
|
||||
"Cash In Hand": {
|
||||
"Cash": {
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"Loans and Advances (Assets)": {
|
||||
"is_group": 1
|
||||
},
|
||||
"Securities and Deposits": {
|
||||
"Earnest Money": {}
|
||||
},
|
||||
"Stock Assets": {
|
||||
"Stock In Hand": {
|
||||
"account_type": "Stock"
|
||||
},
|
||||
"account_type": "Stock"
|
||||
},
|
||||
"Tax Assets": {
|
||||
"is_group": 1
|
||||
}
|
||||
},
|
||||
"Fixed Assets": {
|
||||
"Capital Equipments": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Electronic Equipments": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Furnitures and Fixtures": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Office Equipments": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Plants and Machineries": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Buildings": {
|
||||
"account_type": "Fixed Asset"
|
||||
},
|
||||
"Accumulated Depreciations": {
|
||||
"account_type": "Accumulated Depreciation"
|
||||
}
|
||||
},
|
||||
"Investments": {
|
||||
"is_group": 1
|
||||
},
|
||||
"Temporary Accounts": {
|
||||
"Temporary Opening": {
|
||||
"account_type": "Temporary"
|
||||
}
|
||||
},
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Expenses": {
|
||||
"Direct Expenses": {
|
||||
"Stock Expenses": {
|
||||
"Cost of Goods Sold": {
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"Expenses Included In Valuation": {
|
||||
"account_type": "Expenses Included In Valuation"
|
||||
},
|
||||
"Stock Adjustment": {
|
||||
"account_type": "Stock Adjustment"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Indirect Expenses": {
|
||||
"Administrative Expenses": {},
|
||||
"Commission on Sales": {},
|
||||
"Depreciation": {
|
||||
"account_type": "Depreciation"
|
||||
},
|
||||
"Entertainment Expenses": {},
|
||||
"Freight and Forwarding Charges": {
|
||||
"account_type": "Chargeable"
|
||||
},
|
||||
"Legal Expenses": {},
|
||||
"Marketing Expenses": {},
|
||||
"Miscellaneous Expenses": {},
|
||||
"Office Maintenance Expenses": {},
|
||||
"Office Rent": {},
|
||||
"Postal Expenses": {},
|
||||
"Print and Stationary": {},
|
||||
"Rounded Off": {
|
||||
"account_type": "Round Off"
|
||||
},
|
||||
"Salary": {},
|
||||
"Sales Expenses": {},
|
||||
"Telephone Expenses": {},
|
||||
"Travel Expenses": {},
|
||||
"Utility Expenses": {},
|
||||
"Write Off": {},
|
||||
"Exchange Gain/Loss": {},
|
||||
"Gain/Loss on Asset Disposal": {}
|
||||
},
|
||||
"root_type": "Expense"
|
||||
},
|
||||
"Income": {
|
||||
"Direct Income": {
|
||||
"Sales": {
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"Service": {
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"Indirect Income": {
|
||||
"account_type": "Income Account",
|
||||
"is_group": 1
|
||||
},
|
||||
"root_type": "Income"
|
||||
},
|
||||
"Source of Funds (Liabilities)": {
|
||||
"Capital Account": {
|
||||
"Reserves and Surplus": {},
|
||||
"Shareholders Funds": {}
|
||||
},
|
||||
"Current Liabilities": {
|
||||
"Accounts Payable": {
|
||||
"Creditors": {
|
||||
"account_type": "Payable"
|
||||
},
|
||||
"Payroll Payable": {}
|
||||
},
|
||||
"Stock Liabilities": {
|
||||
"Stock Received But Not Billed": {
|
||||
"account_type": "Stock Received But Not Billed"
|
||||
}
|
||||
},
|
||||
"Duties and Taxes": {
|
||||
"TDS": {
|
||||
"account_type": "Tax"
|
||||
},
|
||||
"IGST": {
|
||||
"accountType": "Tax"
|
||||
},
|
||||
"CGST": {
|
||||
"accountType": "Tax"
|
||||
},
|
||||
"SGST": {
|
||||
"accountType": "Tax"
|
||||
},
|
||||
"Exempt": {
|
||||
"accountType": "Tax"
|
||||
}
|
||||
},
|
||||
"Loans (Liabilities)": {
|
||||
"Secured Loans": {},
|
||||
"Unsecured Loans": {},
|
||||
"Bank Overdraft Account": {}
|
||||
}
|
||||
},
|
||||
"root_type": "Liability"
|
||||
}
|
||||
}
|
||||
}
|
69
models/doctype/Account/importCOA.js
Normal file
69
models/doctype/Account/importCOA.js
Normal file
@ -0,0 +1,69 @@
|
||||
const frappe = require('frappejs');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const countries = require('../../../fixtures/countryInfo.json');
|
||||
const standardCOA = require('../../../fixtures/verified/standardCOA.json');
|
||||
const accountFields = ['accountType', 'rootType', 'isGroup', 'account_type', 'root_type', 'is_group'];
|
||||
|
||||
async function importAccounts(children, parent, rootType, rootAccount) {
|
||||
for (let accountName in children) {
|
||||
const child = children[accountName];
|
||||
|
||||
if (rootAccount) {
|
||||
rootType = child.rootType || child.root_type;
|
||||
}
|
||||
|
||||
if (!accountFields.includes(accountName)) {
|
||||
let isGroup = identifyIsGroup(child);
|
||||
const doc = frappe.newDoc({
|
||||
doctype: 'Account',
|
||||
name: accountName,
|
||||
parentAccount: parent,
|
||||
isGroup,
|
||||
rootType,
|
||||
balance: 0,
|
||||
accountType: child.accountType
|
||||
})
|
||||
|
||||
await doc.insert()
|
||||
|
||||
await importAccounts(child, accountName, rootType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function identifyIsGroup(child) {
|
||||
if (child.isGroup || child.is_group) {
|
||||
return child.isGroup || child.is_group;
|
||||
}
|
||||
|
||||
const keys = Object.keys(child);
|
||||
const children = keys.filter(key => !accountFields.includes(key))
|
||||
|
||||
if (children.length) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
async function getCountryCOA(){
|
||||
const doc = await frappe.getDoc('AccountingSettings');
|
||||
const conCode = countries[doc.country].code;
|
||||
|
||||
const countryCOA = path.resolve(path.join('./fixtures/verified/', conCode + '.json'));
|
||||
|
||||
if(fs.existsSync(countryCOA)){
|
||||
const jsonText = fs.readFileSync(countryCOA, 'utf-8');
|
||||
const json = JSON.parse(jsonText);
|
||||
return json.tree;
|
||||
} else {
|
||||
return standardCOA;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = async function importCharts() {
|
||||
const chart = await getCountryCOA();
|
||||
await importAccounts(chart, '', '', true)
|
||||
}
|
||||
|
@ -51,12 +51,12 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
fieldname: "referenceType",
|
||||
label: "Reference Type",
|
||||
label: "Ref. Type",
|
||||
fieldtype: "Data",
|
||||
},
|
||||
{
|
||||
fieldname: "referenceName",
|
||||
label: "Reference Name",
|
||||
label: "Ref. Name",
|
||||
fieldtype: "DynamicLink",
|
||||
references: "referenceType"
|
||||
},
|
||||
|
@ -9,13 +9,12 @@ module.exports = {
|
||||
isSubmittable: 0,
|
||||
settings: null,
|
||||
keywordFields: [],
|
||||
fields: [
|
||||
{
|
||||
fields: [{
|
||||
label: "Company Name",
|
||||
fieldname: "companyName",
|
||||
fieldtype: "Data",
|
||||
required: 1,
|
||||
disabled: 1
|
||||
disabled: 0
|
||||
},
|
||||
|
||||
{
|
||||
|
@ -45,11 +45,6 @@ module.exports = {
|
||||
"fieldtype": "Data",
|
||||
"required": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "county",
|
||||
"label": "County",
|
||||
"fieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"fieldname": "state",
|
||||
"label": "State",
|
||||
@ -93,11 +88,11 @@ module.exports = {
|
||||
}
|
||||
],
|
||||
|
||||
events: {
|
||||
validate: (doc) => {
|
||||
// events: {
|
||||
// validate: (doc) => {
|
||||
|
||||
}
|
||||
},
|
||||
// }
|
||||
// },
|
||||
|
||||
listSettings: {
|
||||
getFields(list) {
|
||||
@ -113,11 +108,17 @@ module.exports = {
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
fields: [ "addressTitle", "addressType", "addressLine1",
|
||||
"addressLine2", "city", "county", "state", "country",
|
||||
"postalCode"]
|
||||
fields: [
|
||||
"addressTitle", "addressType", "addressLine1",
|
||||
"addressLine2", "city", "country", "state",
|
||||
"postalCode"
|
||||
]
|
||||
},
|
||||
{ fields: [ "emailAddress", "phone", "fax", "isPreferredBilling", "isShippingBilling" ] }
|
||||
{
|
||||
fields: [
|
||||
"emailAddress", "phone", "fax", "isPreferredBilling", "isShippingBilling"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
27
models/doctype/CompanySettings/CompanySettings.js
Normal file
27
models/doctype/CompanySettings/CompanySettings.js
Normal file
@ -0,0 +1,27 @@
|
||||
module.exports = {
|
||||
"name": "CompanySettings",
|
||||
"label": "Company Settings",
|
||||
"naming": "autoincrement",
|
||||
"isSingle": true,
|
||||
"isChild": false,
|
||||
"keywordFields": [
|
||||
"companyName"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "companyName",
|
||||
"label": "Company Name",
|
||||
"fieldtype": "Data",
|
||||
"disabled": false,
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"fieldname": "companyAddress",
|
||||
"label": "Company Address",
|
||||
"fieldtype": "Link",
|
||||
"disabled": false,
|
||||
"required": true,
|
||||
"target": "Address"
|
||||
}
|
||||
]
|
||||
}
|
72
models/doctype/Email/Email.js
Normal file
72
models/doctype/Email/Email.js
Normal file
@ -0,0 +1,72 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = {
|
||||
"name": "Email",
|
||||
"doctype": "DocType",
|
||||
"pageSettings": {
|
||||
hideTitle: true
|
||||
},
|
||||
"isSingle": 0,
|
||||
"isChild": 0,
|
||||
"keywordFields": ["name"],
|
||||
"fields": [{
|
||||
"fieldname": "name",
|
||||
"label": "name",
|
||||
"fieldtype": "Data",
|
||||
"required": 0,
|
||||
"hidden": 1,
|
||||
"disabled": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "fromEmailAddress",
|
||||
"label": "From",
|
||||
"fieldtype": "Data",
|
||||
"required": 1,
|
||||
"hidden": 0,
|
||||
formula: async () => {
|
||||
const accountingSettings = await frappe.getDoc('AccountingSettings');
|
||||
return accountingSettings.email;
|
||||
},
|
||||
"disabled": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "toEmailAddress",
|
||||
"label": "To",
|
||||
"fieldtype": "Data",
|
||||
"required": 1,
|
||||
"hidden": 0,
|
||||
"disabled": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "date",
|
||||
"label": "Date",
|
||||
"fieldtype": "Datetime",
|
||||
"required": 0,
|
||||
"hidden": 0,
|
||||
"disabled": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "subject",
|
||||
"label": "Subject",
|
||||
"fieldtype": "Data",
|
||||
"required": 0,
|
||||
"hidden": 0,
|
||||
"disabled": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "bodyText",
|
||||
"label": "Body",
|
||||
"fieldtype": "Text",
|
||||
"required": 0,
|
||||
"hidden": 0,
|
||||
"disabled": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "filePath",
|
||||
"label": "File Path",
|
||||
"fieldtype": "Text",
|
||||
"required": 0,
|
||||
"hidden": 1,
|
||||
}
|
||||
]
|
||||
}
|
59
models/doctype/EmailAccount/EmailAccount.js
Normal file
59
models/doctype/EmailAccount/EmailAccount.js
Normal file
@ -0,0 +1,59 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = {
|
||||
"name": "EmailAccount",
|
||||
"label": "Email Account",
|
||||
"doctype": "DocType",
|
||||
"isSingle": true,
|
||||
"isChild": false,
|
||||
"keywordFields": [
|
||||
"email"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "email",
|
||||
"label": "Email",
|
||||
"fieldtype": "Data",
|
||||
"required": 1,
|
||||
formula: async () => {
|
||||
const accountingSettings = await frappe.getDoc('AccountingSettings');
|
||||
return accountingSettings.email;
|
||||
},
|
||||
},
|
||||
{
|
||||
"fieldname": "password",
|
||||
"label": "Password",
|
||||
"fieldtype": "Password",
|
||||
"required": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "confirmPassword",
|
||||
"label": "Confirm Password",
|
||||
"fieldtype": "Password",
|
||||
"required": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "smtpHost",
|
||||
"label": "SMTP Host",
|
||||
"fieldtype": "Select",
|
||||
"options": [
|
||||
"smtp.gmail.com",
|
||||
"smtp.mail.yahoo.com",
|
||||
"smtp-mail.outlook.com",
|
||||
"smtp.mail.me.com",
|
||||
"smtp.aol.com"
|
||||
],
|
||||
"default": "smtp.gmail.com"
|
||||
},
|
||||
{
|
||||
"fieldname": "smtpPort",
|
||||
"label": "SMTP Port",
|
||||
"fieldtype": "Select",
|
||||
"options": [
|
||||
"465",
|
||||
"587"
|
||||
],
|
||||
"default": "465"
|
||||
}
|
||||
]
|
||||
}
|
@ -18,7 +18,8 @@ module.exports = {
|
||||
{
|
||||
fieldname: 'date',
|
||||
label: 'Date',
|
||||
fieldtype: 'Date'
|
||||
fieldtype: 'Date',
|
||||
// default: (new Date()).toISOString()
|
||||
},
|
||||
{
|
||||
fieldname: 'customer',
|
||||
|
@ -1,73 +1,61 @@
|
||||
<template>
|
||||
<div class="print-view p-5">
|
||||
<h1>{{ doc.name }}</h1>
|
||||
<div class="row py-4">
|
||||
<div class="col-6">
|
||||
<div><b>{{ _("Customer") }}</b></div>
|
||||
<div>{{ doc.customer }}</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div><b>{{ _("Date") }}</b></div>
|
||||
<div>{{ frappe.format(doc.date, 'Date') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style='width: 30px'></th>
|
||||
<th>{{ _("Item") }}</th>
|
||||
<th class='text-right'>{{ _("Qty") }}</th>
|
||||
<th class='text-right'>{{ _("Rate") }}</th>
|
||||
<th class='text-right'>{{ _("Amount") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in doc.items" :key="row.idx">
|
||||
<td class='text-right'>{{ row.idx + 1 }}</td>
|
||||
<td>{{ row.item }}<br>{{ frappe.format(row.description, 'Text') }}</td>
|
||||
<td class='text-right'>{{ row.quantity }}</td>
|
||||
<td class='text-right'>{{ frappe.format(row.rate, 'Currency') }}</td>
|
||||
<td class='text-right'>{{ frappe.format(row.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class='row'>
|
||||
<div class='col-6'></div>
|
||||
<div class='col-6'>
|
||||
<div class='row'>
|
||||
<div class='col-6'>
|
||||
{{ _("Total") }}
|
||||
</div>
|
||||
<div class='col-6 text-right'>
|
||||
{{ frappe.format(doc.netTotal, 'Currency')}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='row' v-for="tax in doc.taxes" :key="tax.name">
|
||||
<div class='col-6'>
|
||||
{{ tax.account }} ({{ tax.rate }}%)
|
||||
</div>
|
||||
<div class='col-6 text-right'>
|
||||
{{ frappe.format(tax.amount, 'Currency')}}
|
||||
</div>
|
||||
</div>
|
||||
<div class='row py-3'>
|
||||
<div class='col-6'>
|
||||
<h5>{{ _("Grand Total") }}</h5>
|
||||
</div>
|
||||
<div class='col-6 text-right'>
|
||||
<h5>{{ frappe.format(doc.grandTotal, 'Currency')}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='py-3'>
|
||||
{{ frappe.format(doc.terms, 'Text') }}
|
||||
</div>
|
||||
</div>
|
||||
<component :themeColor="color" :font="fontFamily" :is="invoiceTemplate" v-if="doc" :doc="doc"/>
|
||||
</template>
|
||||
<script>
|
||||
import InvoiceTemplate1 from '@/../models/doctype/Invoice/Templates/InvoiceTemplate1';
|
||||
import InvoiceTemplate2 from '@/../models/doctype/Invoice/Templates/InvoiceTemplate2';
|
||||
import InvoiceTemplate3 from '@/../models/doctype/Invoice/Templates/InvoiceTemplate3';
|
||||
|
||||
const invoiceTemplates = {
|
||||
'Basic I': InvoiceTemplate1,
|
||||
'Basic II': InvoiceTemplate2,
|
||||
'Modern': InvoiceTemplate3
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'InvoicePrint',
|
||||
props: ['doc']
|
||||
}
|
||||
</script>
|
||||
props: ['doc', 'themeColor', 'template', 'font'],
|
||||
data() {
|
||||
return {
|
||||
color: undefined,
|
||||
fontFamily: undefined,
|
||||
invoiceTemplate: undefined
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
themeColor: async function() {
|
||||
await this.loadInvoice();
|
||||
},
|
||||
font: async function() {
|
||||
await this.loadInvoice();
|
||||
},
|
||||
template: async function() {
|
||||
await this.loadInvoice();
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.loadInvoice();
|
||||
},
|
||||
methods: {
|
||||
async loadInvoice() {
|
||||
this.color = this.themeColor !== undefined ? this.themeColor : await this.getColor();
|
||||
this.fontFamily = this.font !== undefined ? this.font : await this.getFont();
|
||||
let template = this.template !== undefined ? this.template : await this.getTemplate();
|
||||
let templateFile = invoiceTemplates[template];
|
||||
this.invoiceTemplate = templateFile;
|
||||
},
|
||||
async getTemplate() {
|
||||
let invoiceSettings = await frappe.getDoc('InvoiceSettings');
|
||||
return invoiceSettings.template;
|
||||
},
|
||||
async getColor() {
|
||||
let invoiceSettings = await frappe.getDoc('InvoiceSettings');
|
||||
return invoiceSettings.themeColor;
|
||||
},
|
||||
async getFont() {
|
||||
let invoiceSettings = await frappe.getDoc('InvoiceSettings');
|
||||
return invoiceSettings.font;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
28
models/doctype/Invoice/Templates/AddressDetails.js
Normal file
28
models/doctype/Invoice/Templates/AddressDetails.js
Normal file
@ -0,0 +1,28 @@
|
||||
async function getCompanyDetails() {
|
||||
let companyDetails = {
|
||||
name: null,
|
||||
address: {}
|
||||
};
|
||||
|
||||
let companySettings = await frappe.getDoc('CompanySettings');
|
||||
companyDetails.name = companySettings.companyName;
|
||||
|
||||
let companyAddress = await getAddress(companySettings.companyAddress);
|
||||
companyDetails.address = companyAddress;
|
||||
return companyDetails;
|
||||
}
|
||||
|
||||
async function getCustomerAddress(customer) {
|
||||
let customers = await frappe.db.getAll({ doctype: 'Party', fields:['name, address'], filters: { name: customer }});
|
||||
let customerDetails = await frappe.getDoc('Party', customers[0].name);
|
||||
return await getAddress(customerDetails.address);
|
||||
}
|
||||
|
||||
async function getAddress(addressName) {
|
||||
return await frappe.getDoc('Address', addressName);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getCompanyDetails,
|
||||
getCustomerAddress
|
||||
}
|
38
models/doctype/Invoice/Templates/CompanyAddress.vue
Normal file
38
models/doctype/Invoice/Templates/CompanyAddress.vue
Normal file
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div v-if="detailsPresent">
|
||||
<p :style="[$.bold]" style="font-size: 1.3em">{{ companyDetails.name }}</p>
|
||||
<p :style="$.paraStyle">{{ companyDetails.address.addressLine1 }}</p>
|
||||
<p :style="$.paraStyle">{{ companyDetails.address.addressLine2 }}</p>
|
||||
<p :style="$.paraStyle">
|
||||
{{ companyDetails.address.city + ' ' + companyDetails.address.state }}
|
||||
</p>
|
||||
<p :style="$.paraStyle">
|
||||
{{ companyDetails.address.country + ' - ' + companyDetails.address.postalCode }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import addressDetails from './AddressDetails';
|
||||
import styles from './InvoiceStyles';
|
||||
export default {
|
||||
name: 'CompanyAddress',
|
||||
data() {
|
||||
return {
|
||||
$: styles,
|
||||
detailsPresent: true,
|
||||
companyDetails: {
|
||||
name: null,
|
||||
address: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = styles;
|
||||
try {
|
||||
this.companyDetails = await addressDetails.getCompanyDetails();
|
||||
} catch(e) {
|
||||
this.detailsPresent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
37
models/doctype/Invoice/Templates/CustomerAddress.vue
Normal file
37
models/doctype/Invoice/Templates/CustomerAddress.vue
Normal file
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div v-if="this.detailsPresent">
|
||||
<p :style="[$.bold, $.mediumFontSize]">Billed To</p>
|
||||
<p :style="[$.bold, $.paraStyle]">{{ customer }}</p>
|
||||
<p :style="$.paraStyle">{{ customerAddress.addressLine1 }}</p>
|
||||
<p :style="$.paraStyle">{{ customerAddress.addressLine2 }}</p>
|
||||
<p :style="$.paraStyle">
|
||||
{{ customerAddress.city + ' ' + customerAddress.state }}
|
||||
</p>
|
||||
<p :style="$.paraStyle">
|
||||
{{ customerAddress.country + ' - ' + customerAddress.postalCode }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import addressDetails from './AddressDetails';
|
||||
import styles from './InvoiceStyles';
|
||||
export default {
|
||||
name: 'CustomerAddress',
|
||||
props: ['customer'],
|
||||
data() {
|
||||
return {
|
||||
$: styles,
|
||||
detailsPresent: true,
|
||||
customerAddress: {}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = styles;
|
||||
try {
|
||||
this.customerAddress = await addressDetails.getCustomerAddress(this.customer);
|
||||
} catch(e) {
|
||||
this.detailsPresent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
54
models/doctype/Invoice/Templates/InvoiceStyles.js
Normal file
54
models/doctype/Invoice/Templates/InvoiceStyles.js
Normal file
@ -0,0 +1,54 @@
|
||||
module.exports = {
|
||||
bold: {
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
font: {
|
||||
fontFamily: null
|
||||
},
|
||||
regularFontSize: {
|
||||
fontSize: '0.8rem'
|
||||
},
|
||||
mediumFontSize: {
|
||||
fontSize: '1rem'
|
||||
},
|
||||
paraStyle: {
|
||||
margin: '0.5rem',
|
||||
marginLeft: 0,
|
||||
marginRight: 0
|
||||
},
|
||||
bgColor: {
|
||||
backgroundColor: null
|
||||
},
|
||||
showBorderBottom: {
|
||||
borderBottom: null
|
||||
},
|
||||
hideBorderTop: {
|
||||
borderTop: '0px solid black'
|
||||
},
|
||||
showBorderRight: {
|
||||
borderRight: '1px solid #e0e0d1'
|
||||
},
|
||||
tablePadding: {
|
||||
paddingTop: '3%',
|
||||
paddingBottom: '3%'
|
||||
},
|
||||
fontColor: {
|
||||
color: null
|
||||
},
|
||||
headerColor: {
|
||||
backgroundColor: null,
|
||||
color: 'white'
|
||||
},
|
||||
showNoticeBorderBottom: {
|
||||
borderBottom: null
|
||||
},
|
||||
showBorderTop: {
|
||||
borderTop: null
|
||||
},
|
||||
headerFontColor: {
|
||||
color: null
|
||||
},
|
||||
showBorderTop: {
|
||||
borderTop: null
|
||||
}
|
||||
}
|
108
models/doctype/Invoice/Templates/InvoiceTemplate1.vue
Normal file
108
models/doctype/Invoice/Templates/InvoiceTemplate1.vue
Normal file
@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<div :style="$.font" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters pl-5 pr-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-6">
|
||||
<company-address />
|
||||
</div>
|
||||
<div :style="$.regularFontSize" class="col-6 text-right">
|
||||
<h2 :style="$.headerFontColor">INVOICE</h2>
|
||||
<p :style="$.paraStyle"><strong>{{ doc.name }}</strong></p>
|
||||
<p :style="$.paraStyle">{{ frappe.format(doc.date, 'Date') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-6 mt-1">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
</div>
|
||||
<div :style="$.regularFontSize" class="row pl-5 pr-5 mt-5">
|
||||
<div class="col-12">
|
||||
<table class="table p-0">
|
||||
<thead>
|
||||
<tr :style="$.showBorderBottom">
|
||||
<th :style="$.hideBorderTop" class="text-left pl-0" style="width: 10%">{{ _("NO") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left" style="width: 60%">{{ _("ITEM") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left pl-0" style="width: 20%">{{ _("RATE") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-left" style="width: 15%">{{ _("QTY") }}</th>
|
||||
<th :style="$.hideBorderTop" class="text-right pr-1" style="width: 20%">{{ _("AMOUNT") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in doc.items" :key="row.idx">
|
||||
<td class="text-left pl-1">{{ row.idx + 1 }}</td>
|
||||
<td class="text-left">{{ row.item }}</td>
|
||||
<td class="text-left pl-0">{{ frappe.format(row.rate, 'Currency') }}</td>
|
||||
<td class="text-left">{{ row.quantity }}</td>
|
||||
<td class="text-right pr-1">{{ frappe.format(row.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="$.bold" class="text-left pl-0">SUBTOTAL</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ frappe.format(doc.netTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="$.bold" class="text-left pl-0">{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ frappe.format(tax.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.mediumFontSize, $.showBorderTop]" class="text-left pl-0">TOTAL</td>
|
||||
<td :style="[$.bold, $.mediumFontSize, $.showBorderTop]" class="text-right pr-1" style="color: green;">
|
||||
{{ frappe.format(doc.grandTotal, 'Currency') }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-5">
|
||||
<div :style="$.regularFontSize" class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showBorderBottom]" ><td :style="$.hideBorderTop" class="pl-0">NOTICE</td></tr>
|
||||
<tr><td class="pl-0">{{ doc.terms }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Styles from './InvoiceStyles';
|
||||
import CompanyAddress from './CompanyAddress';
|
||||
import CustomerAddress from './CustomerAddress';
|
||||
export default {
|
||||
name: 'InvoiceTemplate1',
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
},
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
data() {
|
||||
return {
|
||||
$: Styles
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.headerFontColor.color = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.showBorderTop.borderTop = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
117
models/doctype/Invoice/Templates/InvoiceTemplate2.vue
Normal file
117
models/doctype/Invoice/Templates/InvoiceTemplate2.vue
Normal file
@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div :style="[$.regularFontSize, $.font]" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters p-5" :style="$.headerColor">
|
||||
<div class="col-8 text-left">
|
||||
<h1>INVOICE</h1>
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<company-address />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row p-5 mt-4">
|
||||
<div class="col-4">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<p :style="[$.bold, $.mediumFontSize]">Invoice Number</p>
|
||||
<p :style="$.paraStyle">{{ doc.name }}</p><br>
|
||||
<p :style="[$.bold, $.mediumFontSize]">Date</p>
|
||||
<p :style="$.paraStyle">{{doc.date}}</p>
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<p :style="[$.bold, $.mediumFontSize]">Invoice Total</p>
|
||||
<h2 :style="$.fontColor">{{ frappe.format(doc.grandTotal, 'Currency') }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-3">
|
||||
<div class="col-12">
|
||||
<table class="table table-borderless p-0">
|
||||
<thead>
|
||||
<tr :style="[$.showBorderTop, $.fontColor]">
|
||||
<th class="text-left pl-0" style="width: 10%">{{ _("NO") }}</th>
|
||||
<th class="text-left" style="width: 60%">{{ _("ITEM") }}</th>
|
||||
<th class="text-left pl-0" style="width: 20%">{{ _("RATE") }}</th>
|
||||
<th class="text-left" style="width: 15%">{{ _("QTY") }}</th>
|
||||
<th class="text-right pr-1" style="width: 20%">{{ _("AMOUNT") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr :style="$.showBorderBottom" v-for="row in doc.items" :key="row.idx">
|
||||
<td class="text-left pl-1">{{ row.idx + 1 }}</td>
|
||||
<td class="text-left">{{ row.item }}</td>
|
||||
<td class="text-left pl-0">{{ frappe.format(row.rate, 'Currency') }}</td>
|
||||
<td class="text-left">{{ row.quantity }}</td>
|
||||
<td class="text-right pr-1">{{ frappe.format(row.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr><td colspan="5" style="padding: 4%"></td></tr>
|
||||
<tr>
|
||||
<td colspan="2" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.fontColor]" class="text-left pl-0">SUBTOTAL</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ frappe.format(doc.netTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.fontColor]" class="text-left pl-0">{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.bold" class="text-right pr-1">{{ frappe.format(tax.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="$.hideBorderTop" class="text-left pl-1"></td>
|
||||
<td colspan="2" :style="[$.bold, $.fontColor, $.mediumFontSize]" class="text-left pl-0">TOTAL</td>
|
||||
<td :style="[$.bold, $.mediumFontSize]" class="text-right pr-1">{{ frappe.format(doc.grandTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row pl-5 pr-5 mt-5">
|
||||
<div class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showNoticeBorderBottom]" ><td :style="$.hideBorderTop" class="pl-0">NOTICE</td></tr>
|
||||
<tr><td class="pl-0">{{ doc.terms }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Styles from './InvoiceStyles';
|
||||
import CompanyAddress from './CompanyAddress';
|
||||
import CustomerAddress from './CustomerAddress';
|
||||
export default {
|
||||
name: 'InvoicePrint',
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
$: Styles,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.fontColor.color = this.themeColor;
|
||||
this.$.headerColor.backgroundColor = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = '0.1rem solid #e0e0d1';
|
||||
this.$.showNoticeBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.showBorderTop.borderTop = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
113
models/doctype/Invoice/Templates/InvoiceTemplate3.vue
Normal file
113
models/doctype/Invoice/Templates/InvoiceTemplate3.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div :style="[$.regularFontSize, $.font]" style="font-family: sans-serif;">
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6" :style="$.bgColor"></div>
|
||||
<div class="col-4 text-center" style="vertical-align: middle">
|
||||
<h1>INVOICE</h1>
|
||||
</div>
|
||||
<div class="col-2" :style="$.bgColor"></div>
|
||||
</div>
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6 text-left pl-5">
|
||||
<company-address />
|
||||
</div>
|
||||
<div class="col-6 pr-5 text-right">
|
||||
<p :style="[$.bold, $.paraStyle, $.mediumFontSize]">{{ doc.name }}</p>
|
||||
<p :style="$.paraStyle">{{ frappe.format(doc.date, 'Date') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row no-gutters mt-5">
|
||||
<div class="col-6 text-left pl-5">
|
||||
<customer-address :customer="doc.customer" />
|
||||
</div>
|
||||
<div class="col-6"></div>
|
||||
</div>
|
||||
<div class="row mt-5 no-gutters">
|
||||
<div class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td :style="[$.bold, $.showBorderRight, $.tablePadding]" style="width: 15" class="pl-5">{{ _("NO") }}</td>
|
||||
<td :style="[$.bold, $.showBorderRight, $.tablePadding]" style="width: 40%">{{ _("ITEM") }}</td>
|
||||
<td class="text-left" :style="[$.bold, $.showBorderRight, $.tablePadding]" style="width: 20%">{{ _("RATE") }}</td>
|
||||
<td :style="[$.bold, $.showBorderRight, $.tablePadding]" style="width: 10%">{{ _("QTY") }}</td>
|
||||
<td class="text-right pr-5" :style="[$.bold, $.tablePadding]" style="width: 20%">{{ _("AMOUNT") }}</td>
|
||||
</tr>
|
||||
<tr v-for="row in doc.items" :key="row.idx">
|
||||
<td :style="$.tablePadding" class="pl-5 pr-5">{{ row.idx + 1 }}</td>
|
||||
<td :style="$.tablePadding">{{ row.item }}</td>
|
||||
<td :style="$.tablePadding" class="text-left">{{ frappe.format(row.rate, 'Currency') }}</td>
|
||||
<td :style="$.tablePadding">{{ row.quantity }}</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ frappe.format(row.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr><td colspan="5" style="padding: 4%"></td></tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td :style="$.tablePadding" colspan="2">SUBTOTAL</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ frappe.format(doc.netTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr v-for="tax in doc.taxes" :key="tax.name">
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td :style="$.tablePadding" med colspan="2">{{ tax.account.toUpperCase() }} ({{ tax.rate }}%)</td>
|
||||
<td :style="$.tablePadding" class="text-right pr-5">{{ frappe.format(tax.amount, 'Currency') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" :style="[$.hideBorderTop, $.tablePadding]"></td>
|
||||
<td :style="[$.bold, $.tablePadding, $.mediumFontSize]" colspan="2">TOTAL</td>
|
||||
<td :style="[$.bold, $.tablePadding, $.mediumFontSize]" class="text-right pr-5">{{ frappe.format(doc.grandTotal, 'Currency') }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-5">
|
||||
<div :style="$.regularFontSize" class="col-12">
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr :style="[$.bold, $.showBorderBottom]" >
|
||||
<td :style="$.hideBorderTop" class="pl-5">NOTICE</td>
|
||||
</tr>
|
||||
<tr><td class="pl-5">{{ doc.terms }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Styles from './InvoiceStyles';
|
||||
import CompanyAddress from './CompanyAddress';
|
||||
import CustomerAddress from './CustomerAddress';
|
||||
export default {
|
||||
name: 'InvoicePrint',
|
||||
props: ['doc', 'themeColor', 'font'],
|
||||
components: {
|
||||
CompanyAddress,
|
||||
CustomerAddress
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
$: Styles,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
themeColor: function() {
|
||||
this.setTheme();
|
||||
},
|
||||
font: function() {
|
||||
this.setTheme();
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$ = Styles;
|
||||
this.setTheme();
|
||||
},
|
||||
methods: {
|
||||
setTheme() {
|
||||
this.$.bgColor.backgroundColor = this.themeColor;
|
||||
this.$.showBorderBottom.borderBottom = `0.22rem solid ${this.themeColor}`;
|
||||
this.$.font.fontFamily = this.font;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -5,8 +5,7 @@ module.exports = {
|
||||
isChild: 1,
|
||||
keywordFields: [],
|
||||
layout: 'ratio',
|
||||
fields: [
|
||||
{
|
||||
fields: [{
|
||||
fieldname: 'item',
|
||||
label: 'Item',
|
||||
fieldtype: 'Link',
|
||||
|
@ -1,18 +1,42 @@
|
||||
module.exports = {
|
||||
"name": "InvoiceSettings",
|
||||
"label": "Invoice Settings",
|
||||
"doctype": "DocType",
|
||||
"isSingle": 1,
|
||||
"isChild": 0,
|
||||
"keywordFields": [],
|
||||
"fields": [
|
||||
name: "InvoiceSettings",
|
||||
label: "Invoice Settings",
|
||||
doctype: "DocType",
|
||||
isSingle: 1,
|
||||
isChild: 0,
|
||||
keywordFields: [],
|
||||
fields: [
|
||||
{
|
||||
"fieldname": "numberSeries",
|
||||
"label": "Number Series",
|
||||
"fieldtype": "Link",
|
||||
"target": "NumberSeries",
|
||||
"required": 1,
|
||||
"default": "INV"
|
||||
fieldname: "numberSeries",
|
||||
label: "Number Series",
|
||||
fieldtype: "Link",
|
||||
target: "NumberSeries",
|
||||
required: 1,
|
||||
default: "INV"
|
||||
},
|
||||
{
|
||||
fieldname: "template",
|
||||
label: "Template",
|
||||
fieldtype: "Select",
|
||||
options: ["Basic I", "Basic II", "Modern"],
|
||||
required: 1,
|
||||
default: "Basic I"
|
||||
},
|
||||
{
|
||||
fieldname: "font",
|
||||
label: "Font",
|
||||
fieldtype: "Select",
|
||||
options: ["Montserrat", "Open Sans", "Oxygen", "Merriweather"],
|
||||
required: 1,
|
||||
default: "Montserrat"
|
||||
},
|
||||
{
|
||||
fieldname: "themeColor",
|
||||
label: "Theme Color",
|
||||
fieldtype: "Data",
|
||||
required: 1,
|
||||
default: "#000000",
|
||||
hidden: 1
|
||||
}
|
||||
]
|
||||
}
|
@ -6,8 +6,7 @@ module.exports = {
|
||||
'name',
|
||||
'description'
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
fields: [{
|
||||
fieldname: 'name',
|
||||
label: 'Item Name',
|
||||
fieldtype: 'Data',
|
||||
@ -59,25 +58,31 @@ module.exports = {
|
||||
layout: [
|
||||
// section 1
|
||||
{
|
||||
columns: [
|
||||
{ fields: ['name', 'unit'] },
|
||||
{ fields: ['rate'] }
|
||||
columns: [{
|
||||
fields: ['name', 'unit']
|
||||
},
|
||||
{
|
||||
fields: ['rate']
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// section 2
|
||||
{
|
||||
columns: [
|
||||
{ fields: ['description'] }
|
||||
]
|
||||
columns: [{
|
||||
fields: ['description']
|
||||
}]
|
||||
},
|
||||
|
||||
// section 3
|
||||
{
|
||||
title: 'Accounting',
|
||||
columns: [
|
||||
{ fields: ['incomeAccount', 'expenseAccount'] },
|
||||
{ fields: ['tax'] }
|
||||
columns: [{
|
||||
fields: ['incomeAccount', 'expenseAccount']
|
||||
},
|
||||
{
|
||||
fields: ['tax']
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -6,24 +6,29 @@ module.exports = {
|
||||
"keywordFields": [
|
||||
"name"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fields": [{
|
||||
"fieldname": "name",
|
||||
"label": "Name",
|
||||
"fieldtype": "Data",
|
||||
"required": 1
|
||||
},
|
||||
{
|
||||
fieldname: 'defaultAccount',
|
||||
label: 'Default Account',
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
isGroup: 0,
|
||||
accountType: 'Receivable'
|
||||
};
|
||||
}
|
||||
fieldname: "address",
|
||||
label: "Address",
|
||||
fieldtype: "Link",
|
||||
target: "Address"
|
||||
},
|
||||
{
|
||||
fieldname: 'defaultAccount',
|
||||
label: 'Default Account',
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
getFilters: (query, control) => {
|
||||
return {
|
||||
isGroup: 0,
|
||||
accountType: 'Receivable'
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "customer",
|
||||
@ -37,15 +42,13 @@ module.exports = {
|
||||
}
|
||||
],
|
||||
|
||||
links: [
|
||||
{
|
||||
label: 'Invoices',
|
||||
condition: (form) => form.doc.customer,
|
||||
action: form => {
|
||||
form.$router.push({
|
||||
links: [{
|
||||
label: 'Invoices',
|
||||
condition: (form) => form.doc.customer,
|
||||
action: form => {
|
||||
form.$router.push({
|
||||
path: `/report/sales-register?&customer=${form.doc.name}`
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
12
models/doctype/Party/RegionalChanges.js
Normal file
12
models/doctype/Party/RegionalChanges.js
Normal file
@ -0,0 +1,12 @@
|
||||
const party = require('./Party')
|
||||
|
||||
party.fields.splice(3, 0, { //insert at 3rd position
|
||||
fieldname: 'gstin',
|
||||
label: 'GSTIN No.',
|
||||
fieldtype: 'Data',
|
||||
hidden: 0
|
||||
})
|
||||
party.fields.join()
|
||||
const newParty = party
|
||||
|
||||
module.exports = newParty
|
@ -8,11 +8,11 @@ module.exports = {
|
||||
isSubmittable: 1,
|
||||
keywordFields: [],
|
||||
settings: "PaymentSettings",
|
||||
fields: [
|
||||
{
|
||||
fields: [{
|
||||
"fieldname": "date",
|
||||
"label": "Date",
|
||||
"fieldtype": "Date"
|
||||
"label": "Posting Date",
|
||||
"fieldtype": "Date",
|
||||
// default: (new Date()).toISOString()
|
||||
},
|
||||
{
|
||||
fieldname: "party",
|
||||
@ -35,6 +35,23 @@ module.exports = {
|
||||
target: "Account",
|
||||
required: 1
|
||||
},
|
||||
{
|
||||
fieldname: "referenceId",
|
||||
label: "Ref. / Cheque No.",
|
||||
fieldtype: "Data",
|
||||
default: "ABC",
|
||||
required: 1 // TODO: UNIQUE
|
||||
},
|
||||
{
|
||||
fieldname: "referenceDate",
|
||||
label: "Ref. Date",
|
||||
fieldtype: "Date",
|
||||
},
|
||||
{
|
||||
fieldname: "clearanceDate",
|
||||
label: "Clearance Date",
|
||||
fieldtype: "Date",
|
||||
},
|
||||
{
|
||||
fieldname: "amount",
|
||||
label: "Amount",
|
||||
@ -57,26 +74,33 @@ module.exports = {
|
||||
}
|
||||
],
|
||||
|
||||
layout: [
|
||||
{
|
||||
columns: [
|
||||
{ fields: ['date', 'party'] },
|
||||
{ fields: ['account', 'paymentAccount'] },
|
||||
layout: [{
|
||||
columns: [{
|
||||
fields: ['date', 'party']
|
||||
},
|
||||
{
|
||||
fields: ['account', 'paymentAccount']
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
fields: ['for']
|
||||
}
|
||||
]
|
||||
columns: [{
|
||||
fields: ['referenceId']
|
||||
}, {
|
||||
fields: ['referenceDate']
|
||||
},{
|
||||
fields: ['clearanceDate']
|
||||
}]
|
||||
},
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
fields: ['amount', 'writeoff']
|
||||
}
|
||||
]
|
||||
columns: [{
|
||||
fields: ['for']
|
||||
}]
|
||||
},
|
||||
{
|
||||
columns: [{
|
||||
fields: ['amount', 'writeoff']
|
||||
}]
|
||||
}
|
||||
],
|
||||
|
||||
|
@ -1,13 +1,30 @@
|
||||
import { _ } from 'frappejs/utils';
|
||||
import indicators from 'frappejs/ui/constants/indicators';
|
||||
|
||||
export default {
|
||||
doctype: 'Payment',
|
||||
title: _('Payment'),
|
||||
columns: [
|
||||
'party',
|
||||
{
|
||||
label: 'Payment',
|
||||
getValue(doc) {
|
||||
if (doc.submitted === 1 && doc.clearanceDate !== null) {
|
||||
return 'Reconciled';
|
||||
}
|
||||
return 'Not Reconciled';
|
||||
},
|
||||
getIndicator(doc) {
|
||||
if (doc.submitted === 1 && doc.clearanceDate !== null) {
|
||||
return indicators.GREEN;
|
||||
}
|
||||
return indicators.ORANGE;
|
||||
}
|
||||
},
|
||||
'account',
|
||||
'amount',
|
||||
'date',
|
||||
'clearanceDate',
|
||||
'name'
|
||||
]
|
||||
}
|
102
models/doctype/SetupWizard/SetupWizard.js
Normal file
102
models/doctype/SetupWizard/SetupWizard.js
Normal file
@ -0,0 +1,102 @@
|
||||
const countryList = require('../../../fixtures/countryInfo.json');
|
||||
|
||||
module.exports = {
|
||||
name: "SetupWizard",
|
||||
label: "Setup Wizard",
|
||||
naming: "name",
|
||||
isSingle: 1,
|
||||
isChild: 0,
|
||||
isSubmittable: 0,
|
||||
settings: null,
|
||||
keywordFields: [],
|
||||
fields: [{
|
||||
fieldname: 'country',
|
||||
label: 'Country',
|
||||
fieldtype: 'Autocomplete',
|
||||
required: 1,
|
||||
getList: () => Object.keys(countryList).sort()
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'fullname',
|
||||
label: 'Name',
|
||||
fieldtype: 'Data',
|
||||
required: 1
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'email',
|
||||
label: 'Email',
|
||||
fieldtype: 'Data',
|
||||
required: 1,
|
||||
inputType: 'email'
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'companyName',
|
||||
label: 'Company Name',
|
||||
fieldtype: 'Data',
|
||||
required: 1
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'bankName',
|
||||
label: 'Bank Name',
|
||||
fieldtype: 'Data',
|
||||
required: 1
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'fiscalYearStart',
|
||||
label: 'Fiscal Year Start Date',
|
||||
fieldtype: 'Date',
|
||||
formula: (doc) => {
|
||||
let date = countryList[doc.country]["fiscal_year_start"].split("-");
|
||||
var currentYear = (new Date).getFullYear();
|
||||
let currentMonth = date[0] - 1 ;
|
||||
let currentDate = date[1];
|
||||
return new Date(currentYear,currentMonth,currentDate).toISOString().substr(0, 10);;
|
||||
},
|
||||
required: 1
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'fiscalYearEnd',
|
||||
label: 'Fiscal Year End Date',
|
||||
fieldtype: 'Date',
|
||||
formula: (doc) => {
|
||||
let date = countryList[doc.country]["fiscal_year_end"].split("-");
|
||||
var currentYear = (new Date).getFullYear() + 1 ;
|
||||
let currentMonth = date[0] - 1 ;
|
||||
let currentDate = date[1];
|
||||
return new Date(currentYear,currentMonth,currentDate).toISOString().substr(0, 10);;
|
||||
},
|
||||
required: 1
|
||||
}
|
||||
],
|
||||
|
||||
layout: {
|
||||
paginated: true,
|
||||
sections: [{
|
||||
title: 'Select Country',
|
||||
columns: [{
|
||||
fields: ['country']
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Add a Profile',
|
||||
columns: [{
|
||||
fields: ['fullname', 'email']
|
||||
}]
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Add your Company',
|
||||
columns: [{
|
||||
fields: ['companyName', 'bankName', 'fiscalYearStart', 'fiscalYearEnd']
|
||||
}]
|
||||
}
|
||||
].filter(Boolean)
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
"name": "Tax",
|
||||
"label": "Tax",
|
||||
"doctype": "DocType",
|
||||
"isSingle": 0,
|
||||
"isChild": 0,
|
||||
|
10
models/doctype/Tax/TaxList.js
Normal file
10
models/doctype/Tax/TaxList.js
Normal file
@ -0,0 +1,10 @@
|
||||
import { _ } from 'frappejs/utils';
|
||||
import indicators from 'frappejs/ui/constants/indicators';
|
||||
|
||||
export default {
|
||||
doctype: 'Tax',
|
||||
title: _('Tax'),
|
||||
columns: [
|
||||
'name'
|
||||
]
|
||||
}
|
@ -52,7 +52,7 @@ async function getCountryCOA(){
|
||||
const conCode = countries[doc.country].code;
|
||||
|
||||
const countryCOA = path.resolve(path.join('./fixtures/verified/', conCode + '.json'));
|
||||
|
||||
|
||||
if(fs.existsSync(countryCOA)){
|
||||
const jsonText = fs.readFileSync(countryCOA, 'utf-8');
|
||||
const json = JSON.parse(jsonText);
|
||||
|
@ -1,9 +1,11 @@
|
||||
module.exports = {
|
||||
models: {
|
||||
SetupWizard: require('./doctype/SetupWizard/SetupWizard'),
|
||||
Account: require('./doctype/Account/Account.js'),
|
||||
AccountingSettings: require('./doctype/AccountingSettings/AccountingSettings'),
|
||||
CompanySettings: require('./doctype/CompanySettings/CompanySettings'),
|
||||
AccountingLedgerEntry: require('./doctype/AccountingLedgerEntry/AccountingLedgerEntry.js'),
|
||||
Party: require('./doctype/Party/Party.js'),
|
||||
// Party: require('./doctype/Party/Party.js'),
|
||||
|
||||
Payment: require('./doctype/Payment/Payment.js'),
|
||||
PaymentFor: require('./doctype/PaymentFor/PaymentFor.js'),
|
||||
@ -53,5 +55,8 @@ module.exports = {
|
||||
Event: require('./doctype/Event/Event'),
|
||||
EventSchedule: require('./doctype/EventSchedule/EventSchedule'),
|
||||
EventSettings: require('./doctype/EventSettings/EventSettings'),
|
||||
|
||||
Email: require('./doctype/Email/Email'),
|
||||
EmailAccount: require('./doctype/EmailAccount/EmailAccount'),
|
||||
}
|
||||
}
|
||||
}
|
271
package-lock.json
generated
271
package-lock.json
generated
@ -15,9 +15,9 @@
|
||||
"integrity": "sha512-XtGk+IF57pr852UK1AhQJXqmm1WmSgS5uISL+LPs0z/iAxXouMvdlLJrHPeukP6gd7yR2rDTMSMkHNODgwIq7A=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "8.10.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.36.tgz",
|
||||
"integrity": "sha512-SL6KhfM7PTqiFmbCW3eVNwVBZ+88Mrzbuvn9olPsfv43mbiWaFY+nRcz/TGGku0/lc2FepdMbImdMY1JrQ+zbw=="
|
||||
"version": "8.10.38",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.38.tgz",
|
||||
"integrity": "sha512-EibsnbJerd0hBFaDjJStFrVbVBAtOy4dgL8zZFw0uOvPqzBAX59Ci8cgjg3+RgJIWhsB5A4c+pi+D4P9tQQh/A=="
|
||||
},
|
||||
"@vue/component-compiler-utils": {
|
||||
"version": "2.2.0",
|
||||
@ -1867,6 +1867,11 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"clamp": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
|
||||
"integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
|
||||
},
|
||||
"class-utils": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||
@ -2824,12 +2829,12 @@
|
||||
"integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ=="
|
||||
},
|
||||
"electron": {
|
||||
"version": "2.0.12",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-2.0.12.tgz",
|
||||
"integrity": "sha512-mw8hoM/GPtFPP8FGiJcVNe8Rx63YJ7O8bf7McQj21HAvrXGAwReGFrpIe5xN6ec10fDXNSNyfzRucjFXtOtLcg==",
|
||||
"version": "3.0.12",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-3.0.12.tgz",
|
||||
"integrity": "sha512-stvGbqYzWv5qHHtjZZgA7gET3NPGLuxs68IHTrJqsqujQfXGkhMOh8tstpXl86kBdRpzZn7GaDlTWcgeFSmsPw==",
|
||||
"requires": {
|
||||
"@types/node": "^8.0.24",
|
||||
"electron-download": "^3.0.1",
|
||||
"electron-download": "^4.1.0",
|
||||
"extract-zip": "^1.0.3"
|
||||
}
|
||||
},
|
||||
@ -2902,33 +2907,38 @@
|
||||
}
|
||||
},
|
||||
"electron-download": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz",
|
||||
"integrity": "sha1-LP1U1pZsAZxNSa1l++Zcyc3vaMg=",
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-download/-/electron-download-4.1.1.tgz",
|
||||
"integrity": "sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==",
|
||||
"requires": {
|
||||
"debug": "^2.2.0",
|
||||
"fs-extra": "^0.30.0",
|
||||
"home-path": "^1.0.1",
|
||||
"debug": "^3.0.0",
|
||||
"env-paths": "^1.0.0",
|
||||
"fs-extra": "^4.0.1",
|
||||
"minimist": "^1.2.0",
|
||||
"nugget": "^2.0.0",
|
||||
"path-exists": "^2.1.0",
|
||||
"rc": "^1.1.2",
|
||||
"semver": "^5.3.0",
|
||||
"sumchecker": "^1.2.0"
|
||||
"nugget": "^2.0.1",
|
||||
"path-exists": "^3.0.0",
|
||||
"rc": "^1.2.1",
|
||||
"semver": "^5.4.1",
|
||||
"sumchecker": "^2.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
|
||||
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
|
||||
"requires": {
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -3163,6 +3173,11 @@
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.1.tgz",
|
||||
"integrity": "sha1-blwtClYhtdra7O+AuQ7ftc13cvA="
|
||||
},
|
||||
"env-paths": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz",
|
||||
"integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA="
|
||||
},
|
||||
"errno": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
|
||||
@ -3539,6 +3554,11 @@
|
||||
"schema-utils": "^0.4.5"
|
||||
}
|
||||
},
|
||||
"file-saver": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.0.tgz",
|
||||
"integrity": "sha512-cYM1ic5DAkg25pHKgi5f10ziAM7RJU37gaH1XQlyNDrtUnzhC/dfoV9zf2OmF0RMKi42jG5B0JWBnPQqyj/G6g=="
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
|
||||
@ -3720,13 +3740,13 @@
|
||||
"mysql": "^2.15.0",
|
||||
"node-fetch": "^1.7.3",
|
||||
"node-sass": "^4.7.2",
|
||||
"nodemailer": "^4.7.0",
|
||||
"nunjucks": "^3.1.0",
|
||||
"octicons": "^7.2.0",
|
||||
"passport": "^0.4.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"puppeteer": "^1.2.0",
|
||||
"sass-loader": "^7.0.3",
|
||||
"sharp": "^0.20.8",
|
||||
"showdown": "^1.8.6",
|
||||
"socket.io": "^2.0.4",
|
||||
"sqlite3": "^4.0.2",
|
||||
@ -3738,6 +3758,78 @@
|
||||
"webpack": "^4.16.1",
|
||||
"webpack-dev-server": "^3.1.4",
|
||||
"webpack-hot-middleware": "^2.22.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron": {
|
||||
"version": "2.0.12",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-2.0.12.tgz",
|
||||
"integrity": "sha512-mw8hoM/GPtFPP8FGiJcVNe8Rx63YJ7O8bf7McQj21HAvrXGAwReGFrpIe5xN6ec10fDXNSNyfzRucjFXtOtLcg==",
|
||||
"requires": {
|
||||
"@types/node": "^8.0.24",
|
||||
"electron-download": "^3.0.1",
|
||||
"extract-zip": "^1.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-download": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz",
|
||||
"integrity": "sha1-LP1U1pZsAZxNSa1l++Zcyc3vaMg=",
|
||||
"requires": {
|
||||
"debug": "^2.2.0",
|
||||
"fs-extra": "^0.30.0",
|
||||
"home-path": "^1.0.1",
|
||||
"minimist": "^1.2.0",
|
||||
"nugget": "^2.0.0",
|
||||
"path-exists": "^2.1.0",
|
||||
"rc": "^1.1.2",
|
||||
"semver": "^5.3.0",
|
||||
"sumchecker": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
|
||||
"integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^2.1.0",
|
||||
"klaw": "^1.0.0",
|
||||
"path-is-absolute": "^1.0.0",
|
||||
"rimraf": "^2.2.8"
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
|
||||
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
|
||||
"integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
|
||||
"requires": {
|
||||
"pinkie-promise": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"sumchecker": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.3.1.tgz",
|
||||
"integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=",
|
||||
"requires": {
|
||||
"debug": "^2.2.0",
|
||||
"es6-promise": "^4.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fresh": {
|
||||
@ -3804,15 +3896,13 @@
|
||||
"integrity": "sha512-2QY5eeqVv4m2PfyMiEuy9adxNP+ajf+8AR05cEi+OAzPcOj90hvFImeZhTmKLBgSd9EvG33jsD7ZRxsx9dThkQ=="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
|
||||
"integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=",
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz",
|
||||
"integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^2.1.0",
|
||||
"klaw": "^1.0.0",
|
||||
"path-is-absolute": "^1.0.0",
|
||||
"rimraf": "^2.2.8"
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"fs-extra-p": {
|
||||
@ -5372,9 +5462,9 @@
|
||||
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE="
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "http://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
|
||||
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
"integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
@ -5599,6 +5689,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz",
|
||||
"integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ="
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
|
||||
},
|
||||
"loglevel": {
|
||||
"version": "1.6.1",
|
||||
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.1.tgz",
|
||||
@ -5679,6 +5774,11 @@
|
||||
"object-visit": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"material-colors": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/material-colors/-/material-colors-1.2.6.tgz",
|
||||
"integrity": "sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg=="
|
||||
},
|
||||
"md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
@ -6157,23 +6257,23 @@
|
||||
}
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz",
|
||||
"integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==",
|
||||
"version": "4.4.8",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz",
|
||||
"integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==",
|
||||
"requires": {
|
||||
"chownr": "^1.0.1",
|
||||
"chownr": "^1.1.1",
|
||||
"fs-minipass": "^1.2.5",
|
||||
"minipass": "^2.3.3",
|
||||
"minizlib": "^1.1.0",
|
||||
"minipass": "^2.3.4",
|
||||
"minizlib": "^1.1.1",
|
||||
"mkdirp": "^0.5.0",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
|
||||
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
|
||||
"integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A=="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -6214,6 +6314,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"nodemailer": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.7.0.tgz",
|
||||
"integrity": "sha512-IludxDypFpYw4xpzKdMAozBSkzKHmNBvGanUREjJItgJ2NYcK/s8+PggVhj7c2yGFQykKsnnmv1+Aqo0ZfjHmw=="
|
||||
},
|
||||
"noop-logger": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz",
|
||||
@ -7923,49 +8028,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"sharp": {
|
||||
"version": "0.20.8",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.20.8.tgz",
|
||||
"integrity": "sha512-A8NaPGWRDKpmHTi8sl2xzozYXhTQWBb/GaJ8ZPU7L/vKW8wVvd4Yq+isJ0c7p9sX5gnjPQcM3eOfHuvvnZ2fOQ==",
|
||||
"requires": {
|
||||
"color": "^3.0.0",
|
||||
"detect-libc": "^1.0.3",
|
||||
"fs-copy-file-sync": "^1.1.1",
|
||||
"nan": "^2.11.0",
|
||||
"npmlog": "^4.1.2",
|
||||
"prebuild-install": "^4.0.0",
|
||||
"semver": "^5.5.1",
|
||||
"simple-get": "^2.8.1",
|
||||
"tar": "^4.4.6",
|
||||
"tunnel-agent": "^0.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"nan": {
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
|
||||
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA=="
|
||||
},
|
||||
"tar": {
|
||||
"version": "4.4.6",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.6.tgz",
|
||||
"integrity": "sha512-tMkTnh9EdzxyfW+6GK6fCahagXsnYk6kE6S9Gr9pjVdys769+laCTbodXDhPAjzVtEBazRgP0gYqOjnk9dQzLg==",
|
||||
"requires": {
|
||||
"chownr": "^1.0.1",
|
||||
"fs-minipass": "^1.2.5",
|
||||
"minipass": "^2.3.3",
|
||||
"minizlib": "^1.1.0",
|
||||
"mkdirp": "^0.5.0",
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
|
||||
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k="
|
||||
}
|
||||
}
|
||||
},
|
||||
"shebang-command": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||
@ -8459,9 +8521,9 @@
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
|
||||
},
|
||||
"sqlite3": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.2.tgz",
|
||||
"integrity": "sha512-51ferIRwYOhzUEtogqOa/y9supADlAht98bF/gbIi6WkzRJX6Yioldxbzj1MV4yV+LgdKD/kkHwFTeFXOG4htA==",
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.0.4.tgz",
|
||||
"integrity": "sha512-CO8vZMyUXBPC+E3iXOCc7Tz2pAdq5BWfLcQmOokCOZW5S5sZ/paijiPOCdvzpdP83RroWHYa5xYlVqCxSqpnQg==",
|
||||
"requires": {
|
||||
"nan": "~2.10.0",
|
||||
"node-pre-gyp": "^0.10.3",
|
||||
@ -8632,12 +8694,11 @@
|
||||
"integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
|
||||
},
|
||||
"sumchecker": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.3.1.tgz",
|
||||
"integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=",
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-2.0.2.tgz",
|
||||
"integrity": "sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4=",
|
||||
"requires": {
|
||||
"debug": "^2.2.0",
|
||||
"es6-promise": "^4.0.5"
|
||||
"debug": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
@ -8747,6 +8808,11 @@
|
||||
"setimmediate": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"tinycolor2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
|
||||
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
|
||||
},
|
||||
"to-array": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
|
||||
@ -9248,6 +9314,17 @@
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-2.5.17.tgz",
|
||||
"integrity": "sha512-mFbcWoDIJi0w0Za4emyLiW72Jae0yjANHbCVquMKijcavBGypqlF7zHRgMa5k4sesdv7hv2rB4JPdZfR+TPfhQ=="
|
||||
},
|
||||
"vue-color": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/vue-color/-/vue-color-2.7.0.tgz",
|
||||
"integrity": "sha512-fak9oPRL3BsYtakTGmWIS2yNRppRYNlMgGGq78CMH34ipU8fLgi/bT9JiSPcscpdTNLGracuOFuZ8OFeml+SQQ==",
|
||||
"requires": {
|
||||
"clamp": "^1.0.1",
|
||||
"lodash.throttle": "^4.0.0",
|
||||
"material-colors": "^1.0.0",
|
||||
"tinycolor2": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"vue-flatpickr-component": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/vue-flatpickr-component/-/vue-flatpickr-component-7.0.6.tgz",
|
||||
@ -9799,4 +9876,4 @@
|
||||
"integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -20,8 +20,7 @@
|
||||
"dist/electron"
|
||||
],
|
||||
"dmg": {
|
||||
"contents": [
|
||||
{
|
||||
"contents": [{
|
||||
"x": 410,
|
||||
"y": 150,
|
||||
"type": "link",
|
||||
@ -52,7 +51,7 @@
|
||||
"scripts": {
|
||||
"test": "mocha tests",
|
||||
"start": "frappe start",
|
||||
"electron": "cross-env ELECTRON=true frappe start electron",
|
||||
"electron": "ELECTRON=true frappe start electron",
|
||||
"pack-electron": "cross-env NODE_ENV=production ELECTRON=true frappe build electron",
|
||||
"build-electron": "npm run pack-electron && electron-builder",
|
||||
"postinstall": "electron-builder install-app-deps"
|
||||
@ -60,7 +59,9 @@
|
||||
"dependencies": {
|
||||
"cross-env": "^5.2.0",
|
||||
"frappejs": "github:frappe/frappejs",
|
||||
"nodemailer": "^4.7.0",
|
||||
"popper.js": "^1.14.4",
|
||||
"vue-color": "^2.7.0",
|
||||
"vue-toasted": "^1.1.25"
|
||||
}
|
||||
}
|
||||
}
|
@ -29,7 +29,6 @@ async function getReceivablePayable({ reportType = 'Receivable', date }) {
|
||||
let data = [];
|
||||
|
||||
for (let entry of validEntries) {
|
||||
// console.log(entry);
|
||||
|
||||
const { outStandingAmount, creditNoteAmount } = getOutstandingAmount(entry);
|
||||
|
||||
|
42
reports/BankReconciliation/BankReconciliation.js
Normal file
42
reports/BankReconciliation/BankReconciliation.js
Normal file
@ -0,0 +1,42 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
class BankReconciliation {
|
||||
async run(params) {
|
||||
const filters = {};
|
||||
if (params.paymentAccount) filters.paymentAccount = params.paymentAccount;
|
||||
if (params.party) filters.party = params.party;
|
||||
// if (params.referenceType) filters.referenceType = params.referenceType;
|
||||
// if (params.referenceName) filters.referenceName = params.referenceName;
|
||||
if (params.toDate || params.fromDate) {
|
||||
filters.date = [];
|
||||
if (params.toDate) filters.date.push('<=', params.toDate);
|
||||
if (params.fromDate) filters.date.push('>=', params.fromDate);
|
||||
}
|
||||
|
||||
let data = await frappe.db.getAll({
|
||||
doctype: 'Payment',
|
||||
fields: ['date', 'account', 'paymentAccount', 'party', 'name', 'referenceDate','clearanceDate'],
|
||||
filters: filters,
|
||||
});
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
let ledger = await frappe.db.getAll({
|
||||
doctype: 'AccountingLedgerEntry',
|
||||
fields: ['date', 'referenceType', 'referenceName', 'debit', 'credit'],
|
||||
filters: {
|
||||
referenceType: 'Payment',
|
||||
account: data[i].paymentAccount,
|
||||
referenceName: data[i].name
|
||||
}
|
||||
})
|
||||
data[i].credit = ledger[0].credit;
|
||||
data[i].debit = ledger[0].debit;
|
||||
data[i].referenceName = ledger[0].referenceName;
|
||||
data[i].referenceType = ledger[0].referenceType;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BankReconciliation;
|
80
reports/BankReconciliation/BankReconciliationView.js
Normal file
80
reports/BankReconciliation/BankReconciliationView.js
Normal file
@ -0,0 +1,80 @@
|
||||
const ReportPage = require('frappejs/client/desk/reportpage');
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = class BankReconciliationView extends ReportPage {
|
||||
constructor() {
|
||||
super({
|
||||
title: frappe._('Bank Reconciliation'),
|
||||
filterFields: [{
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
label: 'Payment Account'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Link',
|
||||
target: 'Party',
|
||||
label: 'Party'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'From Date'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'To Date'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.method = 'bank-reconciliation';
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return [{
|
||||
label: 'Posting Date',
|
||||
fieldtype: 'Date',
|
||||
fieldname: 'date'
|
||||
},
|
||||
{
|
||||
label: 'Payment Account',
|
||||
fieldtype: 'Link'
|
||||
},
|
||||
{
|
||||
label: 'Debit',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Credit',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Balance',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Ref. Type',
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'referenceType'
|
||||
},
|
||||
{
|
||||
label: 'Ref. Name',
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'referenceName'
|
||||
},
|
||||
{
|
||||
label: 'Ref. Date',
|
||||
fieldtype: 'Date',
|
||||
fieldname: 'referenceDate'
|
||||
},
|
||||
{
|
||||
label: 'Clearance Date',
|
||||
fieldtype: 'Date',
|
||||
fieldname: 'clearanceDate'
|
||||
},
|
||||
{
|
||||
label: 'Party',
|
||||
fieldtype: 'Link'
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
76
reports/BankReconciliation/viewConfig.js
Normal file
76
reports/BankReconciliation/viewConfig.js
Normal file
@ -0,0 +1,76 @@
|
||||
const title = 'Bank Reconciliation';
|
||||
module.exports = {
|
||||
title: title,
|
||||
method: 'bank-reconciliation',
|
||||
filterFields: [{
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
label: 'Payement Account',
|
||||
fieldname: 'paymentAccount'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Link',
|
||||
target: 'Party',
|
||||
label: 'Party',
|
||||
fieldname: 'party'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'From Date',
|
||||
fieldname: 'fromDate'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'To Date',
|
||||
fieldname: 'toDate'
|
||||
}
|
||||
],
|
||||
getColumns() {
|
||||
return [{
|
||||
label: 'Posting Date',
|
||||
fieldtype: 'Date',
|
||||
fieldname: 'date'
|
||||
},
|
||||
{
|
||||
label: 'Payment Account',
|
||||
fieldtype: 'Link'
|
||||
},
|
||||
{
|
||||
label: 'Debit',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Credit',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Balance',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Ref. Type',
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'referenceType'
|
||||
},
|
||||
{
|
||||
label: 'Ref. Name',
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'referenceName'
|
||||
},
|
||||
{
|
||||
label: 'Ref. Date',
|
||||
fieldtype: 'Date',
|
||||
fieldname: 'referenceDate'
|
||||
},
|
||||
{
|
||||
label: 'Clearance Date',
|
||||
fieldtype: 'Date',
|
||||
fieldname: 'clearanceDate'
|
||||
},
|
||||
{
|
||||
label: 'Party',
|
||||
fieldtype: 'Link'
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
@ -12,7 +12,6 @@ class GeneralLedger {
|
||||
if (params.toDate) filters.date.push('<=', params.toDate);
|
||||
if (params.fromDate) filters.date.push('>=', params.fromDate);
|
||||
}
|
||||
|
||||
let data = await frappe.db.getAll({
|
||||
doctype: 'AccountingLedgerEntry',
|
||||
fields: ['date', 'account', 'party', 'referenceType', 'referenceName', 'debit', 'credit'],
|
||||
|
@ -5,8 +5,7 @@ module.exports = class GeneralLedgerView extends ReportPage {
|
||||
constructor() {
|
||||
super({
|
||||
title: frappe._('General Ledger'),
|
||||
filterFields: [
|
||||
{
|
||||
filterFields: [{
|
||||
fieldtype: 'Select',
|
||||
options: ['', 'Invoice', 'Payment'],
|
||||
label: 'Reference Type',
|
||||
@ -43,16 +42,42 @@ module.exports = class GeneralLedgerView extends ReportPage {
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return [
|
||||
{label: 'Date', fieldtype: 'Date'},
|
||||
{label: 'Account', fieldtype: 'Link'},
|
||||
{label: 'Debit', fieldtype: 'Currency'},
|
||||
{label: 'Credit', fieldtype: 'Currency'},
|
||||
{label: 'Balance', fieldtype: 'Currency'},
|
||||
{label: 'Reference Type', fieldtype: 'Data'},
|
||||
{label: 'Reference Name', fieldtype: 'Data'},
|
||||
{label: 'Party', fieldtype: 'Link'},
|
||||
{label: 'Description', fieldtype: 'Data'}
|
||||
return [{
|
||||
label: 'Date',
|
||||
fieldtype: 'Date'
|
||||
},
|
||||
{
|
||||
label: 'Account',
|
||||
fieldtype: 'Link'
|
||||
},
|
||||
{
|
||||
label: 'Debit',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Credit',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Balance',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Reference Type',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Reference Name',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Party',
|
||||
fieldtype: 'Link'
|
||||
},
|
||||
{
|
||||
label: 'Description',
|
||||
fieldtype: 'Data'
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
||||
|
@ -2,8 +2,7 @@ const title = 'General Ledger';
|
||||
module.exports = {
|
||||
title: title,
|
||||
method: 'general-ledger',
|
||||
filterFields: [
|
||||
{
|
||||
filterFields: [{
|
||||
fieldtype: 'Select',
|
||||
options: ['', 'Invoice', 'Payment'],
|
||||
label: 'Reference Type',
|
||||
@ -15,22 +14,66 @@ module.exports = {
|
||||
label: 'Reference Name',
|
||||
fieldname: 'referenceName'
|
||||
},
|
||||
{ fieldtype: 'Link', target: 'Account', label: 'Account', fieldname: 'account' },
|
||||
{ fieldtype: 'Link', target: 'Party', label: 'Party', fieldname: 'party' },
|
||||
{ fieldtype: 'Date', label: 'From Date', fieldname: 'fromDate' },
|
||||
{ fieldtype: 'Date', label: 'To Date', fieldname: 'toDate' }
|
||||
{
|
||||
fieldtype: 'Link',
|
||||
target: 'Account',
|
||||
label: 'Account',
|
||||
fieldname: 'account'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Link',
|
||||
target: 'Party',
|
||||
label: 'Party',
|
||||
fieldname: 'party'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'From Date',
|
||||
fieldname: 'fromDate'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'To Date',
|
||||
fieldname: 'toDate'
|
||||
}
|
||||
],
|
||||
getColumns() {
|
||||
return [
|
||||
{ label: 'Date', fieldtype: 'Date' },
|
||||
{ label: 'Account', fieldtype: 'Link' },
|
||||
{ label: 'Debit', fieldtype: 'Currency' },
|
||||
{ label: 'Credit', fieldtype: 'Currency' },
|
||||
{ label: 'Balance', fieldtype: 'Currency' },
|
||||
{ label: 'Reference Type', fieldtype: 'Data' },
|
||||
{ label: 'Reference Name', fieldtype: 'Data' },
|
||||
{ label: 'Party', fieldtype: 'Link' },
|
||||
{ label: 'Description', fieldtype: 'Data' }
|
||||
return [{
|
||||
label: 'Date',
|
||||
fieldtype: 'Date'
|
||||
},
|
||||
{
|
||||
label: 'Account',
|
||||
fieldtype: 'Link'
|
||||
},
|
||||
{
|
||||
label: 'Debit',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Credit',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Balance',
|
||||
fieldtype: 'Currency'
|
||||
},
|
||||
{
|
||||
label: 'Reference Type',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Reference Name',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Party',
|
||||
fieldtype: 'Link'
|
||||
},
|
||||
{
|
||||
label: 'Description',
|
||||
fieldtype: 'Data'
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
||||
|
59
reports/GoodsAndServiceTax/GoodsAndServiceTax.js
Normal file
59
reports/GoodsAndServiceTax/GoodsAndServiceTax.js
Normal file
@ -0,0 +1,59 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
class GoodsAndServiceTax {
|
||||
async run(params) {
|
||||
let filters = {};
|
||||
if (params.toDate || params.fromDate) {
|
||||
filters.date = [];
|
||||
if (params.toDate) filters.date.push('<=', params.toDate);
|
||||
if (params.fromDate) filters.date.push('>=', params.fromDate);
|
||||
}
|
||||
if (params.transferType) filters.transferType = params.transferType;
|
||||
|
||||
let invoiceNames = await frappe.db.getAll({
|
||||
doctype: 'Invoice',
|
||||
filter: filters
|
||||
});
|
||||
|
||||
let tableData = [];
|
||||
for (let invoice of invoiceNames) {
|
||||
const row = await this.getRow(invoice.name)
|
||||
tableData.push(row)
|
||||
}
|
||||
|
||||
if(Object.keys(filters).length != 0){
|
||||
tableData = tableData.filter((row) => {
|
||||
if(filters.account) return row.account === filters.account
|
||||
if(filters.transferType) return row.transferType === filters.transferType
|
||||
if(filters.place) return row.place === filters.place
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return tableData;
|
||||
}
|
||||
|
||||
async getRow(name) {
|
||||
let row = {}
|
||||
let invoiceDetails = await frappe.getDoc('Invoice', name);
|
||||
let customerDetails = await frappe.getDoc('Party', invoiceDetails.customer);
|
||||
let addressDetails = await frappe.getDoc('Address', customerDetails.address);
|
||||
row.gstin = customerDetails.gstin
|
||||
row.cusName = invoiceDetails.customer
|
||||
row.invNo = invoiceDetails.name
|
||||
row.invDate = invoiceDetails.date
|
||||
row.place = addressDetails.state
|
||||
row.rate = 0
|
||||
row.transferType = 'In State';
|
||||
invoiceDetails.taxes.forEach(tax => {
|
||||
row.rate += tax.rate
|
||||
if (tax.account === 'IGST') row.transferType = 'Out of State';
|
||||
});
|
||||
row.invAmt = invoiceDetails.grandTotal
|
||||
row.taxAmt = invoiceDetails.netTotal
|
||||
return row
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = GoodsAndServiceTax;
|
74
reports/GoodsAndServiceTax/GoodsAndServiceTaxView.js
Normal file
74
reports/GoodsAndServiceTax/GoodsAndServiceTaxView.js
Normal file
@ -0,0 +1,74 @@
|
||||
const ReportPage = require('frappejs/client/desk/reportpage');
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = class GoodsAndServiceTaxView extends ReportPage {
|
||||
constructor() {
|
||||
super({
|
||||
title: frappe._('Goods and Service Tax'),
|
||||
filterFields: [
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
label: 'Transfer Type'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
label: 'Place'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'From Date'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'To Date'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
this.method = 'gst-taxes';
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
return [{
|
||||
label: 'GSTIN No.',
|
||||
fieldname: 'gstin',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'cusName',
|
||||
label: 'Customer Name'
|
||||
},
|
||||
{
|
||||
label: 'Invoice No.',
|
||||
fieldname: 'invNo',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Invoice Value',
|
||||
fieldname: 'invAmt',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Invoice Date',
|
||||
fieldname: 'invDate',
|
||||
fieldtype: 'Date'
|
||||
},
|
||||
{
|
||||
label: 'Place of supply',
|
||||
fieldname: 'place',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Rate',
|
||||
fieldname: 'rate',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Taxable Amount',
|
||||
fieldname: 'taxAmt',
|
||||
fieldtype: 'Data'
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
69
reports/GoodsAndServiceTax/viewConfig.js
Normal file
69
reports/GoodsAndServiceTax/viewConfig.js
Normal file
@ -0,0 +1,69 @@
|
||||
const title = 'Goods and Service Tax';
|
||||
module.exports = {
|
||||
title: title,
|
||||
method: 'gst-taxes',
|
||||
filterFields: [{
|
||||
fieldtype: 'Data',
|
||||
label: 'Transfer Type',
|
||||
fieldname: 'transferType'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
label: 'Place',
|
||||
fieldname: 'place'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'From Date',
|
||||
fieldname: 'fromDate'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Date',
|
||||
label: 'To Date',
|
||||
fieldname: 'toDate'
|
||||
}],
|
||||
|
||||
getColumns() {
|
||||
return [{
|
||||
label: 'GSTIN No.',
|
||||
fieldname: 'gstin',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'cusName',
|
||||
label: 'Customer Name'
|
||||
},
|
||||
{
|
||||
label: 'Invoice No.',
|
||||
fieldname: 'invNo',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Invoice Value',
|
||||
fieldname: 'invAmt',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Invoice Date',
|
||||
fieldname: 'invDate',
|
||||
fieldtype: 'Date'
|
||||
},
|
||||
{
|
||||
label: 'Place of supply',
|
||||
fieldname: 'place',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Rate',
|
||||
fieldname: 'rate',
|
||||
fieldtype: 'Data'
|
||||
},
|
||||
{
|
||||
label: 'Taxable Amount',
|
||||
fieldname: 'taxAmt',
|
||||
fieldtype: 'Data'
|
||||
}
|
||||
];
|
||||
}
|
||||
};
|
@ -5,12 +5,13 @@ const BalanceSheet = require('./BalanceSheet/BalanceSheet');
|
||||
const TrialBalance = require('./TrialBalance/TrialBalance');
|
||||
const SalesRegister = require('./SalesRegister/SalesRegister');
|
||||
const PurchaseRegister = require('./PurchaseRegister/PurchaseRegister');
|
||||
const BankReconciliation = require('./BankReconciliation/BankReconciliation');
|
||||
const GoodsAndServiceTax = require('./GoodsAndServiceTax/GoodsAndServiceTax');
|
||||
const AccountsReceivablePayable = require('./AccountsReceivablePayable/AccountsReceivablePayable');
|
||||
|
||||
// called on server side
|
||||
function registerReportMethods() {
|
||||
const reports = [
|
||||
{
|
||||
const reports = [{
|
||||
method: 'general-ledger',
|
||||
class: GeneralLedger
|
||||
},
|
||||
@ -33,7 +34,15 @@ function registerReportMethods() {
|
||||
{
|
||||
method: 'purchase-register',
|
||||
class: PurchaseRegister
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'bank-reconciliation',
|
||||
class: BankReconciliation
|
||||
},
|
||||
{
|
||||
method: 'gst-taxes',
|
||||
class: GoodsAndServiceTax
|
||||
},
|
||||
];
|
||||
|
||||
reports.forEach(report => {
|
||||
|
@ -2,5 +2,7 @@ module.exports = {
|
||||
'general-ledger': require('./GeneralLedger/viewConfig'),
|
||||
'sales-register': require('./SalesRegister/viewConfig'),
|
||||
'profit-and-loss': require('./ProfitAndLoss/viewConfig'),
|
||||
'trial-balance': require('./TrialBalance/viewConfig')
|
||||
'trial-balance': require('./TrialBalance/viewConfig'),
|
||||
'bank-reconciliation': require('./BankReconciliation/viewConfig'),
|
||||
'gst-taxes': require('./GoodsAndServiceTax/viewConfig'),
|
||||
}
|
||||
|
@ -1,9 +1,15 @@
|
||||
const frappe = require('frappejs');
|
||||
const registerReportMethods = require('../reports');
|
||||
const sender = require('../email/sender');
|
||||
|
||||
module.exports = function registerServerMethods() {
|
||||
registerReportMethods();
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'send-mail',
|
||||
handler: sender.sendMail
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'import-coa',
|
||||
async handler() {
|
||||
@ -19,7 +25,7 @@ module.exports = function registerServerMethods() {
|
||||
const path = require('path');
|
||||
const { getPDFForElectron } = require('frappejs/server/pdf');
|
||||
const { getSettings } = require('../electron/settings');
|
||||
const destination = path.resolve(getSettings().dbPath, '..')
|
||||
const destination = path.resolve('.')
|
||||
getPDFForElectron(doctype, name, destination, html);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
const { app, BrowserWindow } = require('electron');
|
||||
const {
|
||||
app,
|
||||
BrowserWindow
|
||||
} = require('electron');
|
||||
const setupMenu = require('./menu');
|
||||
|
||||
let mainWindow
|
||||
@ -9,7 +12,9 @@ if (process.env.NODE_ENV !== 'development') {
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
const { getAppConfig } = require('frappejs/webpack/utils');
|
||||
const {
|
||||
getAppConfig
|
||||
} = require('frappejs/webpack/utils');
|
||||
const appConfig = getAppConfig();
|
||||
winURL = `http://localhost:${appConfig.dev.devServerPort}`;
|
||||
} else {
|
||||
@ -20,6 +25,7 @@ function createWindow() {
|
||||
/**
|
||||
* Initial window options
|
||||
*/
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1024,
|
||||
height: 768,
|
||||
|
113
src/components/ExportWizard.vue
Normal file
113
src/components/ExportWizard.vue
Normal file
@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div id="exportWizard" class="modal-body">
|
||||
<div class="ml-4 col-6 text-left">
|
||||
<input
|
||||
id="select-cbox"
|
||||
@change="toggleSelect"
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
v-model="selectAllFlag"
|
||||
>
|
||||
<label class="form-check-label bold ml-2" for="select-cbox">{{ "Select/Clear All" }}</label>
|
||||
</div>
|
||||
<hr width="93%">
|
||||
<div class="row ml-4 mb-4">
|
||||
<div v-for="column in columns" :key="column.id" class="form-check mt-2 col-6">
|
||||
<input :id="column.id" class="form-check-input" type="checkbox" v-model="column.checked">
|
||||
<label class="form-check-label" :for="column.id">{{ column.content }}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row footer-divider mb-3">
|
||||
<div class="col-12" style="border-bottom:1px solid #e9ecef"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 text-right">
|
||||
<f-button primary @click="save">{{ 'Download CSV' }}</f-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import path from 'path';
|
||||
const { writeFile } = require('frappejs/server/utils');
|
||||
var FileSaver = require('file-saver');
|
||||
|
||||
export default {
|
||||
props: ['title', 'rows', 'columnData'],
|
||||
data() {
|
||||
return {
|
||||
selectAllFlag: true,
|
||||
columns: this.columnData
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleSelect() {
|
||||
this.columns = this.columns.map(column => {
|
||||
return {
|
||||
id: column.id,
|
||||
content: column.content,
|
||||
checked: this.selectAllFlag
|
||||
};
|
||||
});
|
||||
},
|
||||
close() {
|
||||
this.$modal.hide();
|
||||
},
|
||||
checkNoneSelected(columns) {
|
||||
for (let column of columns) {
|
||||
if (column.checked) return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
async save() {
|
||||
if (this.checkNoneSelected(this.columns)) {
|
||||
alert(
|
||||
`No columns have been selected.\n` +
|
||||
`Please select at least one column to perform export.`
|
||||
);
|
||||
} else {
|
||||
let selectedColumnIds = this.columns.map(column => {
|
||||
if (column.checked) return column.id;
|
||||
});
|
||||
let selectedColumns = this.columnData.filter(
|
||||
column => selectedColumnIds.indexOf(column.id) != -1
|
||||
);
|
||||
await this.exportData(this.rows, selectedColumns);
|
||||
this.$modal.hide();
|
||||
}
|
||||
},
|
||||
async exportData(rows = this.rows, columns = this.columnData) {
|
||||
let title = this.title;
|
||||
let columnNames = columns.map(column => column.content).toString();
|
||||
let columnIDs = columns.map(column => column.id);
|
||||
let rowData = rows.map(row => columnIDs.map(id => row[id]).toString());
|
||||
let csvDataArray = [columnNames, ...rowData];
|
||||
let csvData = csvDataArray.join('\n');
|
||||
let d = new Date();
|
||||
let fileName = [
|
||||
title.replace(/\s/g, '-'),
|
||||
[d.getDate(), d.getMonth(), d.getFullYear()].join('-'),
|
||||
`${d.getTime()}.csv`
|
||||
].join('_');
|
||||
var blob = new Blob([csvData], { type: 'text/plain;charset=utf-8' });
|
||||
await FileSaver.saveAs(blob, fileName);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
.fixed-btn-width {
|
||||
width: 5vw !important;
|
||||
}
|
||||
.bold {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
#select-cbox {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
#exportWizard {
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
71
src/components/InvoiceCustomizer.vue
Normal file
71
src/components/InvoiceCustomizer.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="doc" class="p-4">
|
||||
<div class="row">
|
||||
<div class="col-6 text-center">
|
||||
<h4>Customizer</h4>
|
||||
</div>
|
||||
<div class="col-6 text-right">
|
||||
<f-button secondary @click="$emit('closeInvoiceCustomizer')">{{ _('Close') }}</f-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 mt-4">
|
||||
<form-layout
|
||||
:doc="doc"
|
||||
:fields="fields"
|
||||
@updateDoc="saveDoc"
|
||||
/>
|
||||
<sketch-picker v-model="color"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import FormLayout from 'frappejs/ui/components/Form/FormLayout';
|
||||
import { Sketch } from 'vue-color';
|
||||
|
||||
export default {
|
||||
name: 'InvoiceCustomizer',
|
||||
components: {
|
||||
FormLayout,
|
||||
'sketch-picker': Sketch
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
doc: null,
|
||||
fields: [],
|
||||
color: null,
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.doc = await frappe.getDoc('InvoiceSettings');
|
||||
this.color = this.doc.themeColor;
|
||||
const meta = frappe.getMeta('InvoiceSettings');
|
||||
this.fields = meta.fields.filter((field) => field.fieldname !== "numberSeries");
|
||||
},
|
||||
methods: {
|
||||
async saveDoc(updatedValue) {
|
||||
let { fieldname, value } = updatedValue;
|
||||
if (fieldname === 'template') {
|
||||
this.$emit('changeTemplate', value);
|
||||
} else if (fieldname === 'font') {
|
||||
this.$emit('changeFont', value);
|
||||
}
|
||||
await this.doc.update();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
color: async function() {
|
||||
if (this.doc) {
|
||||
if (this.doc.themeColor != this.color.hex) {
|
||||
this.doc.themeColor = this.color.hex;
|
||||
this.$emit('changeColor', this.color.hex);
|
||||
await this.doc.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -6,7 +6,7 @@
|
||||
<script>
|
||||
export default {
|
||||
props: ['title']
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
.page-header {
|
||||
|
@ -4,12 +4,17 @@
|
||||
<div class="company-name px-3 py-2 my-2">
|
||||
<h6 class="m-0">{{ companyName }}</h6>
|
||||
</div>
|
||||
<div :class="['sidebar-item px-3 py-2 ', isCurrentRoute(item.route) ? 'active' : '']" @click="routeTo(item.route)" v-for="item in items" :key="item.label">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
<div
|
||||
:class="['sidebar-item px-3 py-2 ', isCurrentRoute(item.route) ? 'active' : '']"
|
||||
@click="routeTo(item.route)"
|
||||
v-for="item in items"
|
||||
:key="item.label"
|
||||
>{{ item.label }}</div>
|
||||
</div>
|
||||
<div class="sidebar-item px-3 py-2 d-flex align-items-center"
|
||||
v-if="dbFileName" @click="goToDatabaseSelector"
|
||||
<div
|
||||
class="sidebar-item px-3 py-2 d-flex align-items-center"
|
||||
v-if="dbFileName"
|
||||
@click="goToDatabaseSelector"
|
||||
>
|
||||
<feather-icon class="mr-2" name="settings"></feather-icon>
|
||||
<span>{{ dbFileName }}</span>
|
||||
@ -26,7 +31,7 @@ export default {
|
||||
items: [
|
||||
{
|
||||
label: 'Chart of Accounts',
|
||||
route: '/tree/Account'
|
||||
route: '/chartOfAccounts'
|
||||
},
|
||||
{
|
||||
label: 'Customers',
|
||||
@ -36,6 +41,14 @@ export default {
|
||||
label: 'Items',
|
||||
route: '/list/Item'
|
||||
},
|
||||
{
|
||||
label: 'Tax',
|
||||
route: '/list/Tax'
|
||||
},
|
||||
{
|
||||
label: 'Payments',
|
||||
route: '/list/Payment'
|
||||
},
|
||||
{
|
||||
label: 'Invoices',
|
||||
route: '/list/Invoice'
|
||||
@ -47,9 +60,9 @@ export default {
|
||||
{
|
||||
label: 'Settings',
|
||||
route: '/settings'
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
const accountingSettings = await frappe.getDoc('AccountingSettings');
|
||||
@ -71,11 +84,11 @@ export default {
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../styles/variables.scss";
|
||||
@import '../styles/variables.scss';
|
||||
|
||||
.page-sidebar {
|
||||
height: 100vh;
|
||||
|
@ -5,6 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<title>Frappe Accounting</title>
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat|Open+Sans|Oxygen|Merriweather" rel="stylesheet">
|
||||
<% if (htmlWebpackPlugin.options.nodeModules) { %>
|
||||
<script>
|
||||
require('module').globalPaths.push('<%= htmlWebpackPlugin.options.nodeModules.replace(/\\/g, '\\\\') %>')
|
||||
|
@ -6,7 +6,10 @@ import common from 'frappejs/common';
|
||||
import coreModels from 'frappejs/models';
|
||||
import models from '../models';
|
||||
import postStart from '../server/postStart';
|
||||
import { getSettings, saveSettings } from '../electron/settings';
|
||||
import {
|
||||
getSettings,
|
||||
saveSettings
|
||||
} from '../electron/settings';
|
||||
|
||||
// vue imports
|
||||
import Vue from 'vue';
|
||||
@ -23,10 +26,19 @@ import Toasted from 'vue-toasted';
|
||||
frappe.registerModels(coreModels);
|
||||
frappe.registerModels(models);
|
||||
frappe.fetch = window.fetch.bind();
|
||||
|
||||
frappe.events.on('connect-database', async (filepath) => {
|
||||
await connectToLocalDatabase(filepath);
|
||||
|
||||
const accountingSettings = await frappe.getSingle('AccountingSettings');
|
||||
const country = accountingSettings.country;
|
||||
|
||||
if (country === "India") {
|
||||
frappe.models.Party = require('../models/doctype/Party/RegionalChanges.js')
|
||||
} else {
|
||||
frappe.models.Party = require('../models/doctype/Party/Party.js')
|
||||
}
|
||||
frappe.events.trigger('show-desk');
|
||||
|
||||
});
|
||||
|
||||
frappe.events.on('DatabaseSelector:file-selected', async (filepath) => {
|
||||
@ -65,34 +77,93 @@ import Toasted from 'vue-toasted';
|
||||
});
|
||||
|
||||
await doc.update();
|
||||
await frappe.call({ method: 'import-coa' });
|
||||
await frappe.call({
|
||||
method: 'import-coa'
|
||||
});
|
||||
|
||||
if (country === "India") {
|
||||
frappe.models.Party = require('../models/doctype/Party/RegionalChanges.js')
|
||||
await frappe.db.migrate()
|
||||
await generateGstTaxes();
|
||||
}
|
||||
frappe.events.trigger('show-desk');
|
||||
});
|
||||
async function generateGstTaxes() {
|
||||
const gstPercents = [5, 12, 18, 28];
|
||||
const gstTypes = ['Out of State', 'In State'];
|
||||
let newTax = await frappe.getNewDoc('Tax');
|
||||
for (const type of gstTypes) {
|
||||
for (const percent of gstPercents) {
|
||||
switch (type) {
|
||||
case 'Out of State':
|
||||
await newTax.set({
|
||||
name: `${type}-${percent}`,
|
||||
details: [{
|
||||
account: "IGST",
|
||||
rate: percent
|
||||
}]
|
||||
})
|
||||
break;
|
||||
case 'In State':
|
||||
await newTax.set({
|
||||
name: `${type}-${percent}`,
|
||||
details: [{
|
||||
account: "CGST",
|
||||
rate: percent / 2
|
||||
},
|
||||
{
|
||||
account: "SGST",
|
||||
rate: percent / 2
|
||||
}
|
||||
]
|
||||
})
|
||||
break;
|
||||
}
|
||||
await newTax.insert();
|
||||
}
|
||||
}
|
||||
await newTax.set({
|
||||
name: `Exempt-0`,
|
||||
details: [{
|
||||
account: "Exempt",
|
||||
rate: 0
|
||||
}]
|
||||
})
|
||||
await newTax.insert();
|
||||
}
|
||||
|
||||
async function connectToLocalDatabase(filepath) {
|
||||
frappe.login('Administrator');
|
||||
frappe.db = new SQLite({ dbPath: filepath });
|
||||
await frappe.db.connect();
|
||||
await frappe.db.migrate();
|
||||
frappe.getSingle('SystemSettings');
|
||||
await postStart();
|
||||
try {
|
||||
frappe.login('Administrator');
|
||||
frappe.db = new SQLite({
|
||||
dbPath: filepath
|
||||
});
|
||||
await frappe.db.connect();
|
||||
await frappe.db.migrate();
|
||||
frappe.getSingle('SystemSettings');
|
||||
await postStart();
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
window.frappe = frappe;
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
Vue.use(frappeVue);
|
||||
Vue.use(Toasted, {
|
||||
position: 'bottom-right',
|
||||
duration : 3000
|
||||
});
|
||||
duration: 3000
|
||||
});
|
||||
|
||||
/* eslint-disable no-new */
|
||||
new Vue({
|
||||
el: '#app',
|
||||
router,
|
||||
components: { App },
|
||||
components: {
|
||||
App
|
||||
},
|
||||
template: '<App/>'
|
||||
});
|
||||
})()
|
||||
})()
|
96
src/pages/ChartOfAccounts/Branch.vue
Normal file
96
src/pages/ChartOfAccounts/Branch.vue
Normal file
@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<div class="branch">
|
||||
<div class="branch-label px-3 py-2" @click.self="toggleChildren">
|
||||
<div class="d-flex align-items-center" @click="toggleChildren">
|
||||
<feather-icon class="mr-1" :name="iconName" v-show="iconName" />
|
||||
<span>{{ label }}</span>
|
||||
<div class="ml-auto d-flex align-items-center">
|
||||
<feather-icon v-if="balance !== ''" style="width:15px; height:15px" name="dollar-sign"/>
|
||||
<span>{{ balance }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div :class="['branch-children', expanded ? '' : 'd-none']">
|
||||
<branch v-for="child in children" :key="child.label"
|
||||
:label="child.label"
|
||||
:balance="child.balance"
|
||||
:parentValue="child.name"
|
||||
:doctype="doctype"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
const Branch = {
|
||||
props: ['label', 'parentValue', 'doctype', 'balance'],
|
||||
data() {
|
||||
return {
|
||||
expanded: false,
|
||||
children: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iconName() {
|
||||
if (this.children && this.children.length ==0) return 'chevron-right';
|
||||
return this.expanded ? 'chevron-down' : 'chevron-right';
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Branch: () => Promise.resolve(Branch)
|
||||
},
|
||||
mounted() {
|
||||
this.settings = frappe.getMeta(this.doctype).treeSettings;
|
||||
},
|
||||
methods: {
|
||||
async toggleChildren() {
|
||||
await this.getChildren();
|
||||
this.expanded = !this.expanded;
|
||||
},
|
||||
async getChildren() {
|
||||
if (this.children) return;
|
||||
|
||||
this.children = [];
|
||||
|
||||
let filters = {
|
||||
[this.settings.parentField]: this.parentValue
|
||||
};
|
||||
|
||||
const children = await frappe.db.getAll({
|
||||
doctype: this.doctype,
|
||||
filters,
|
||||
fields: [this.settings.parentField, 'isGroup', 'name', 'balance'],
|
||||
orderBy: 'name',
|
||||
order: 'asc'
|
||||
});
|
||||
|
||||
this.children = children.map(c => {
|
||||
c.label = c.name;
|
||||
c.balance = c.balance
|
||||
return c;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default Branch;
|
||||
</script>
|
||||
<style lang="scss">
|
||||
@import "../../styles/variables";
|
||||
|
||||
.branch {
|
||||
font-size: 1rem;
|
||||
}
|
||||
.borderBottom {
|
||||
border-bottom: 0.1rem solid #aaaaaa;
|
||||
}
|
||||
.branch-label {
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.branch-label:hover {
|
||||
background-color: $dropdown-link-hover-bg;
|
||||
}
|
||||
.branch-children {
|
||||
padding-left: 2.25rem;
|
||||
}
|
||||
</style>
|
50
src/pages/ChartOfAccounts/index.vue
Normal file
50
src/pages/ChartOfAccounts/index.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="p-3" v-if="root">
|
||||
<branch :label="root.label" :balance="root.balance" :parentValue="''" :doctype="doctype" ref="root"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import frappe from 'frappejs';
|
||||
import Branch from './Branch';
|
||||
import { setTimeout } from 'timers';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Branch,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
root: null,
|
||||
doctype: "Account"
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.settings = frappe.getMeta(this.doctype).treeSettings;
|
||||
this.root = {
|
||||
label: await this.settings.getRootLabel(),
|
||||
balance: 'Net Worth'
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async getChildren(parentValue) {
|
||||
let filters = {
|
||||
[this.settings.parentField]: parentValue
|
||||
};
|
||||
|
||||
const children = await frappe.db.getAll({
|
||||
doctype: this.doctype,
|
||||
filters,
|
||||
fields: [this.settings.parentField, 'isGroup', 'name', 'balance'],
|
||||
orderBy: 'name',
|
||||
order: 'asc'
|
||||
});
|
||||
|
||||
return children.map(c => {
|
||||
c.label = c.name;
|
||||
c.balance = c.balance;
|
||||
return c;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
47
src/pages/Email/EmailSend.vue
Normal file
47
src/pages/Email/EmailSend.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="frappe-form">
|
||||
<form-actions v-if="shouldRenderForm" :doc="doc" @send="send"/>
|
||||
<div class="p-3">
|
||||
<form-layout
|
||||
v-if="shouldRenderForm"
|
||||
:doc="doc"
|
||||
:fields="meta.fields"
|
||||
:layout="meta.layout"
|
||||
:invalid="invalid"
|
||||
/>
|
||||
</div>
|
||||
<not-found v-if="notFound"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import frappe from 'frappejs';
|
||||
import Form from 'frappejs/ui/components/Form/Form';
|
||||
import FormActions from './EmailSendActions';
|
||||
|
||||
export default {
|
||||
name: 'Form',
|
||||
extends: Form,
|
||||
components: {
|
||||
FormActions
|
||||
},
|
||||
methods: {
|
||||
async send() {
|
||||
this.doc = await frappe.getDoc(this.doctype, this.name);
|
||||
var response = await frappe.call({
|
||||
method: 'send-mail',
|
||||
args: this.doc.getValidDict()
|
||||
});
|
||||
if (response) {
|
||||
let emailFields = frappe.getMeta('Email').fields;
|
||||
// this.doc['name'] = this.name;
|
||||
this.save();
|
||||
} else {
|
||||
// Raise Error
|
||||
console.log('Email Not Found');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
25
src/pages/Email/EmailSendActions.vue
Normal file
25
src/pages/Email/EmailSendActions.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div
|
||||
class="frappe-form-actions d-flex justify-content-between align-items-center p-3 border-bottom"
|
||||
>
|
||||
<h5 class="m-0">Compose Email</h5>
|
||||
<h5 class="m-0"></h5>
|
||||
<f-button primary v-if="isDirty" @click="$emit('send')">Send</f-button>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import frappe from 'frappejs';
|
||||
export default {
|
||||
props: ['doc'],
|
||||
data() {
|
||||
return {
|
||||
isDirty: false
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.doc.on('change', () => {
|
||||
this.isDirty = this.doc._dirty;
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<div class="bg-light">
|
||||
<page-header :title="doctype" />
|
||||
<div class="form-container col-8 bg-white mt-4 ml-auto mr-auto border p-5">
|
||||
<form-actions
|
||||
v-if="shouldRenderForm"
|
||||
@ -49,7 +48,7 @@ export default {
|
||||
links: [],
|
||||
isFormInvalid: false,
|
||||
notFound: false
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
shouldRenderForm() {
|
||||
@ -70,7 +69,10 @@ export default {
|
||||
this.doc = null;
|
||||
this.doc = await frappe.getDoc(this.doctype, this.name);
|
||||
|
||||
if (this.doc._notInserted && this.meta.fields.map(df => df.fieldname).includes('name')) {
|
||||
if (
|
||||
this.doc._notInserted &&
|
||||
this.meta.fields.map(df => df.fieldname).includes('name')
|
||||
) {
|
||||
// For a user editable name field,
|
||||
// it should be unset since it is autogenerated
|
||||
this.doc.set('name', '');
|
||||
@ -103,7 +105,6 @@ export default {
|
||||
}
|
||||
|
||||
this.$emit('save', this.doc);
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return;
|
||||
@ -145,5 +146,5 @@ export default {
|
||||
this.isFormInvalid = !validity;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,13 +1,11 @@
|
||||
<template>
|
||||
<div class="list-container">
|
||||
<list-row class="text-muted rounded-top bg-light">
|
||||
<list-cell v-for="column in columns" :key="column.label">
|
||||
{{ column.label }}
|
||||
</list-cell>
|
||||
<list-cell v-for="column in columns" :key="column.label">{{ column.label }}</list-cell>
|
||||
</list-row>
|
||||
<list-row v-for="doc in data" :key="doc.name" @click.native="openForm(doc.name)">
|
||||
<list-cell v-for="column in columns" :key="column.label" class="d-flex align-items-center">
|
||||
<indicator v-if="column.getIndicator" :color="column.getIndicator(doc)" class="mr-2" />
|
||||
<indicator v-if="column.getIndicator" :color="column.getIndicator(doc)" class="mr-2"/>
|
||||
<span>{{ frappe.format(column.getValue(doc), column.fieldtype || {}) }}</span>
|
||||
</list-cell>
|
||||
</list-row>
|
||||
@ -66,20 +64,22 @@ export default {
|
||||
});
|
||||
},
|
||||
prepareColumns() {
|
||||
this.columns = this.listConfig.columns.map(col => {
|
||||
if (typeof col === 'string') {
|
||||
const field = this.meta.getField(col);
|
||||
if (!field) return null;
|
||||
return {
|
||||
label: field.label,
|
||||
fieldtype: field.fieldtype,
|
||||
getValue(doc) {
|
||||
return doc[col];
|
||||
}
|
||||
};
|
||||
}
|
||||
return col;
|
||||
}).filter(Boolean);
|
||||
this.columns = this.listConfig.columns
|
||||
.map(col => {
|
||||
if (typeof col === 'string') {
|
||||
const field = this.meta.getField(col);
|
||||
if (!field) return null;
|
||||
return {
|
||||
label: field.label,
|
||||
fieldtype: field.fieldtype,
|
||||
getValue(doc) {
|
||||
return doc[col];
|
||||
}
|
||||
};
|
||||
}
|
||||
return col;
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -2,10 +2,12 @@ import Invoice from '../../../models/doctype/Invoice/InvoiceList';
|
||||
import Customer from '../../../models/doctype/Party/CustomerList';
|
||||
import Item from '../../../models/doctype/Item/ItemList';
|
||||
import Payment from '../../../models/doctype/Payment/PaymentList';
|
||||
import Tax from '../../../models/doctype/Tax/TaxList';
|
||||
|
||||
export default {
|
||||
Invoice,
|
||||
Customer,
|
||||
Item,
|
||||
Payment
|
||||
Payment,
|
||||
Tax
|
||||
}
|
@ -1,35 +1,63 @@
|
||||
<template>
|
||||
<div class="bg-light">
|
||||
<page-header :title="doctype" />
|
||||
<page-header :title="doctype"/>
|
||||
<div class="row no-gutters">
|
||||
<div v-if="showInvoiceCustomizer" class="col-3 mt-4 mx-auto"></div>
|
||||
<div class="col-8 mx-auto text-right mt-4">
|
||||
<f-button primary @click="makePDF">{{ _('PDF') }}</f-button>
|
||||
<f-button primary @click="send">{{ _('Send') }}</f-button>
|
||||
<f-button secondary @click="toggleInvoiceCustomizer">{{ _('Customize') }}</f-button>
|
||||
<f-button secondary @click="makePDF">{{ _('PDF') }}</f-button>
|
||||
</div>
|
||||
<div ref="printComponent" class="form-container col-8 bg-white mt-4 mx-auto border">
|
||||
<component :is="printComponent" v-if="doc" :doc="doc" />
|
||||
</div>
|
||||
<div class="row no-gutters">
|
||||
<div v-if="showInvoiceCustomizer" class="col-3 mt-4 mx-auto">
|
||||
<invoice-customizer
|
||||
class="border"
|
||||
style="position: fixed"
|
||||
@closeInvoiceCustomizer="toggleInvoiceCustomizer"
|
||||
@changeColor="changeColor($event)"
|
||||
@changeTemplate="changeTemplate($event)"
|
||||
@changeFont="changeFont($event)"
|
||||
/>
|
||||
</div>
|
||||
<div ref="printComponent" class="col-8 bg-white mt-4 mx-auto border shadow">
|
||||
<component
|
||||
:themeColor="themeColor"
|
||||
:template="template"
|
||||
:font="font"
|
||||
:is="printComponent"
|
||||
v-if="doc"
|
||||
:doc="doc"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import PageHeader from '@/components/PageHeader';
|
||||
import InvoicePrint from '@/../models/doctype/Invoice/InvoicePrint'
|
||||
import InvoiceCustomizer from '@/components/InvoiceCustomizer';
|
||||
import InvoicePrint from '@/../models/doctype/Invoice/InvoicePrint';
|
||||
import EmailSend from '../Email/EmailSend';
|
||||
|
||||
const printComponents = {
|
||||
Invoice: InvoicePrint
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'PrintView',
|
||||
props: ['doctype', 'name'],
|
||||
components: {
|
||||
PageHeader
|
||||
PageHeader,
|
||||
InvoiceCustomizer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
doc: null,
|
||||
printComponent: null
|
||||
}
|
||||
doc: undefined,
|
||||
printComponent: undefined,
|
||||
themeColor: undefined,
|
||||
template: undefined,
|
||||
font: undefined,
|
||||
showInvoiceCustomizer: false
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
this.doc = await frappe.getDoc(this.doctype, this.name);
|
||||
@ -44,8 +72,42 @@ export default {
|
||||
name: this.name,
|
||||
html: this.$refs.printComponent.innerHTML
|
||||
}
|
||||
})
|
||||
});
|
||||
},
|
||||
async send() {
|
||||
let doc = await frappe.getNewDoc('Email');
|
||||
let emailFields = frappe.getMeta('Email').fields;
|
||||
var file_path = this.name;
|
||||
doc['fromEmailAddress'] = this.selectedId;
|
||||
this.makePDF();
|
||||
doc['filePath'] = this.name + '.pdf';
|
||||
this.$modal.show({
|
||||
component: EmailSend,
|
||||
props: {
|
||||
doctype: doc.doctype,
|
||||
name: doc.name
|
||||
},
|
||||
modalProps: {
|
||||
title: 'Send Invoice',
|
||||
footerMessage: 'Invoice attached along..'
|
||||
}
|
||||
});
|
||||
doc.on('afterInsert', data => {
|
||||
this.$modal.hide();
|
||||
});
|
||||
},
|
||||
async toggleInvoiceCustomizer() {
|
||||
this.showInvoiceCustomizer = !this.showInvoiceCustomizer;
|
||||
},
|
||||
changeColor(color) {
|
||||
this.themeColor = color;
|
||||
},
|
||||
changeTemplate(template) {
|
||||
this.template = template;
|
||||
},
|
||||
changeFont(font) {
|
||||
this.font = font;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
};
|
||||
</script>
|
111
src/pages/Report/index.vue
Normal file
111
src/pages/Report/index.vue
Normal file
@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="p-4">
|
||||
<h4 class="pb-2">{{ reportConfig.title }}</h4>
|
||||
<div class="col-12 text-right mt-4 mb-4">
|
||||
<f-button primary @click="openExportWizard">{{ 'Export' }}</f-button>
|
||||
</div>
|
||||
<report-filters
|
||||
v-if="filtersExists"
|
||||
:filters="reportConfig.filterFields"
|
||||
:filterDefaults="filters"
|
||||
@change="getReportData"
|
||||
></report-filters>
|
||||
<div class="pt-2" ref="datatable" v-once></div>
|
||||
</div>
|
||||
<not-found v-if="!reportConfig"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import DataTable from 'frappe-datatable';
|
||||
import frappe from 'frappejs';
|
||||
import ReportFilters from 'frappejs/ui/pages/Report/ReportFilters';
|
||||
import utils from 'frappejs/client/ui/utils';
|
||||
import ExportWizard from '../../components/ExportWizard';
|
||||
|
||||
export default {
|
||||
name: 'Report',
|
||||
props: ['reportName', 'reportConfig', 'filters'],
|
||||
computed: {
|
||||
filtersExists() {
|
||||
return (this.reportConfig.filterFields || []).length;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async openExportWizard() {
|
||||
this.$modal.show({
|
||||
modalProps: {
|
||||
title: `Export ${this.reportConfig.title}`,
|
||||
noFooter: true
|
||||
},
|
||||
component: ExportWizard,
|
||||
props: await this.getReportDetails()
|
||||
});
|
||||
},
|
||||
async getReportDetails() {
|
||||
let { title, filterFields } = this.reportConfig;
|
||||
let [rows, columns] = await this.getReportData(filterFields || []);
|
||||
let columnData = columns.map(column => {
|
||||
return {
|
||||
id: column.id,
|
||||
content: column.content,
|
||||
checked: true
|
||||
};
|
||||
});
|
||||
return {
|
||||
title: title,
|
||||
rows: rows,
|
||||
columnData: columnData
|
||||
};
|
||||
},
|
||||
async getReportData(filters) {
|
||||
let data = await frappe.call({
|
||||
method: this.reportConfig.method,
|
||||
args: filters
|
||||
});
|
||||
|
||||
let rows, columns;
|
||||
if (data.rows) {
|
||||
rows = data.rows;
|
||||
} else {
|
||||
rows = data;
|
||||
}
|
||||
|
||||
if (data.columns) {
|
||||
columns = this.getColumns(data);
|
||||
}
|
||||
|
||||
if (!rows) {
|
||||
rows = [];
|
||||
}
|
||||
|
||||
if (!columns) {
|
||||
columns = this.getColumns();
|
||||
}
|
||||
|
||||
for (let column of columns) {
|
||||
column.editable = false;
|
||||
}
|
||||
|
||||
if (this.datatable) {
|
||||
this.datatable.refresh(rows, columns);
|
||||
} else {
|
||||
this.datatable = new DataTable(this.$refs.datatable, {
|
||||
columns: columns,
|
||||
data: rows
|
||||
});
|
||||
}
|
||||
return [rows, columns];
|
||||
},
|
||||
getColumns(data) {
|
||||
const columns = this.reportConfig.getColumns(data);
|
||||
return utils.convertFieldsToDatatableColumns(columns);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ReportFilters
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header title="Reports" />
|
||||
<page-header title="Reports"/>
|
||||
<div class="row">
|
||||
<div class="col-8 mx-auto">
|
||||
<clickable-card
|
||||
@ -27,25 +27,48 @@
|
||||
description="Sales transactions for a given period with invoiced amount and tax details"
|
||||
@click="routeTo('sales-register')"
|
||||
/>
|
||||
<clickable-card
|
||||
class="mt-2"
|
||||
title="Bank Reconciliation"
|
||||
description="Bank Reconciliation statement"
|
||||
@click="routeTo('bank-reconciliation',{'toDate' : (new Date()).toISOString()})"
|
||||
/>
|
||||
<clickable-card v-if="country === 'India'"
|
||||
class="mt-2"
|
||||
title="Goods and Service Tax"
|
||||
description="See your goods and services tax here."
|
||||
@click="routeTo('gst-taxes',{'toDate' : (new Date()).toISOString()})"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import frappe from 'frappejs';
|
||||
import PageHeader from '../components/PageHeader';
|
||||
import ClickableCard from '../components/ClickableCard';
|
||||
|
||||
export default {
|
||||
name: 'Report',
|
||||
data() {
|
||||
return {
|
||||
country: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
PageHeader,
|
||||
ClickableCard
|
||||
},
|
||||
async created() {
|
||||
const doc = await frappe.getDoc('AccountingSettings');
|
||||
this.country = doc.country;
|
||||
|
||||
},
|
||||
methods: {
|
||||
routeTo(route, filters) {
|
||||
const query = new URLSearchParams(filters);
|
||||
this.$router.push(`/report/${route}?${query}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -1,11 +1,15 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-header title="Settings" />
|
||||
<page-header title="Settings"/>
|
||||
<div class="row">
|
||||
<div class="col-8 bg-white mt-4 mx-auto border p-5">
|
||||
<setting-section doctype="AccountingSettings" />
|
||||
<setting-section doctype="AccountingSettings"/>
|
||||
<hr class="mt-4">
|
||||
<setting-section doctype="SystemSettings" />
|
||||
<setting-section doctype="CompanySettings"/>
|
||||
<hr class="mt-4">
|
||||
<setting-section doctype="EmailAccount"/>
|
||||
<hr class="mt-4">
|
||||
<setting-section doctype="SystemSettings"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -20,5 +24,5 @@ export default {
|
||||
PageHeader,
|
||||
SettingSection
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -1,32 +1,31 @@
|
||||
<template>
|
||||
<div class="setup-wizard d-flex justify-content-center">
|
||||
<div v-if="meta" class="setup-wizard d-flex justify-content-center">
|
||||
<div class="col-4 border rounded shadow-sm p-4 mt-5">
|
||||
<h4 class="text-center">Setup your account</h4>
|
||||
<div class="progress-indicator d-flex justify-content-center p-4">
|
||||
<indicator class="mr-1" :color="indicatorColor(index)" v-for="(section, index) in layout.sections" :key="index" />
|
||||
<indicator
|
||||
class="mr-1"
|
||||
:color="indicatorColor(index)"
|
||||
v-for="(section, index) in layout.sections"
|
||||
:key="index"
|
||||
/>
|
||||
</div>
|
||||
<form-layout
|
||||
:doc="doc"
|
||||
:fields="fields"
|
||||
:layout="layout"
|
||||
:currentSection="currentSection"
|
||||
/>
|
||||
<form-layout :doc="doc" :fields="fields" :layout="layout" :currentSection="currentSection"/>
|
||||
<div class="d-flex justify-content-between">
|
||||
<div>
|
||||
<f-button secondary v-if="currentSection > 0"
|
||||
@click="prevSection">
|
||||
Prev
|
||||
</f-button>
|
||||
<f-button secondary v-if="currentSection > 0" @click="prevSection">Prev</f-button>
|
||||
</div>
|
||||
<div>
|
||||
<f-button primary v-if="currentSection < layout.sections.length - 1"
|
||||
@click="nextSection">
|
||||
Next
|
||||
</f-button>
|
||||
<f-button primary v-if="currentSection === layout.sections.length - 1"
|
||||
@click="submit">
|
||||
Complete
|
||||
</f-button>
|
||||
<f-button
|
||||
primary
|
||||
v-if="currentSection < layout.sections.length - 1"
|
||||
@click="nextSection"
|
||||
>Next</f-button>
|
||||
<f-button
|
||||
primary
|
||||
v-if="currentSection === layout.sections.length - 1"
|
||||
@click="submit"
|
||||
>Complete</f-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -37,12 +36,12 @@ import frappe from 'frappejs';
|
||||
import Observable from 'frappejs/utils/observable';
|
||||
import FormLayout from 'frappejs/ui/components/Form/FormLayout';
|
||||
import indicatorColor from 'frappejs/ui/constants/indicators';
|
||||
import setupConfig from './config';
|
||||
|
||||
export default {
|
||||
name: 'SetupWizard',
|
||||
data() {
|
||||
return {
|
||||
meta: null,
|
||||
currentSection: 0
|
||||
};
|
||||
},
|
||||
@ -75,10 +74,10 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
fields() {
|
||||
return setupConfig.fields;
|
||||
return this.meta.fields;
|
||||
},
|
||||
layout() {
|
||||
return setupConfig.layout;
|
||||
return this.meta.layout;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -1,83 +0,0 @@
|
||||
const frappe = require('frappejs');
|
||||
const countryList = Object.keys(require('../../../fixtures/countryInfo.json')).sort();
|
||||
|
||||
export default {
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'country',
|
||||
label: 'Country',
|
||||
fieldtype: 'Autocomplete',
|
||||
required: 1,
|
||||
getList: () => countryList
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'name',
|
||||
label: 'Name',
|
||||
fieldtype: 'Data',
|
||||
required: 1
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'email',
|
||||
label: 'Email',
|
||||
fieldtype: 'Data',
|
||||
required: 1,
|
||||
inputType: 'email'
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'companyName',
|
||||
label: 'Company Name',
|
||||
fieldtype: 'Data',
|
||||
required: 1
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'bankName',
|
||||
label: 'Bank Name',
|
||||
fieldtype: 'Data',
|
||||
required: 1
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'fiscalYearStart',
|
||||
label: 'Fiscal Year Start Date',
|
||||
fieldtype: 'Date',
|
||||
required: 1
|
||||
},
|
||||
|
||||
{
|
||||
fieldname: 'fiscalYearEnd',
|
||||
label: 'Fiscal Year End Date',
|
||||
fieldtype: 'Date',
|
||||
required: 1
|
||||
}
|
||||
],
|
||||
|
||||
layout: {
|
||||
paginated: true,
|
||||
sections: [
|
||||
{
|
||||
title: 'Select Country',
|
||||
columns: [
|
||||
{ fields: ['country'] }
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Add a Profile',
|
||||
columns: [
|
||||
{ fields: ['name', 'email'] }
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
title: 'Add your Company',
|
||||
columns: [
|
||||
{ fields: ['companyName', 'bankName', 'fiscalYearStart', 'fiscalYearEnd'] }
|
||||
]
|
||||
}
|
||||
].filter(Boolean)
|
||||
}
|
||||
}
|
@ -5,14 +5,15 @@ import ListView from '../pages/ListView';
|
||||
import FormView from '../pages/FormView/FormView';
|
||||
import PrintView from '../pages/PrintView';
|
||||
|
||||
import Report from 'frappejs/ui/pages/Report';
|
||||
import Report from '../pages/Report';
|
||||
import reportViewConfig from '../../reports/view';
|
||||
|
||||
import DataImport from '../pages/DataImport';
|
||||
|
||||
import Settings from '../pages/Settings/Settings';
|
||||
|
||||
import ReportList from '../pages/Report';
|
||||
import ReportList from '../pages/ReportList';
|
||||
import ChartOfAccounts from '../pages/ChartOfAccounts';
|
||||
|
||||
import Tree from 'frappejs/ui/components/Tree';
|
||||
|
||||
@ -71,6 +72,12 @@ const routes = [
|
||||
component: Tree,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/chartOfAccounts',
|
||||
name: 'Chart Of Accounts',
|
||||
component: ChartOfAccounts,
|
||||
props: true
|
||||
},
|
||||
];
|
||||
|
||||
export default new Router({ routes });
|
||||
|
@ -49,6 +49,12 @@ export default {
|
||||
},
|
||||
{
|
||||
label: _('Sales Register'), route: '#/report/sales-register'
|
||||
},
|
||||
{
|
||||
label: _('Bank Reconciliation'), route: '#/report/bank-reconciliation'
|
||||
},
|
||||
{
|
||||
label: _('Goods and Service Tax'), route: '#/report/gst-taxes'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user