[feature] Opening Invoice Creation Tool (#11589)
* [enhance] Opening Invoice Tool create Opening Sales/Purchase Invoices * [minor] added progress bar while creating invoice * [minor] allow bulk upload for the invoices * [minor] calculate item rate from net total * [minor] added defaults for invoices fields, create gl entries only if grand_total is greater than 0 * [minor] check mandatory values before making opening invoices * [minor] enable primary button if there is any error while creating invoices * [minor] minor fixes, set company currency as invoice currency added tests * [dashboard] added Opening Invoice Summery section in Dashboard * [minor] added total invoices count on dashboard * [minor] don't show dashboard if there are no opening invoices available * [minor] added Opening Invoice Tools to Account's Tools section * [minor] codecy fixes * [fix] refactored general ledger and buttons on charts * [fix] tests
This commit is contained in:
parent
78ab8235f6
commit
d3f5d0961a
@ -41,6 +41,41 @@ frappe.treeview_settings["Account"] = {
|
|||||||
description: __("Optional. Sets company's default currency, if not specified.")}
|
description: __("Optional. Sets company's default currency, if not specified.")}
|
||||||
],
|
],
|
||||||
ignore_fields:["parent_account"],
|
ignore_fields:["parent_account"],
|
||||||
|
onload: function(treeview) {
|
||||||
|
function get_company() {
|
||||||
|
return treeview.page.fields_dict.company.get_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tools
|
||||||
|
treeview.page.add_inner_button(__("Chart of Cost Centers"), function() {
|
||||||
|
frappe.set_route('Tree', 'Cost Center', {company: get_company()});
|
||||||
|
}, __('View'));
|
||||||
|
|
||||||
|
treeview.page.add_inner_button(__("Opening Invoice Creation Tool"), function() {
|
||||||
|
frappe.set_route('Form', 'Opening Invoice Creation Tool', {company: get_company()});
|
||||||
|
}, __('View'));
|
||||||
|
|
||||||
|
treeview.page.add_inner_button(__("Period Closing Voucher"), function() {
|
||||||
|
frappe.set_route('List', 'Period Closing Voucher', {company: get_company()});
|
||||||
|
}, __('View'));
|
||||||
|
|
||||||
|
// make
|
||||||
|
treeview.page.add_inner_button(__("Journal Entry"), function() {
|
||||||
|
frappe.new_doc('Journal Entry', {company: get_company()});
|
||||||
|
}, __('Make'));
|
||||||
|
treeview.page.add_inner_button(__("New Company"), function() {
|
||||||
|
frappe.new_doc('Company');
|
||||||
|
}, __('Make'));
|
||||||
|
|
||||||
|
// financial statements
|
||||||
|
for (let report of ['Trial Balance', 'General Ledger', 'Balance Sheet',
|
||||||
|
'Profit and Loss', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) {
|
||||||
|
treeview.page.add_inner_button(__(report), function() {
|
||||||
|
frappe.set_route('query-report', report, {company: get_company()});
|
||||||
|
}, __('Financial Statements'));
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
onrender: function(node) {
|
onrender: function(node) {
|
||||||
var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr";
|
var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr";
|
||||||
if (node.data && node.data.balance!==undefined) {
|
if (node.data && node.data.balance!==undefined) {
|
||||||
|
@ -23,5 +23,30 @@ frappe.treeview_settings["Cost Center"] = {
|
|||||||
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
{fieldtype:'Check', fieldname:'is_group', label:__('Is Group'),
|
||||||
description:__('Further cost centers can be made under Groups but entries can be made against non-Groups')}
|
description:__('Further cost centers can be made under Groups but entries can be made against non-Groups')}
|
||||||
],
|
],
|
||||||
ignore_fields:["parent_cost_center"]
|
ignore_fields:["parent_cost_center"],
|
||||||
|
onload: function(treeview) {
|
||||||
|
function get_company() {
|
||||||
|
return treeview.page.fields_dict.company.get_value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tools
|
||||||
|
treeview.page.add_inner_button(__("Chart of Accounts"), function() {
|
||||||
|
frappe.set_route('Tree', 'Account', {company: get_company()});
|
||||||
|
}, __('View'));
|
||||||
|
|
||||||
|
// make
|
||||||
|
treeview.page.add_inner_button(__("Budget List"), function() {
|
||||||
|
frappe.set_route('List', 'Budget', {company: get_company()});
|
||||||
|
}, __('Budget'));
|
||||||
|
|
||||||
|
treeview.page.add_inner_button(__("Monthly Distribution"), function() {
|
||||||
|
frappe.set_route('List', 'Monthly Distribution', {company: get_company()});
|
||||||
|
}, __('Budget'));
|
||||||
|
|
||||||
|
treeview.page.add_inner_button(__("Budget Variance Report"), function() {
|
||||||
|
frappe.set_route('query-report', 'Budget Variance Report', {company: get_company()});
|
||||||
|
}, __('Budget'));
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,96 @@
|
|||||||
|
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
// For license information, please see license.txt
|
||||||
|
|
||||||
|
frappe.ui.form.on('Opening Invoice Creation Tool', {
|
||||||
|
setup: function(frm) {
|
||||||
|
frm.set_query('party_type', 'invoices', function(doc, cdt, cdn) {
|
||||||
|
return {
|
||||||
|
filters: {
|
||||||
|
'name': ['in', 'Customer,Supplier']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
refresh: function(frm) {
|
||||||
|
frm.disable_save();
|
||||||
|
frm.trigger("make_dashboard");
|
||||||
|
frm.page.set_primary_action(__("Make Invoices"), () => {
|
||||||
|
let btn_primary = frm.page.btn_primary.get(0);
|
||||||
|
return frm.call({
|
||||||
|
doc: frm.doc,
|
||||||
|
freeze: true,
|
||||||
|
btn: $(btn_primary),
|
||||||
|
method: "make_invoices",
|
||||||
|
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]),
|
||||||
|
callback: (r) => {
|
||||||
|
if(!r.exc){
|
||||||
|
frappe.msgprint(__("Opening {0} Invoice created", [frm.doc.invoice_type]));
|
||||||
|
frm.clear_table("invoices");
|
||||||
|
frm.refresh_fields();
|
||||||
|
frm.reload_doc();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
company: function(frm) {
|
||||||
|
frappe.call({
|
||||||
|
method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account',
|
||||||
|
args: {
|
||||||
|
company: frm.doc.company
|
||||||
|
},
|
||||||
|
callback: (r) => {
|
||||||
|
if (r.message) {
|
||||||
|
frm.doc.__onload.temporary_opening_account = r.message;
|
||||||
|
frm.trigger('update_invoice_table');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
invoice_type: function(frm) {
|
||||||
|
$.each(frm.doc.invoices, (idx, row) => {
|
||||||
|
row.party_type = frm.doc.invoice_type == "Sales"? "Customer": "Supplier";
|
||||||
|
row.party = "";
|
||||||
|
});
|
||||||
|
frm.refresh_fields();
|
||||||
|
},
|
||||||
|
|
||||||
|
make_dashboard: function(frm) {
|
||||||
|
let max_count = frm.doc.__onload.max_count;
|
||||||
|
let opening_invoices_summary = frm.doc.__onload.opening_invoices_summary;
|
||||||
|
if(!$.isEmptyObject(opening_invoices_summary)) {
|
||||||
|
let section = frm.dashboard.add_section(
|
||||||
|
frappe.render_template('opening_invoice_creation_tool_dashboard', {
|
||||||
|
data: opening_invoices_summary,
|
||||||
|
max_count: max_count
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
section.on('click', '.invoice-link', function() {
|
||||||
|
let doctype = $(this).attr('data-type');
|
||||||
|
let company = $(this).attr('data-company');
|
||||||
|
frappe.set_route('List', doctype,
|
||||||
|
{'is_opening': 'Yes', 'company': company, 'docstatus': 1});
|
||||||
|
});
|
||||||
|
frm.dashboard.show();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
update_invoice_table: function(frm) {
|
||||||
|
$.each(frm.doc.invoices, (idx, row) => {
|
||||||
|
if (!row.temporary_opening_account) {
|
||||||
|
row.temporary_opening_account = frm.doc.__onload.temporary_opening_account;
|
||||||
|
}
|
||||||
|
row.party_type = frm.doc.invoice_type == "Sales"? "Customer": "Supplier";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on('Opening Invoice Creation Tool Item', {
|
||||||
|
invoices_add: (frm) => {
|
||||||
|
frm.trigger('update_invoice_table');
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,184 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 1,
|
||||||
|
"allow_guest_to_view": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"beta": 1,
|
||||||
|
"creation": "2017-08-29 02:22:54.947711",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Company",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Company",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "invoice_type",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Invoice Type",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Sales\nPurchase",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "section_break_4",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Invoices",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 1,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "invoices",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Opening Invoice Creation Tool Item",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_web_view": 0,
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 1,
|
||||||
|
"idx": 0,
|
||||||
|
"image_view": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 1,
|
||||||
|
"istable": 0,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2017-09-05 01:30:33.235664",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Opening Invoice Creation Tool",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [
|
||||||
|
{
|
||||||
|
"amend": 0,
|
||||||
|
"apply_user_permissions": 0,
|
||||||
|
"cancel": 0,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 0,
|
||||||
|
"email": 1,
|
||||||
|
"export": 0,
|
||||||
|
"if_owner": 0,
|
||||||
|
"import": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 0,
|
||||||
|
"role": "System Manager",
|
||||||
|
"set_user_permissions": 0,
|
||||||
|
"share": 1,
|
||||||
|
"submit": 0,
|
||||||
|
"write": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"show_name_in_global_search": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1,
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
@ -0,0 +1,163 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe import _
|
||||||
|
from frappe.utils import flt
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class OpeningInvoiceCreationTool(Document):
|
||||||
|
def onload(self):
|
||||||
|
"""Load the Opening Invoice summary"""
|
||||||
|
summary, max_count = self.get_opening_invoice_summary()
|
||||||
|
self.set_onload('opening_invoices_summary', summary)
|
||||||
|
self.set_onload('max_count', max_count)
|
||||||
|
self.set_onload('temporary_opening_account', get_temporary_opening_account(self.company))
|
||||||
|
|
||||||
|
def get_opening_invoice_summary(self):
|
||||||
|
def prepare_invoice_summary(doctype, invoices):
|
||||||
|
# add company wise sales / purchase invoice summary
|
||||||
|
paid_amount = []
|
||||||
|
outstanding_amount = []
|
||||||
|
for invoice in invoices:
|
||||||
|
company = invoice.pop("company")
|
||||||
|
_summary = invoices_summary.get(company, {})
|
||||||
|
_summary.update({
|
||||||
|
"currency": company_wise_currency.get(company),
|
||||||
|
doctype: invoice
|
||||||
|
})
|
||||||
|
invoices_summary.update({company: _summary})
|
||||||
|
|
||||||
|
paid_amount.append(invoice.paid_amount)
|
||||||
|
outstanding_amount.append(invoice.outstanding_amount)
|
||||||
|
|
||||||
|
if paid_amount or outstanding_amount:
|
||||||
|
max_count.update({
|
||||||
|
doctype: {
|
||||||
|
"max_paid": max(paid_amount) if paid_amount else 0.0,
|
||||||
|
"max_due": max(outstanding_amount) if outstanding_amount else 0.0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
invoices_summary = {}
|
||||||
|
max_count = {}
|
||||||
|
fields = [
|
||||||
|
"company", "count(name) as total_invoices", "sum(outstanding_amount) as outstanding_amount"
|
||||||
|
]
|
||||||
|
companies = frappe.get_all("Company", fields=["name as company", "default_currency as currency"])
|
||||||
|
if not companies:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
company_wise_currency = {row.company: row.currency for row in companies}
|
||||||
|
for doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
|
invoices = frappe.get_all(doctype, filters=dict(is_opening="Yes", docstatus=1),
|
||||||
|
fields=fields, group_by="company")
|
||||||
|
prepare_invoice_summary(doctype, invoices)
|
||||||
|
|
||||||
|
return invoices_summary, max_count
|
||||||
|
|
||||||
|
def make_invoices(self):
|
||||||
|
names = []
|
||||||
|
mandatory_error_msg = _("Row {idx}: {field} is required to create the Opening {invoice_type} Invoices")
|
||||||
|
if not self.company:
|
||||||
|
frappe.throw(_("Please select the Company"))
|
||||||
|
|
||||||
|
for row in self.invoices:
|
||||||
|
if not row.qty:
|
||||||
|
row.qty = 1.0
|
||||||
|
if not row.party:
|
||||||
|
frappe.throw(mandatory_error_msg.format(
|
||||||
|
idx=row.idx,
|
||||||
|
field= _("Party"),
|
||||||
|
invoice_type=self.invoice_type
|
||||||
|
))
|
||||||
|
# set party type if not available
|
||||||
|
if not row.party_type:
|
||||||
|
row.party_type = "Customer" if self.invoice_type == "Sales" else "Supplier"
|
||||||
|
|
||||||
|
if not row.posting_date:
|
||||||
|
frappe.throw(mandatory_error_msg.format(
|
||||||
|
idx=row.idx,
|
||||||
|
field= _("Party"),
|
||||||
|
invoice_type=self.invoice_type
|
||||||
|
))
|
||||||
|
|
||||||
|
if not row.outstanding_amount:
|
||||||
|
frappe.throw(mandatory_error_msg.format(
|
||||||
|
idx=row.idx,
|
||||||
|
field= _("Outstanding Amount"),
|
||||||
|
invoice_type=self.invoice_type
|
||||||
|
))
|
||||||
|
|
||||||
|
args = self.get_invoice_dict(row=row)
|
||||||
|
if not args:
|
||||||
|
continue
|
||||||
|
|
||||||
|
doc = frappe.get_doc(args).insert()
|
||||||
|
doc.submit()
|
||||||
|
names.append(doc.name)
|
||||||
|
|
||||||
|
if(len(self.invoices) > 5):
|
||||||
|
frappe.publish_realtime("progress",
|
||||||
|
dict(progress=[row.idx, len(self.invoices)], title=_('Creating {0}').format(doc.doctype)),
|
||||||
|
user=frappe.session.user)
|
||||||
|
|
||||||
|
return names
|
||||||
|
|
||||||
|
def get_invoice_dict(self, row=None):
|
||||||
|
def get_item_dict():
|
||||||
|
default_uom = frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos")
|
||||||
|
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
|
if not cost_center:
|
||||||
|
frappe.throw(_("Please set the Default Cost Center in {0} company").format(frappe.bold(self.company)))
|
||||||
|
rate = flt(row.outstanding_amount) / row.qty
|
||||||
|
|
||||||
|
return frappe._dict({
|
||||||
|
"uom": default_uom,
|
||||||
|
"rate": rate or 0.0,
|
||||||
|
"qty": row.qty,
|
||||||
|
"conversion_factor": 1.0,
|
||||||
|
"item_name": row.item_name or "Opening Invoice Item",
|
||||||
|
"description": row.item_name or "Opening Invoice Item",
|
||||||
|
income_expense_account_field: row.temporary_opening_account,
|
||||||
|
"cost_center": cost_center
|
||||||
|
})
|
||||||
|
|
||||||
|
if not row:
|
||||||
|
return None
|
||||||
|
|
||||||
|
party_type = "Customer"
|
||||||
|
income_expense_account_field = "income_account"
|
||||||
|
if self.invoice_type == "Purchase":
|
||||||
|
party_type = "Supplier"
|
||||||
|
income_expense_account_field = "expense_account"
|
||||||
|
|
||||||
|
item = get_item_dict()
|
||||||
|
return frappe._dict({
|
||||||
|
"items": [item],
|
||||||
|
"is_opening": "Yes",
|
||||||
|
"set_posting_time": 1,
|
||||||
|
"company": self.company,
|
||||||
|
"due_date": row.due_date,
|
||||||
|
"posting_date": row.posting_date,
|
||||||
|
frappe.scrub(party_type): row.party,
|
||||||
|
"doctype": "Sales Invoice" if self.invoice_type == "Sales" \
|
||||||
|
else "Purchase Invoice",
|
||||||
|
"currency": frappe.db.get_value("Company", self.company, "default_currency")
|
||||||
|
})
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_temporary_opening_account(company=None):
|
||||||
|
if not company:
|
||||||
|
return
|
||||||
|
|
||||||
|
accounts = frappe.get_all("Account", filters={
|
||||||
|
'company': company,
|
||||||
|
'account_type': 'Temporary'
|
||||||
|
})
|
||||||
|
if not accounts:
|
||||||
|
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
|
||||||
|
|
||||||
|
return accounts[0].name
|
@ -0,0 +1,32 @@
|
|||||||
|
<h5 style="margin-top: 0px;">{{ __("Opening Invoices Summary") }}</h5>
|
||||||
|
{% $.each(data, (company, summary) => { %}
|
||||||
|
<h6 style="margin: 15px 0px -10px 0px;"><a class="company-link"> {{ company }}</a></h6>
|
||||||
|
|
||||||
|
<table class="table table-bordered small">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td style="width: 33%">{{ __("Invoice Type") }}</td>
|
||||||
|
<td style="width: 33%" class="text-right">{{ __("Opening Invoices") }}</td>
|
||||||
|
<td style="width: 33%" class="text-right">{{ __("Total Outstanding") }}</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% $.each(["Sales Invoice", "Purchase Invoice"], (idx, doctype) => { %}
|
||||||
|
{% if summary[doctype] %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a class="invoice-link" data-type="{{ doctype }}" data-company="{{ company }}">
|
||||||
|
{{ __(doctype) }}</a>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{ summary[doctype].total_invoices }}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
{{ format_currency(summary[doctype].outstanding_amount, summary.currency, 2) }}
|
||||||
|
</td>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% }); %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% }); %}
|
@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// rename this file from _test_[name] to test_[name] to activate
|
||||||
|
// and remove above this line
|
||||||
|
|
||||||
|
QUnit.test("test: Opening Invoice Creation Tool", function (assert) {
|
||||||
|
let done = assert.async();
|
||||||
|
|
||||||
|
// number of asserts
|
||||||
|
assert.expect(1);
|
||||||
|
|
||||||
|
frappe.run_serially([
|
||||||
|
// insert a new Opening Invoice Creation Tool
|
||||||
|
() => frappe.tests.make('Opening Invoice Creation Tool', [
|
||||||
|
// values to be set
|
||||||
|
{key: 'value'}
|
||||||
|
]),
|
||||||
|
() => {
|
||||||
|
assert.equal(cur_frm.doc.key, 'value');
|
||||||
|
},
|
||||||
|
() => done()
|
||||||
|
]);
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,79 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
|
# See license.txt
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
test_dependencies = ["Customer", "Supplier"]
|
||||||
|
from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool import get_temporary_opening_account
|
||||||
|
|
||||||
|
class TestOpeningInvoiceCreationTool(unittest.TestCase):
|
||||||
|
def make_invoices(self, invoice_type="Sales"):
|
||||||
|
doc = frappe.get_single("Opening Invoice Creation Tool")
|
||||||
|
args = get_opening_invoice_creation_dict(invoice_type=invoice_type)
|
||||||
|
doc.update(args)
|
||||||
|
return doc.make_invoices()
|
||||||
|
|
||||||
|
def test_opening_sales_invoice_creation(self):
|
||||||
|
invoices = self.make_invoices()
|
||||||
|
|
||||||
|
self.assertEqual(len(invoices), 2)
|
||||||
|
expected_value = {
|
||||||
|
"keys": ["customer", "outstanding_amount", "status"],
|
||||||
|
0: ["_Test Customer", 300, "Overdue"],
|
||||||
|
1: ["_Test Customer 1", 250, "Overdue"],
|
||||||
|
}
|
||||||
|
self.check_expected_values(invoices, expected_value)
|
||||||
|
|
||||||
|
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
|
||||||
|
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
|
||||||
|
|
||||||
|
for invoice_idx, invoice in enumerate(invoices or []):
|
||||||
|
si = frappe.get_doc(doctype, invoice)
|
||||||
|
for field_idx, field in enumerate(expected_value["keys"]):
|
||||||
|
self.assertEqual(si.get(field, ""), expected_value[invoice_idx][field_idx])
|
||||||
|
|
||||||
|
def test_opening_purchase_invoice_creation(self):
|
||||||
|
invoices = self.make_invoices(invoice_type="Purchase")
|
||||||
|
|
||||||
|
self.assertEqual(len(invoices), 2)
|
||||||
|
expected_value = {
|
||||||
|
"keys": ["supplier", "outstanding_amount", "status"],
|
||||||
|
0: ["_Test Supplier", 300, "Overdue"],
|
||||||
|
1: ["_Test Supplier 1", 250, "Overdue"],
|
||||||
|
}
|
||||||
|
self.check_expected_values(invoices, expected_value, invoice_type="Purchase", )
|
||||||
|
|
||||||
|
def get_opening_invoice_creation_dict(**args):
|
||||||
|
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
|
||||||
|
company = args.get("company", "_Test Company")
|
||||||
|
|
||||||
|
invoice_dict = frappe._dict({
|
||||||
|
"company": company,
|
||||||
|
"invoice_type": args.get("invoice_type", "Sales"),
|
||||||
|
"invoices": [
|
||||||
|
{
|
||||||
|
"qty": 1.0,
|
||||||
|
"outstanding_amount": 300,
|
||||||
|
"party": "_Test {0}".format(party),
|
||||||
|
"item_name": "Opening Item",
|
||||||
|
"due_date": "2016-09-10",
|
||||||
|
"posting_date": "2016-09-05",
|
||||||
|
"temporary_opening_account": get_temporary_opening_account(company)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"qty": 2.0,
|
||||||
|
"outstanding_amount": 250,
|
||||||
|
"party": "_Test {0} 1".format(party),
|
||||||
|
"item_name": "Opening Item",
|
||||||
|
"due_date": "2016-09-10",
|
||||||
|
"posting_date": "2016-09-05",
|
||||||
|
"temporary_opening_account": get_temporary_opening_account(company)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
invoice_dict.update(args)
|
||||||
|
return invoice_dict
|
@ -0,0 +1,318 @@
|
|||||||
|
{
|
||||||
|
"allow_copy": 0,
|
||||||
|
"allow_guest_to_view": 0,
|
||||||
|
"allow_import": 0,
|
||||||
|
"allow_rename": 0,
|
||||||
|
"beta": 0,
|
||||||
|
"creation": "2017-08-29 04:26:36.159247",
|
||||||
|
"custom": 0,
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "DocType",
|
||||||
|
"document_type": "",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "party_type",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 1,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Party Type",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "DocType",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 1,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "party",
|
||||||
|
"fieldtype": "Dynamic Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Party",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "party_type",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "temporary_opening_account",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Temporary Opening Account",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "Account",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "column_break_3",
|
||||||
|
"fieldtype": "Column Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"default": "Today",
|
||||||
|
"fieldname": "posting_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Posting Date",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"default": "Today",
|
||||||
|
"fieldname": "due_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Due Date",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 1,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"fieldname": "section_break_5",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 0,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"default": "Opening Invoice Item",
|
||||||
|
"fieldname": "item_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Item Name",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"options": "",
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_bulk_edit": 0,
|
||||||
|
"allow_on_submit": 0,
|
||||||
|
"bold": 0,
|
||||||
|
"collapsible": 0,
|
||||||
|
"columns": 0,
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "outstanding_amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"hidden": 0,
|
||||||
|
"ignore_user_permissions": 0,
|
||||||
|
"ignore_xss_filter": 0,
|
||||||
|
"in_filter": 0,
|
||||||
|
"in_global_search": 0,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"in_standard_filter": 0,
|
||||||
|
"label": "Outstanding Amount",
|
||||||
|
"length": 0,
|
||||||
|
"no_copy": 0,
|
||||||
|
"permlevel": 0,
|
||||||
|
"precision": "",
|
||||||
|
"print_hide": 0,
|
||||||
|
"print_hide_if_no_value": 0,
|
||||||
|
"read_only": 0,
|
||||||
|
"remember_last_selected_value": 0,
|
||||||
|
"report_hide": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"search_index": 0,
|
||||||
|
"set_only_once": 0,
|
||||||
|
"unique": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_web_view": 0,
|
||||||
|
"hide_heading": 0,
|
||||||
|
"hide_toolbar": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"image_view": 0,
|
||||||
|
"in_create": 0,
|
||||||
|
"is_submittable": 0,
|
||||||
|
"issingle": 0,
|
||||||
|
"istable": 1,
|
||||||
|
"max_attachments": 0,
|
||||||
|
"modified": "2017-11-15 14:19:00.433148",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Opening Invoice Creation Tool Item",
|
||||||
|
"name_case": "",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"read_only": 0,
|
||||||
|
"read_only_onload": 0,
|
||||||
|
"show_name_in_global_search": 0,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1,
|
||||||
|
"track_seen": 0
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class OpeningInvoiceCreationToolItem(Document):
|
||||||
|
pass
|
@ -369,7 +369,7 @@
|
|||||||
"in_filter": 0,
|
"in_filter": 0,
|
||||||
"in_global_search": 0,
|
"in_global_search": 0,
|
||||||
"in_list_view": 0,
|
"in_list_view": 0,
|
||||||
"in_standard_filter": 0,
|
"in_standard_filter": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
"length": 0,
|
||||||
"no_copy": 0,
|
"no_copy": 0,
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.utils import flt, getdate, cstr
|
from frappe.utils import getdate, cstr, flt
|
||||||
from frappe import _
|
from frappe import _, _dict
|
||||||
from erpnext.accounts.utils import get_account_currency
|
from erpnext.accounts.utils import get_account_currency
|
||||||
|
|
||||||
def execute(filters=None):
|
def execute(filters=None):
|
||||||
@ -163,129 +163,89 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
|
|||||||
data = []
|
data = []
|
||||||
gle_map = initialize_gle_map(gl_entries)
|
gle_map = initialize_gle_map(gl_entries)
|
||||||
|
|
||||||
opening, total_debit, total_credit, opening_in_account_currency, total_debit_in_account_currency, \
|
totals, entries = get_accountwise_gle(filters, gl_entries, gle_map)
|
||||||
total_credit_in_account_currency, gle_map = get_accountwise_gle(filters, gl_entries, gle_map)
|
|
||||||
|
|
||||||
# Opening for filtered account
|
# Opening for filtered account
|
||||||
if filters.get("account") or filters.get("party"):
|
data.append(totals.opening)
|
||||||
data += [get_balance_row(_("Opening"), opening, opening_in_account_currency), {}]
|
|
||||||
|
|
||||||
if filters.get("group_by_account"):
|
if filters.get("group_by_account"):
|
||||||
for acc, acc_dict in gle_map.items():
|
for acc, acc_dict in gle_map.items():
|
||||||
if acc_dict.entries:
|
if acc_dict.entries:
|
||||||
# Opening for individual ledger, if grouped by account
|
# opening
|
||||||
data.append(get_balance_row(_("Opening"), acc_dict.opening,
|
data.append({})
|
||||||
acc_dict.opening_in_account_currency))
|
data.append(acc_dict.totals.opening)
|
||||||
|
|
||||||
data += acc_dict.entries
|
data += acc_dict.entries
|
||||||
|
|
||||||
# Totals and closing for individual ledger, if grouped by account
|
# totals
|
||||||
account_closing = acc_dict.opening + acc_dict.total_debit - acc_dict.total_credit
|
data.append(acc_dict.totals.total)
|
||||||
account_closing_in_account_currency = acc_dict.opening_in_account_currency \
|
|
||||||
+ acc_dict.total_debit_in_account_currency - acc_dict.total_credit_in_account_currency
|
|
||||||
|
|
||||||
data += [{"account": "'" + _("Totals") + "'", "debit": acc_dict.total_debit,
|
# closing
|
||||||
"credit": acc_dict.total_credit},
|
data.append(acc_dict.totals.closing)
|
||||||
get_balance_row(_("Closing (Opening + Totals)"),
|
data.append({})
|
||||||
account_closing, account_closing_in_account_currency), {}]
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for gl in gl_entries:
|
data += entries
|
||||||
if gl.posting_date >= getdate(filters.from_date) and gl.posting_date <= getdate(filters.to_date) \
|
|
||||||
and gl.is_opening == "No":
|
|
||||||
data.append(gl)
|
|
||||||
|
|
||||||
|
# totals
|
||||||
|
data.append(totals.total)
|
||||||
|
|
||||||
# Total debit and credit between from and to date
|
# closing
|
||||||
if total_debit or total_credit:
|
data.append(totals.closing)
|
||||||
data.append({
|
|
||||||
"account": "'" + _("Totals") + "'",
|
|
||||||
"debit": total_debit,
|
|
||||||
"credit": total_credit,
|
|
||||||
"debit_in_account_currency": total_debit_in_account_currency,
|
|
||||||
"credit_in_account_currency": total_credit_in_account_currency
|
|
||||||
})
|
|
||||||
|
|
||||||
# Closing for filtered account
|
|
||||||
if filters.get("account") or filters.get("party"):
|
|
||||||
closing = opening + total_debit - total_credit
|
|
||||||
closing_in_account_currency = opening_in_account_currency + \
|
|
||||||
total_debit_in_account_currency - total_credit_in_account_currency
|
|
||||||
|
|
||||||
data.append(get_balance_row(_("Closing (Opening + Totals)"),
|
|
||||||
closing, closing_in_account_currency))
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get_totals_dict():
|
||||||
|
def _get_debit_credit_dict(label):
|
||||||
|
return _dict(
|
||||||
|
account = "'{0}'".format(label),
|
||||||
|
debit = 0.0,
|
||||||
|
credit = 0.0,
|
||||||
|
debit_in_account_currency = 0.0,
|
||||||
|
credit_in_account_currency = 0.0
|
||||||
|
)
|
||||||
|
return _dict(
|
||||||
|
opening = _get_debit_credit_dict(_('Opening')),
|
||||||
|
total = _get_debit_credit_dict(_('Total')),
|
||||||
|
closing = _get_debit_credit_dict(_('Closing (Opening + Total)'))
|
||||||
|
)
|
||||||
|
|
||||||
def initialize_gle_map(gl_entries):
|
def initialize_gle_map(gl_entries):
|
||||||
gle_map = frappe._dict()
|
gle_map = frappe._dict()
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
gle_map.setdefault(gle.account, frappe._dict({
|
gle_map.setdefault(gle.account, _dict(totals = get_totals_dict(), entries = []))
|
||||||
"opening": 0,
|
|
||||||
"opening_in_account_currency": 0,
|
|
||||||
"entries": [],
|
|
||||||
"total_debit": 0,
|
|
||||||
"total_debit_in_account_currency": 0,
|
|
||||||
"total_credit": 0,
|
|
||||||
"total_credit_in_account_currency": 0,
|
|
||||||
"closing": 0,
|
|
||||||
"closing_in_account_currency": 0
|
|
||||||
}))
|
|
||||||
return gle_map
|
return gle_map
|
||||||
|
|
||||||
def get_accountwise_gle(filters, gl_entries, gle_map):
|
def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||||
opening, total_debit, total_credit = 0, 0, 0
|
totals = get_totals_dict()
|
||||||
opening_in_account_currency, total_debit_in_account_currency, total_credit_in_account_currency = 0, 0, 0
|
entries = []
|
||||||
|
|
||||||
|
def update_value_in_dict(data, key, gle):
|
||||||
|
data[key].debit += flt(gle.debit)
|
||||||
|
data[key].credit += flt(gle.credit)
|
||||||
|
|
||||||
|
data[key].debit_in_account_currency += flt(gle.debit_in_account_currency)
|
||||||
|
data[key].credit_in_account_currency += flt(gle.credit_in_account_currency)
|
||||||
|
|
||||||
|
|
||||||
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
||||||
for gle in gl_entries:
|
for gle in gl_entries:
|
||||||
amount = flt(gle.debit, 3) - flt(gle.credit, 3)
|
if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes":
|
||||||
amount_in_account_currency = flt(gle.debit_in_account_currency, 3) - flt(gle.credit_in_account_currency, 3)
|
update_value_in_dict(gle_map[gle.account].totals, 'opening', gle)
|
||||||
|
update_value_in_dict(totals, 'opening', gle)
|
||||||
if (filters.get("account") or filters.get("party") or filters.get("group_by_account")) \
|
|
||||||
and (gle.posting_date < from_date or cstr(gle.is_opening) == "Yes"):
|
|
||||||
|
|
||||||
gle_map[gle.account].opening += amount
|
|
||||||
if filters.get("show_in_account_currency"):
|
|
||||||
gle_map[gle.account].opening_in_account_currency += amount_in_account_currency
|
|
||||||
|
|
||||||
if filters.get("account") or filters.get("party"):
|
|
||||||
opening += amount
|
|
||||||
if filters.get("show_in_account_currency"):
|
|
||||||
opening_in_account_currency += amount_in_account_currency
|
|
||||||
|
|
||||||
elif gle.posting_date <= to_date:
|
elif gle.posting_date <= to_date:
|
||||||
gle_map[gle.account].entries.append(gle)
|
update_value_in_dict(gle_map[gle.account].totals, 'total', gle)
|
||||||
gle_map[gle.account].total_debit += flt(gle.debit, 3)
|
update_value_in_dict(totals, 'total', gle)
|
||||||
gle_map[gle.account].total_credit += flt(gle.credit, 3)
|
if filters.get("group_by_account"):
|
||||||
|
gle_map[gle.account].entries.append(gle)
|
||||||
|
else:
|
||||||
|
entries.append(gle)
|
||||||
|
|
||||||
total_debit += flt(gle.debit, 3)
|
update_value_in_dict(gle_map[gle.account].totals, 'closing', gle)
|
||||||
total_credit += flt(gle.credit, 3)
|
update_value_in_dict(totals, 'closing', gle)
|
||||||
|
|
||||||
if filters.get("show_in_account_currency"):
|
return totals, entries
|
||||||
gle_map[gle.account].total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
|
|
||||||
gle_map[gle.account].total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
|
|
||||||
|
|
||||||
total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
|
|
||||||
total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
|
|
||||||
|
|
||||||
return opening, total_debit, total_credit, opening_in_account_currency, \
|
|
||||||
total_debit_in_account_currency, total_credit_in_account_currency, gle_map
|
|
||||||
|
|
||||||
def get_balance_row(label, balance, balance_in_account_currency=None):
|
|
||||||
balance_row = {
|
|
||||||
"account": "'" + label + "'",
|
|
||||||
"debit": balance if balance > 0 else 0,
|
|
||||||
"credit": -1*balance if balance < 0 else 0
|
|
||||||
}
|
|
||||||
|
|
||||||
if balance_in_account_currency != None:
|
|
||||||
balance_row.update({
|
|
||||||
"debit_in_account_currency": balance_in_account_currency if balance_in_account_currency > 0 else 0,
|
|
||||||
"credit_in_account_currency": -1*balance_in_account_currency if balance_in_account_currency < 0 else 0
|
|
||||||
})
|
|
||||||
|
|
||||||
return balance_row
|
|
||||||
|
|
||||||
def get_result_as_list(data, filters):
|
def get_result_as_list(data, filters):
|
||||||
result = []
|
result = []
|
||||||
|
@ -15,6 +15,11 @@
|
|||||||
"supplier_name": "_Test Supplier 1",
|
"supplier_name": "_Test Supplier 1",
|
||||||
"supplier_type": "_Test Supplier Type"
|
"supplier_type": "_Test Supplier Type"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"doctype": "Supplier",
|
||||||
|
"supplier_name": "_Test Supplier 2",
|
||||||
|
"supplier_type": "_Test Supplier Type"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"doctype": "Supplier",
|
"doctype": "Supplier",
|
||||||
"supplier_name": "_Test Supplier USD",
|
"supplier_name": "_Test Supplier USD",
|
||||||
|
@ -284,6 +284,11 @@ def get_data():
|
|||||||
"name": "Cheque Print Template",
|
"name": "Cheque Print Template",
|
||||||
"description": _("Setup cheque dimensions for printing")
|
"description": _("Setup cheque dimensions for printing")
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "doctype",
|
||||||
|
"name": "Opening Invoice Creation Tool",
|
||||||
|
"description": _("Make Opening Sales and Purchase Invoices")
|
||||||
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -83,9 +83,6 @@ class AccountsController(TransactionBase):
|
|||||||
self.set(fieldname, today())
|
self.set(fieldname, today())
|
||||||
break
|
break
|
||||||
|
|
||||||
# set taxes table if missing from `taxes_and_charges`
|
|
||||||
self.set_taxes()
|
|
||||||
|
|
||||||
def calculate_taxes_and_totals(self):
|
def calculate_taxes_and_totals(self):
|
||||||
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
||||||
calculate_taxes_and_totals(self)
|
calculate_taxes_and_totals(self)
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 137 KiB |
@ -1,4 +1,4 @@
|
|||||||
#Updating Opening Balance in Accounts
|
# Updating Opening Balance in Accounts
|
||||||
|
|
||||||
If you are a new company you can start using ERPNext accounting module by going to chart of accounts. However, if you are migrating from a legacy accounting system like Tally or a Fox Pro based software
|
If you are a new company you can start using ERPNext accounting module by going to chart of accounts. However, if you are migrating from a legacy accounting system like Tally or a Fox Pro based software
|
||||||
|
|
||||||
@ -13,28 +13,28 @@ If you were using another accounting software before, firstly you should close f
|
|||||||
> Opening entry is only for Balance Sheet accounts and not for the Accounts in the Profit and Loss statement.
|
> Opening entry is only for Balance Sheet accounts and not for the Accounts in the Profit and Loss statement.
|
||||||
|
|
||||||
* For all assets (excluding Accounts Receivables): This entry will contain all your assets except the amounts you are expecting from your Customers against outstanding Sales Invoices. You will have to update your receivables by making an individual entry for each Invoice (this is because, the system will help you track the invoices which are yet to be paid). You can credit the sum of all these debits against the **Temporary Opening** account.
|
* For all assets (excluding Accounts Receivables): This entry will contain all your assets except the amounts you are expecting from your Customers against outstanding Sales Invoices. You will have to update your receivables by making an individual entry for each Invoice (this is because, the system will help you track the invoices which are yet to be paid). You can credit the sum of all these debits against the **Temporary Opening** account.
|
||||||
|
|
||||||
* For all liabilities: Similarly you need to pass a Journal Entry for your Opening Liabilities (except for the bills you have to pay) against **Temporary Opening** account.
|
* For all liabilities: Similarly you need to pass a Journal Entry for your Opening Liabilities (except for the bills you have to pay) against **Temporary Opening** account.
|
||||||
|
|
||||||
###Opening Entry
|
###Opening Entry
|
||||||
|
|
||||||
####Step 1: New Journal Entry
|
#### Step 1: New Journal Entry
|
||||||
|
|
||||||
To open new Journal Entry, go to:
|
To open new Journal Entry, go to:
|
||||||
|
|
||||||
`Explore > Accounts > Journal Entry`
|
`Explore > Accounts > Journal Entry`
|
||||||
|
|
||||||
####Step 2: Entry Type
|
#### Step 2: Entry Type
|
||||||
|
|
||||||
If Entry Type is selected as Opening Entry, all the Balance Sheet Accounts will be auto-fetched in the Journal Entry.
|
If Entry Type is selected as Opening Entry, all the Balance Sheet Accounts will be auto-fetched in the Journal Entry.
|
||||||
|
|
||||||
<img class="screenshot" alt="Opening Account" src="/docs/assets/img/accounts/opening-account-1.png">
|
<img class="screenshot" alt="Opening Account" src="/docs/assets/img/accounts/opening-account-1.png">
|
||||||
|
|
||||||
####Step 3: Posting Date
|
#### Step 3: Posting Date
|
||||||
|
|
||||||
Select Posting Date on which Accounts Opening Balance will be updated.
|
Select Posting Date on which Accounts Opening Balance will be updated.
|
||||||
|
|
||||||
####Step 4: Enter Debit/Credit Value
|
#### Step 4: Enter Debit/Credit Value
|
||||||
|
|
||||||
For each Account, enter opening value in the Debit or Credit column. As per the double entry valuation system, Total Debit value in a entry must be equal to Total Credit value.
|
For each Account, enter opening value in the Debit or Credit column. As per the double entry valuation system, Total Debit value in a entry must be equal to Total Credit value.
|
||||||
|
|
||||||
@ -86,9 +86,9 @@ To update stock opening balance, create [Stock Reconciliation entry](/docs/user/
|
|||||||
|
|
||||||
Opening balance for the fixed asset account should be updated via Journal Entry. Assets which are not fully depreciated should be added in the [Asset master](/docs/user/manual/en/accounts/managing-fixed-assets.html). For adding Assets in your possession, ensure to check **Is Existing Asset** field.
|
Opening balance for the fixed asset account should be updated via Journal Entry. Assets which are not fully depreciated should be added in the [Asset master](/docs/user/manual/en/accounts/managing-fixed-assets.html). For adding Assets in your possession, ensure to check **Is Existing Asset** field.
|
||||||
|
|
||||||
### Outstanding Invoices
|
### Outstanding Payables and Receivables
|
||||||
|
|
||||||
After opening Journal Entries are made, you will need to enter each Sales Invoice and Purchase Invoice that is yet to be paid.
|
After opening Journal Entries are made, you will need to enter the Sales Invoice and Purchase Invoice that is yet to be paid.
|
||||||
|
|
||||||
Since you have already booked the income or expense on these invoices in the previous period, select **Temporary Opening** in the “Income” and “Expense” accounts.
|
Since you have already booked the income or expense on these invoices in the previous period, select **Temporary Opening** in the “Income” and “Expense” accounts.
|
||||||
|
|
||||||
@ -96,4 +96,12 @@ Since you have already booked the income or expense on these invoices in the pre
|
|||||||
|
|
||||||
If you don’t care what items are in that invoice, just make a dummy item entry in the Invoice. Item code in the Invoice is not necessary, so it should not be such a problem.
|
If you don’t care what items are in that invoice, just make a dummy item entry in the Invoice. Item code in the Invoice is not necessary, so it should not be such a problem.
|
||||||
|
|
||||||
|
You can also do this quickly using the **Opening Invoice Creation Tool**
|
||||||
|
|
||||||
|
To use this tool, just type "Opening Invoice" in the search bar and select the **Opening Invoice Creation Tool**
|
||||||
|
|
||||||
|
Here, select the company and type of invoice (sales or purchase) and add a line item for each invoice you want to create.
|
||||||
|
|
||||||
|
<img class="screenshot" alt="Opening Invoice Creation Tool" src="/docs/assets/img/accounts/opening-invoice-creation-tool.png">
|
||||||
|
|
||||||
{next}
|
{next}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user