Merge branch 'develop' of github.com:frappe/erpnext into refactor-website-routing
This commit is contained in:
commit
b14a935c5d
@ -151,6 +151,7 @@
|
||||
"context": true,
|
||||
"before": true,
|
||||
"beforeEach": true,
|
||||
"onScan": true
|
||||
"onScan": true,
|
||||
"extend_cscript": true
|
||||
}
|
||||
}
|
||||
|
12
.git-blame-ignore-revs
Normal file
12
.git-blame-ignore-revs
Normal file
@ -0,0 +1,12 @@
|
||||
# Since version 2.23 (released in August 2019), git-blame has a feature
|
||||
# to ignore or bypass certain commits.
|
||||
#
|
||||
# This file contains a list of commits that are not likely what you
|
||||
# are looking for in a blame, such as mass reformatting or renaming.
|
||||
# You can set this file as a default ignore file for blame by running
|
||||
# the following command.
|
||||
#
|
||||
# $ git config blame.ignoreRevsFile .git-blame-ignore-revs
|
||||
|
||||
# Replace use of Class.extend with native JS class
|
||||
1fe891b287a1b3f225d29ee3d07e7b1824aba9e7
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@ latest_updates.json
|
||||
.wnf-lang-status
|
||||
*.egg-info
|
||||
dist/
|
||||
erpnext/public/dist
|
||||
erpnext/docs/current
|
||||
*.swp
|
||||
*.swo
|
||||
|
@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '13.2.1'
|
||||
__version__ = '13.3.1'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
@ -7,26 +7,30 @@
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"auto_accounting_for_stock",
|
||||
"acc_frozen_upto",
|
||||
"frozen_accounts_modifier",
|
||||
"determine_address_tax_category_from",
|
||||
"accounts_transactions_settings_section",
|
||||
"over_billing_allowance",
|
||||
"role_allowed_to_over_bill",
|
||||
"column_break_4",
|
||||
"credit_controller",
|
||||
"check_supplier_invoice_uniqueness",
|
||||
"make_payment_via_journal_entry",
|
||||
"column_break_11",
|
||||
"check_supplier_invoice_uniqueness",
|
||||
"unlink_payment_on_cancellation_of_invoice",
|
||||
"unlink_advance_payment_on_cancelation_of_order",
|
||||
"book_asset_depreciation_entry_automatically",
|
||||
"add_taxes_from_item_tax_template",
|
||||
"automatically_fetch_payment_terms",
|
||||
"delete_linked_ledger_entries",
|
||||
"book_asset_depreciation_entry_automatically",
|
||||
"unlink_advance_payment_on_cancelation_of_order",
|
||||
"tax_settings_section",
|
||||
"determine_address_tax_category_from",
|
||||
"column_break_19",
|
||||
"add_taxes_from_item_tax_template",
|
||||
"period_closing_settings_section",
|
||||
"acc_frozen_upto",
|
||||
"frozen_accounts_modifier",
|
||||
"column_break_4",
|
||||
"credit_controller",
|
||||
"deferred_accounting_settings_section",
|
||||
"automatically_process_deferred_accounting_entry",
|
||||
"book_deferred_entries_based_on",
|
||||
"column_break_18",
|
||||
"automatically_process_deferred_accounting_entry",
|
||||
"book_deferred_entries_via_journal_entry",
|
||||
"submit_journal_entries",
|
||||
"print_settings",
|
||||
@ -40,15 +44,6 @@
|
||||
"use_custom_cash_flow"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"default": "1",
|
||||
"description": "If enabled, the system will post accounting entries for inventory automatically",
|
||||
"fieldname": "auto_accounting_for_stock",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Make Accounting Entry For Every Stock Movement"
|
||||
},
|
||||
{
|
||||
"description": "Accounting entries are frozen up to this date. Nobody can create or modify entries except users with the role specified below",
|
||||
"fieldname": "acc_frozen_upto",
|
||||
@ -94,6 +89,7 @@
|
||||
"default": "0",
|
||||
"fieldname": "make_payment_via_journal_entry",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Make Payment via Journal Entry"
|
||||
},
|
||||
{
|
||||
@ -234,6 +230,29 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Role Allowed to Over Bill ",
|
||||
"options": "Role"
|
||||
},
|
||||
{
|
||||
"fieldname": "period_closing_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Period Closing Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "accounts_transactions_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Transactions Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "tax_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Tax Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_19",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
@ -241,7 +260,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-11 18:52:05.601996",
|
||||
"modified": "2021-04-30 15:25:10.381008",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
|
@ -15,7 +15,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
frappe.require("assets/js/bank-reconciliation-tool.min.js", () =>
|
||||
frappe.require("bank-reconciliation-tool.bundle.js", () =>
|
||||
frm.trigger("make_reconciliation_tool")
|
||||
);
|
||||
frm.upload_statement_button = frm.page.set_secondary_action(
|
||||
|
@ -239,6 +239,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
"withdrawal",
|
||||
"description",
|
||||
"reference_number",
|
||||
"bank_account"
|
||||
],
|
||||
},
|
||||
});
|
||||
@ -319,7 +320,7 @@ frappe.ui.form.on("Bank Statement Import", {
|
||||
return;
|
||||
}
|
||||
|
||||
frappe.require("/assets/js/data_import_tools.min.js", () => {
|
||||
frappe.require("data_import_tools.bundle.js", () => {
|
||||
frm.import_preview = new frappe.data_import.ImportPreview({
|
||||
wrapper: frm.get_field("import_preview").$wrapper,
|
||||
doctype: frm.doc.reference_doctype,
|
||||
|
@ -146,7 +146,7 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.__islocal && !doc.import_file\n",
|
||||
"description": "Must be a publicly accessible Google Sheets URL",
|
||||
"description": "Must be a publicly accessible Google Sheets URL and adding Bank Account column is necessary for importing via Google Sheets",
|
||||
"fieldname": "google_sheets_url",
|
||||
"fieldtype": "Data",
|
||||
"label": "Import from Google Sheets"
|
||||
@ -202,7 +202,7 @@
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
"links": [],
|
||||
"modified": "2021-02-10 19:29:59.027325",
|
||||
"modified": "2021-05-12 14:17:37.777246",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Bank Statement Import",
|
||||
@ -224,4 +224,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,13 @@ class BankStatementImport(DataImport):
|
||||
|
||||
def start_import(self):
|
||||
|
||||
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
|
||||
self.import_file, self.google_sheets_url
|
||||
)
|
||||
|
||||
if 'Bank Account' not in json.dumps(preview):
|
||||
frappe.throw(_("Please add the Bank Account column"))
|
||||
|
||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
||||
from frappe.utils.scheduler import is_scheduler_inactive
|
||||
|
||||
@ -67,6 +74,7 @@ class BankStatementImport(DataImport):
|
||||
data_import=self.name,
|
||||
bank_account=self.bank_account,
|
||||
import_file_path=self.import_file,
|
||||
google_sheets_url=self.google_sheets_url,
|
||||
bank=self.bank,
|
||||
template_options=self.template_options,
|
||||
now=frappe.conf.developer_mode or frappe.flags.in_test,
|
||||
@ -90,18 +98,20 @@ def download_errored_template(data_import_name):
|
||||
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
|
||||
data_import.export_errored_rows()
|
||||
|
||||
def start_import(data_import, bank_account, import_file_path, bank, template_options):
|
||||
def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
|
||||
"""This method runs in background job"""
|
||||
|
||||
update_mapping_db(bank, template_options)
|
||||
|
||||
data_import = frappe.get_doc("Bank Statement Import", data_import)
|
||||
file = import_file_path if import_file_path else google_sheets_url
|
||||
|
||||
import_file = ImportFile("Bank Transaction", file = import_file_path, import_type="Insert New Records")
|
||||
import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records")
|
||||
data = import_file.raw_data
|
||||
|
||||
add_bank_account(data, bank_account)
|
||||
write_files(import_file, data)
|
||||
if import_file_path:
|
||||
add_bank_account(data, bank_account)
|
||||
write_files(import_file, data)
|
||||
|
||||
try:
|
||||
i = Importer(data_import.reference_doctype, data_import=data_import)
|
||||
|
@ -194,19 +194,19 @@ var update_jv_details = function(doc, r) {
|
||||
refresh_field("accounts");
|
||||
}
|
||||
|
||||
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
onload: function() {
|
||||
erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Controller {
|
||||
onload() {
|
||||
this.load_defaults();
|
||||
this.setup_queries();
|
||||
this.setup_balance_formatter();
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
|
||||
},
|
||||
}
|
||||
|
||||
onload_post_render: function() {
|
||||
onload_post_render() {
|
||||
cur_frm.get_field("accounts").grid.set_multiple_add("account");
|
||||
},
|
||||
}
|
||||
|
||||
load_defaults: function() {
|
||||
load_defaults() {
|
||||
//this.frm.show_print_first = true;
|
||||
if(this.frm.doc.__islocal && this.frm.doc.company) {
|
||||
frappe.model.set_default_values(this.frm.doc);
|
||||
@ -216,9 +216,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
var posting_date = this.frm.doc.posting_date;
|
||||
if(!this.frm.doc.amended_from) this.frm.set_value('posting_date', posting_date || frappe.datetime.get_today());
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
setup_queries: function() {
|
||||
setup_queries() {
|
||||
var me = this;
|
||||
|
||||
me.frm.set_query("account", "accounts", function(doc, cdt, cdn) {
|
||||
@ -324,9 +324,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
});
|
||||
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
setup_balance_formatter: function() {
|
||||
setup_balance_formatter() {
|
||||
const formatter = function(value, df, options, doc) {
|
||||
var currency = frappe.meta.get_field_currency(df, doc);
|
||||
var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
|
||||
@ -337,9 +337,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
};
|
||||
this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter);
|
||||
this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter);
|
||||
},
|
||||
}
|
||||
|
||||
reference_name: function(doc, cdt, cdn) {
|
||||
reference_name(doc, cdt, cdn) {
|
||||
var d = frappe.get_doc(cdt, cdn);
|
||||
|
||||
if(d.reference_name) {
|
||||
@ -351,9 +351,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
this.get_outstanding('Journal Entry', d.reference_name, doc.company, d);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
get_outstanding: function(doctype, docname, company, child, due_date) {
|
||||
get_outstanding(doctype, docname, company, child, due_date) {
|
||||
var me = this;
|
||||
var args = {
|
||||
"doctype": doctype,
|
||||
@ -375,9 +375,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
accounts_add: function(doc, cdt, cdn) {
|
||||
accounts_add(doc, cdt, cdn) {
|
||||
var row = frappe.get_doc(cdt, cdn);
|
||||
$.each(doc.accounts, function(i, d) {
|
||||
if(d.account && d.party && d.party_type) {
|
||||
@ -400,9 +400,9 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
|
||||
cur_frm.cscript.update_totals(doc);
|
||||
|
||||
erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'accounts');
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
cur_frm.script_manager.make(erpnext.accounts.JournalEntry);
|
||||
|
||||
|
@ -1,87 +1,39 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2014-08-29 16:02:39.740505",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"actions": [],
|
||||
"creation": "2014-08-29 16:02:39.740505",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"field_order": [
|
||||
"company",
|
||||
"account"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Company",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"label": "Account",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Account",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Account",
|
||||
"options": "Account"
|
||||
}
|
||||
],
|
||||
"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": "2016-07-11 03:28:03.348246",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Party Account",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_seen": 0
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-04-07 18:13:08.833822",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Party Account",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
@ -34,8 +34,8 @@ frappe.ui.form.on("Payment Reconciliation Payment", {
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.extend({
|
||||
onload: function() {
|
||||
erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationController extends frappe.ui.form.Controller {
|
||||
onload() {
|
||||
var me = this;
|
||||
|
||||
this.frm.set_query("party", function() {
|
||||
@ -84,18 +84,18 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
frappe.throw({message: __("Please Select Both Company and Party Type First"), title: title});
|
||||
}
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
refresh: function() {
|
||||
refresh() {
|
||||
this.frm.disable_save();
|
||||
this.toggle_primary_action();
|
||||
},
|
||||
}
|
||||
|
||||
onload_post_render: function() {
|
||||
onload_post_render() {
|
||||
this.toggle_primary_action();
|
||||
},
|
||||
}
|
||||
|
||||
party: function() {
|
||||
party() {
|
||||
var me = this
|
||||
if (!me.frm.doc.receivable_payable_account && me.frm.doc.party_type && me.frm.doc.party) {
|
||||
return frappe.call({
|
||||
@ -112,9 +112,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
get_unreconciled_entries: function() {
|
||||
get_unreconciled_entries() {
|
||||
var me = this;
|
||||
return this.frm.call({
|
||||
doc: me.frm.doc,
|
||||
@ -125,9 +125,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
}
|
||||
});
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
reconcile: function() {
|
||||
reconcile() {
|
||||
var me = this;
|
||||
var show_dialog = me.frm.doc.payments.filter(d => d.difference_amount && !d.difference_account);
|
||||
|
||||
@ -209,9 +209,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
} else {
|
||||
this.reconcile_payment_entries();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
reconcile_payment_entries: function() {
|
||||
reconcile_payment_entries() {
|
||||
var me = this;
|
||||
|
||||
return this.frm.call({
|
||||
@ -222,9 +222,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
me.toggle_primary_action();
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
set_invoice_options: function() {
|
||||
set_invoice_options() {
|
||||
var me = this;
|
||||
var invoices = [];
|
||||
|
||||
@ -244,9 +244,9 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
}
|
||||
|
||||
refresh_field("payments");
|
||||
},
|
||||
}
|
||||
|
||||
toggle_primary_action: function() {
|
||||
toggle_primary_action() {
|
||||
if ((this.frm.doc.payments || []).length) {
|
||||
this.frm.fields_dict.reconcile.$input
|
||||
&& this.frm.fields_dict.reconcile.$input.addClass("btn-primary");
|
||||
@ -260,6 +260,6 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));
|
||||
|
@ -114,7 +114,7 @@ class PaymentReconciliation(Document):
|
||||
'party_type': self.party_type,
|
||||
'voucher_type': voucher_type,
|
||||
'account': self.receivable_payable_account
|
||||
}, as_dict=1, debug=1)
|
||||
}, as_dict=1)
|
||||
|
||||
def add_payment_entries(self, entries):
|
||||
self.set('payments', [])
|
||||
|
@ -101,15 +101,24 @@ frappe.ui.form.on('POS Closing Entry', {
|
||||
},
|
||||
|
||||
before_save: function(frm) {
|
||||
frm.set_value("grand_total", 0);
|
||||
frm.set_value("net_total", 0);
|
||||
frm.set_value("total_quantity", 0);
|
||||
frm.set_value("taxes", []);
|
||||
|
||||
for (let row of frm.doc.payment_reconciliation) {
|
||||
row.expected_amount = 0;
|
||||
}
|
||||
|
||||
for (let row of frm.doc.pos_transactions) {
|
||||
frappe.db.get_doc("POS Invoice", row.pos_invoice).then(doc => {
|
||||
cur_frm.doc.grand_total -= flt(doc.grand_total);
|
||||
cur_frm.doc.net_total -= flt(doc.net_total);
|
||||
cur_frm.doc.total_quantity -= flt(doc.total_qty);
|
||||
refresh_payments(doc, cur_frm, 1);
|
||||
refresh_taxes(doc, cur_frm, 1);
|
||||
refresh_fields(cur_frm);
|
||||
set_html_data(cur_frm);
|
||||
frm.doc.grand_total += flt(doc.grand_total);
|
||||
frm.doc.net_total += flt(doc.net_total);
|
||||
frm.doc.total_quantity += flt(doc.total_qty);
|
||||
refresh_payments(doc, frm);
|
||||
refresh_taxes(doc, frm);
|
||||
refresh_fields(frm);
|
||||
set_html_data(frm);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -118,7 +127,7 @@ frappe.ui.form.on('POS Closing Entry', {
|
||||
frappe.ui.form.on('POS Closing Entry Detail', {
|
||||
closing_amount: (frm, cdt, cdn) => {
|
||||
const row = locals[cdt][cdn];
|
||||
frappe.model.set_value(cdt, cdn, "difference", flt(row.expected_amount - row.closing_amount))
|
||||
frappe.model.set_value(cdt, cdn, "difference", flt(row.expected_amount - row.closing_amount));
|
||||
}
|
||||
})
|
||||
|
||||
@ -142,28 +151,28 @@ function add_to_pos_transaction(d, frm) {
|
||||
})
|
||||
}
|
||||
|
||||
function refresh_payments(d, frm, remove) {
|
||||
function refresh_payments(d, frm) {
|
||||
d.payments.forEach(p => {
|
||||
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
|
||||
if (payment) {
|
||||
if (!remove) payment.expected_amount += flt(p.amount);
|
||||
else payment.expected_amount -= flt(p.amount);
|
||||
payment.expected_amount += flt(p.amount);
|
||||
payment.difference = payment.closing_amount - payment.expected_amount;
|
||||
} else {
|
||||
frm.add_child("payment_reconciliation", {
|
||||
mode_of_payment: p.mode_of_payment,
|
||||
opening_amount: 0,
|
||||
expected_amount: p.amount
|
||||
expected_amount: p.amount,
|
||||
closing_amount: 0
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function refresh_taxes(d, frm, remove) {
|
||||
function refresh_taxes(d, frm) {
|
||||
d.taxes.forEach(t => {
|
||||
const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
|
||||
if (tax) {
|
||||
if (!remove) tax.amount += flt(t.tax_amount);
|
||||
else tax.amount -= flt(t.tax_amount);
|
||||
tax.amount += flt(t.tax_amount);
|
||||
} else {
|
||||
frm.add_child("taxes", {
|
||||
account_head: t.account_head,
|
||||
|
@ -4,18 +4,18 @@
|
||||
{% include 'erpnext/selling/sales_common.js' %};
|
||||
frappe.provide("erpnext.accounts");
|
||||
|
||||
erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend({
|
||||
erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnext.selling.SellingController {
|
||||
setup(doc) {
|
||||
this.setup_posting_date_time_check();
|
||||
this._super(doc);
|
||||
},
|
||||
super.setup(doc);
|
||||
}
|
||||
|
||||
company: function() {
|
||||
company() {
|
||||
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
|
||||
},
|
||||
}
|
||||
|
||||
onload(doc) {
|
||||
this._super();
|
||||
super.onload();
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log'];
|
||||
if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
|
||||
this.frm.script_manager.trigger("is_pos");
|
||||
@ -23,10 +23,10 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
}
|
||||
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
|
||||
},
|
||||
}
|
||||
|
||||
refresh(doc) {
|
||||
this._super();
|
||||
super.refresh();
|
||||
if (doc.docstatus == 1 && !doc.is_return) {
|
||||
this.frm.add_custom_button(__('Return'), this.make_sales_return, __('Create'));
|
||||
this.frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
@ -36,13 +36,13 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
this.frm.return_print_format = "Sales Invoice Return";
|
||||
this.frm.set_value('consolidated_invoice', '');
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
is_pos: function() {
|
||||
is_pos() {
|
||||
this.set_pos_data();
|
||||
},
|
||||
}
|
||||
|
||||
set_pos_data: async function() {
|
||||
async set_pos_data() {
|
||||
if(this.frm.doc.is_pos) {
|
||||
this.frm.set_value("allocate_advances_automatically", 0);
|
||||
if(!this.frm.doc.company) {
|
||||
@ -69,7 +69,7 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
customer() {
|
||||
if (!this.frm.doc.customer) return
|
||||
@ -86,13 +86,13 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
}, () => {
|
||||
this.apply_pricing_rule();
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
amount: function(){
|
||||
amount(){
|
||||
this.write_off_outstanding_amount_automatically()
|
||||
},
|
||||
}
|
||||
|
||||
change_amount: function(){
|
||||
change_amount(){
|
||||
if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
|
||||
this.calculate_write_off_amount();
|
||||
}else {
|
||||
@ -101,16 +101,16 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
}
|
||||
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
loyalty_amount: function(){
|
||||
loyalty_amount(){
|
||||
this.calculate_outstanding_amount();
|
||||
this.frm.refresh_field("outstanding_amount");
|
||||
this.frm.refresh_field("paid_amount");
|
||||
this.frm.refresh_field("base_paid_amount");
|
||||
},
|
||||
}
|
||||
|
||||
write_off_outstanding_amount_automatically: function() {
|
||||
write_off_outstanding_amount_automatically() {
|
||||
if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
|
||||
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
|
||||
// this will make outstanding amount 0
|
||||
@ -125,17 +125,17 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
|
||||
this.calculate_outstanding_amount(false);
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
make_sales_return: function() {
|
||||
make_sales_return() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_sales_return",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.selling.POSInvoiceController({ frm: cur_frm }))
|
||||
extend_cscript(cur_frm.cscript, new erpnext.selling.POSInvoiceController({ frm: cur_frm }))
|
||||
|
||||
frappe.ui.form.on('POS Invoice', {
|
||||
redeem_loyalty_points: function(frm) {
|
||||
@ -235,4 +235,4 @@ frappe.ui.form.on('POS Invoice', {
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -461,7 +461,17 @@ def get_stock_availability(item_code, warehouse):
|
||||
order by posting_date desc, posting_time desc
|
||||
limit 1""", (item_code, warehouse), as_dict=1)
|
||||
|
||||
pos_sales_qty = frappe.db.sql("""select sum(p_item.qty) as qty
|
||||
pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
|
||||
|
||||
sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0
|
||||
|
||||
if sle_qty and pos_sales_qty:
|
||||
return sle_qty - pos_sales_qty
|
||||
else:
|
||||
return sle_qty
|
||||
|
||||
def get_pos_reserved_qty(item_code, warehouse):
|
||||
reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty
|
||||
from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item
|
||||
where p.name = p_item.parent
|
||||
and p.consolidated_invoice is NULL
|
||||
@ -470,14 +480,8 @@ def get_stock_availability(item_code, warehouse):
|
||||
and p_item.item_code = %s
|
||||
and p_item.warehouse = %s
|
||||
""", (item_code, warehouse), as_dict=1)
|
||||
|
||||
sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0
|
||||
pos_sales_qty = pos_sales_qty[0].qty or 0 if pos_sales_qty else 0
|
||||
|
||||
if sle_qty and pos_sales_qty:
|
||||
return sle_qty - pos_sales_qty
|
||||
else:
|
||||
return sle_qty
|
||||
|
||||
return reserved_qty[0].qty or 0 if reserved_qty else 0
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_sales_return(source_name, target_doc=None):
|
||||
|
@ -1,24 +1,43 @@
|
||||
<h1 class="text-center" style="page-break-before:always">{{ filters.party[0] }}</h1>
|
||||
<h3 class="text-center">{{ _("Statement of Accounts") }}</h3>
|
||||
<div class="page-break">
|
||||
<div id="header-html" class="hidden-pdf">
|
||||
{% if letter_head %}
|
||||
<div class="letter-head text-center">{{ letter_head.content }}</div>
|
||||
<hr style="height:2px;border-width:0;color:black;background-color:black;">
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="footer-html" class="visible-pdf">
|
||||
{% if letter_head.footer %}
|
||||
<div class="letter-head-footer">
|
||||
<hr style="border-width:0;color:black;background-color:black;padding-bottom:2px;">
|
||||
{{ letter_head.footer }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h5 class="text-center">
|
||||
{{ frappe.format(filters.from_date, 'Date')}}
|
||||
{{ _("to") }}
|
||||
{{ frappe.format(filters.to_date, 'Date')}}
|
||||
</h5>
|
||||
<h2 class="text-center">{{ _("STATEMENTS OF ACCOUNTS") }}</h2>
|
||||
<div>
|
||||
<h5 style="float: left;">{{ _("Customer: ") }} <b>{{filters.party[0] }}</b></h5>
|
||||
<h5 style="float: right;">
|
||||
{{ _("Date: ") }}
|
||||
<b>{{ frappe.format(filters.from_date, 'Date')}}
|
||||
{{ _("to") }}
|
||||
{{ frappe.format(filters.to_date, 'Date')}}</b>
|
||||
</h5>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 12%">{{ _("Date") }}</th>
|
||||
<th style="width: 15%">{{ _("Ref") }}</th>
|
||||
<th style="width: 25%">{{ _("Party") }}</th>
|
||||
<th style="width: 15%">{{ _("Debit") }}</th>
|
||||
<th style="width: 15%">{{ _("Credit") }}</th>
|
||||
<th style="width: 18%">{{ _("Balance (Dr - Cr)") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 12%">{{ _("Date") }}</th>
|
||||
<th style="width: 15%">{{ _("Reference") }}</th>
|
||||
<th style="width: 25%">{{ _("Remarks") }}</th>
|
||||
<th style="width: 15%">{{ _("Debit") }}</th>
|
||||
<th style="width: 15%">{{ _("Credit") }}</th>
|
||||
<th style="width: 18%">{{ _("Balance (Dr - Cr)") }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in data %}
|
||||
<tr>
|
||||
{% if(row.posting_date) %}
|
||||
@ -58,32 +77,34 @@
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<br><br>
|
||||
{% if ageing %}
|
||||
<h3 class="text-center">{{ _("Ageing Report Based On ") }} {{ ageing.ageing_based_on }}</h3>
|
||||
<h5 class="text-center">
|
||||
{{ _("Up to " ) }} {{ frappe.format(filters.to_date, 'Date')}}
|
||||
</h5>
|
||||
<br>
|
||||
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 12%">30 Days</th>
|
||||
<th style="width: 15%">60 Days</th>
|
||||
<th style="width: 25%">90 Days</th>
|
||||
<th style="width: 15%">120 Days</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ frappe.utils.fmt_money(ageing.range1, currency=filters.presentation_currency) }}</td>
|
||||
<td>{{ frappe.utils.fmt_money(ageing.range2, currency=filters.presentation_currency) }}</td>
|
||||
<td>{{ frappe.utils.fmt_money(ageing.range3, currency=filters.presentation_currency) }}</td>
|
||||
<td>{{ frappe.utils.fmt_money(ageing.range4, currency=filters.presentation_currency) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<p class="text-right text-muted">Printed On {{ frappe.format(frappe.utils.get_datetime(), 'Datetime') }}</p>
|
||||
</table>
|
||||
<br>
|
||||
{% if ageing %}
|
||||
<h4 class="text-center">{{ _("Ageing Report based on ") }} {{ ageing.ageing_based_on }}
|
||||
{{ _("up to " ) }} {{ frappe.format(filters.to_date, 'Date')}}
|
||||
</h4>
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 25%">30 Days</th>
|
||||
<th style="width: 25%">60 Days</th>
|
||||
<th style="width: 25%">90 Days</th>
|
||||
<th style="width: 25%">120 Days</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ frappe.utils.fmt_money(ageing.range1, currency=filters.presentation_currency) }}</td>
|
||||
<td>{{ frappe.utils.fmt_money(ageing.range2, currency=filters.presentation_currency) }}</td>
|
||||
<td>{{ frappe.utils.fmt_money(ageing.range3, currency=filters.presentation_currency) }}</td>
|
||||
<td>{{ frappe.utils.fmt_money(ageing.range4, currency=filters.presentation_currency) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% if terms_and_conditions %}
|
||||
<div>
|
||||
{{ terms_and_conditions }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -1,6 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"allow_workflow": 1,
|
||||
"autoname": "Prompt",
|
||||
"creation": "2020-05-22 16:46:18.712954",
|
||||
"doctype": "DocType",
|
||||
@ -28,9 +27,11 @@
|
||||
"customers",
|
||||
"preferences",
|
||||
"orientation",
|
||||
"section_break_14",
|
||||
"include_ageing",
|
||||
"ageing_based_on",
|
||||
"section_break_14",
|
||||
"letter_head",
|
||||
"terms_and_conditions",
|
||||
"section_break_1",
|
||||
"enable_auto_email",
|
||||
"section_break_18",
|
||||
@ -270,10 +271,22 @@
|
||||
"fieldname": "body",
|
||||
"fieldtype": "Text Editor",
|
||||
"label": "Body"
|
||||
},
|
||||
{
|
||||
"fieldname": "letter_head",
|
||||
"fieldtype": "Link",
|
||||
"label": "Letter Head",
|
||||
"options": "Letter Head"
|
||||
},
|
||||
{
|
||||
"fieldname": "terms_and_conditions",
|
||||
"fieldtype": "Link",
|
||||
"label": "Terms and Conditions",
|
||||
"options": "Terms and Conditions"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-08-08 08:47:09.185728",
|
||||
"modified": "2021-05-13 12:44:19.574844",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Process Statement Of Accounts",
|
||||
|
@ -64,6 +64,9 @@ def get_report_pdf(doc, consolidated=True):
|
||||
tax_id = frappe.get_doc('Customer', entry.customer).tax_id
|
||||
presentation_currency = get_party_account_currency('Customer', entry.customer, doc.company) \
|
||||
or doc.currency or get_company_currency(doc.company)
|
||||
if doc.letter_head:
|
||||
from frappe.www.printview import get_letter_head
|
||||
letter_head = get_letter_head(doc, 0)
|
||||
|
||||
filters= frappe._dict({
|
||||
'from_date': doc.from_date,
|
||||
@ -91,8 +94,10 @@ def get_report_pdf(doc, consolidated=True):
|
||||
continue
|
||||
|
||||
html = frappe.render_template(template_path, \
|
||||
{"filters": filters, "data": res, "ageing": ageing[0] if (doc.include_ageing and ageing) else None})
|
||||
|
||||
{"filters": filters, "data": res, "ageing": ageing[0] if doc.include_ageing else None,
|
||||
"letter_head": letter_head if doc.letter_head else None,
|
||||
"terms_and_conditions": frappe.db.get_value('Terms and Conditions', doc.terms_and_conditions, 'terms')
|
||||
if doc.terms_and_conditions else None})
|
||||
html = frappe.render_template(base_template_path, {"body": html, \
|
||||
"css": get_print_style(), "title": "Statement For " + entry.customer})
|
||||
statement_dict[entry.customer] = html
|
||||
|
@ -4,10 +4,10 @@
|
||||
frappe.provide("erpnext.accounts");
|
||||
{% include 'erpnext/public/js/controllers/buying.js' %};
|
||||
|
||||
erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
setup: function(doc) {
|
||||
erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.BuyingController {
|
||||
setup(doc) {
|
||||
this.setup_posting_date_time_check();
|
||||
this._super(doc);
|
||||
super.setup(doc);
|
||||
|
||||
// formatter for purchase invoice item
|
||||
if(this.frm.doc.update_stock) {
|
||||
@ -25,14 +25,14 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
company: function() {
|
||||
company() {
|
||||
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
|
||||
},
|
||||
}
|
||||
|
||||
onload: function() {
|
||||
this._super();
|
||||
onload() {
|
||||
super.onload();
|
||||
|
||||
if(!this.frm.doc.__islocal) {
|
||||
// show credit_to in print format
|
||||
@ -48,11 +48,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
}
|
||||
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
|
||||
},
|
||||
}
|
||||
|
||||
refresh: function(doc) {
|
||||
refresh(doc) {
|
||||
const me = this;
|
||||
this._super();
|
||||
super.refresh();
|
||||
|
||||
hide_fields(this.frm.doc);
|
||||
// Show / Hide button
|
||||
@ -161,26 +161,26 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
}
|
||||
|
||||
this.frm.set_df_property("tax_withholding_category", "hidden", doc.apply_tds ? 0 : 1);
|
||||
},
|
||||
}
|
||||
|
||||
unblock_invoice: function() {
|
||||
unblock_invoice() {
|
||||
const me = this;
|
||||
frappe.call({
|
||||
'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.unblock_invoice',
|
||||
'args': {'name': me.frm.doc.name},
|
||||
'callback': (r) => me.frm.reload_doc()
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
block_invoice: function() {
|
||||
block_invoice() {
|
||||
this.make_comment_dialog_and_block_invoice();
|
||||
},
|
||||
}
|
||||
|
||||
change_release_date: function() {
|
||||
change_release_date() {
|
||||
this.make_dialog_and_set_release_date();
|
||||
},
|
||||
}
|
||||
|
||||
can_change_release_date: function(date) {
|
||||
can_change_release_date(date) {
|
||||
const diff = frappe.datetime.get_diff(date, frappe.datetime.nowdate());
|
||||
if (diff < 0) {
|
||||
frappe.throw(__('New release date should be in the future'));
|
||||
@ -188,9 +188,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
make_comment_dialog_and_block_invoice: function(){
|
||||
make_comment_dialog_and_block_invoice(){
|
||||
const me = this;
|
||||
|
||||
const title = __('Block Invoice');
|
||||
@ -232,9 +232,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
});
|
||||
|
||||
this.dialog.show();
|
||||
},
|
||||
}
|
||||
|
||||
make_dialog_and_set_release_date: function() {
|
||||
make_dialog_and_set_release_date() {
|
||||
const me = this;
|
||||
|
||||
const title = __('Set New Release Date');
|
||||
@ -263,17 +263,17 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
});
|
||||
|
||||
this.dialog.show();
|
||||
},
|
||||
}
|
||||
|
||||
set_release_date: function(data) {
|
||||
set_release_date(data) {
|
||||
return frappe.call({
|
||||
'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.change_release_date',
|
||||
'args': data,
|
||||
'callback': (r) => this.frm.reload_doc()
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
supplier: function() {
|
||||
supplier() {
|
||||
var me = this;
|
||||
|
||||
// Do not update if inter company reference is there as the details will already be updated
|
||||
@ -295,9 +295,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1);
|
||||
me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1);
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
apply_tds: function(frm) {
|
||||
apply_tds(frm) {
|
||||
var me = this;
|
||||
|
||||
if (!me.frm.doc.apply_tds) {
|
||||
@ -307,9 +307,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
me.frm.set_value("tax_withholding_category", me.frm.supplier_tds);
|
||||
me.frm.set_df_property("tax_withholding_category", "hidden", 0);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
credit_to: function() {
|
||||
credit_to() {
|
||||
var me = this;
|
||||
if(this.frm.doc.credit_to) {
|
||||
me.frm.call({
|
||||
@ -327,16 +327,16 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
make_inter_company_invoice: function(frm) {
|
||||
make_inter_company_invoice(frm) {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_inter_company_sales_invoice",
|
||||
frm: frm
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
is_paid: function() {
|
||||
is_paid() {
|
||||
hide_fields(this.frm.doc);
|
||||
if(cint(this.frm.doc.is_paid)) {
|
||||
this.frm.set_value("allocate_advances_automatically", 0);
|
||||
@ -347,44 +347,44 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
}
|
||||
this.calculate_outstanding_amount();
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
write_off_amount: function() {
|
||||
write_off_amount() {
|
||||
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
|
||||
this.calculate_outstanding_amount();
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
paid_amount: function() {
|
||||
paid_amount() {
|
||||
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
|
||||
this.write_off_amount();
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
allocated_amount: function() {
|
||||
allocated_amount() {
|
||||
this.calculate_total_advance();
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
items_add: function(doc, cdt, cdn) {
|
||||
items_add(doc, cdt, cdn) {
|
||||
var row = frappe.get_doc(cdt, cdn);
|
||||
this.frm.script_manager.copy_from_first_row("items", row,
|
||||
["expense_account", "cost_center", "project"]);
|
||||
},
|
||||
}
|
||||
|
||||
on_submit: function() {
|
||||
on_submit() {
|
||||
$.each(this.frm.doc["items"] || [], function(i, row) {
|
||||
if(row.purchase_receipt) frappe.model.clear_doc("Purchase Receipt", row.purchase_receipt)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
make_debit_note: function() {
|
||||
make_debit_note() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
|
||||
|
||||
|
@ -1380,7 +1380,7 @@
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-30 22:45:58.334107",
|
||||
"modified": "2021-04-30 22:45:58.334107",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
@ -5,19 +5,19 @@
|
||||
frappe.provide("erpnext.accounts");
|
||||
|
||||
|
||||
erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.extend({
|
||||
setup: function(doc) {
|
||||
erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends erpnext.selling.SellingController {
|
||||
setup(doc) {
|
||||
this.setup_posting_date_time_check();
|
||||
this._super(doc);
|
||||
},
|
||||
company: function() {
|
||||
super.setup(doc);
|
||||
}
|
||||
company() {
|
||||
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
|
||||
},
|
||||
onload: function() {
|
||||
}
|
||||
onload() {
|
||||
var me = this;
|
||||
this._super();
|
||||
super.onload();
|
||||
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice'];
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet'];
|
||||
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
|
||||
// show debit_to in print format
|
||||
this.frm.set_df_property("debit_to", "print_hide", 0);
|
||||
@ -35,11 +35,11 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
erpnext.queries.setup_warehouse_query(this.frm);
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
|
||||
},
|
||||
}
|
||||
|
||||
refresh: function(doc, dt, dn) {
|
||||
refresh(doc, dt, dn) {
|
||||
const me = this;
|
||||
this._super();
|
||||
super.refresh();
|
||||
if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) {
|
||||
// hide new msgbox
|
||||
cur_frm.msgbox.hide();
|
||||
@ -138,16 +138,16 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}, __('Create'));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
make_maintenance_schedule: function() {
|
||||
make_maintenance_schedule() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
on_submit: function(doc, dt, dn) {
|
||||
on_submit(doc, dt, dn) {
|
||||
var me = this;
|
||||
|
||||
if (frappe.get_route()[0] != 'Form') {
|
||||
@ -157,9 +157,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
$.each(doc["items"], function(i, row) {
|
||||
if(row.delivery_note) frappe.model.clear_doc("Delivery Note", row.delivery_note)
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
set_default_print_format: function() {
|
||||
set_default_print_format() {
|
||||
// set default print format to POS type or Credit Note
|
||||
if(cur_frm.doc.is_pos) {
|
||||
if(cur_frm.pos_print_format) {
|
||||
@ -180,9 +180,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
cur_frm.meta._default_print_format = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
sales_order_btn: function() {
|
||||
sales_order_btn() {
|
||||
var me = this;
|
||||
this.$sales_order_btn = this.frm.add_custom_button(__('Sales Order'),
|
||||
function() {
|
||||
@ -201,9 +201,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
})
|
||||
}, __("Get Items From"));
|
||||
},
|
||||
}
|
||||
|
||||
quotation_btn: function() {
|
||||
quotation_btn() {
|
||||
var me = this;
|
||||
this.$quotation_btn = this.frm.add_custom_button(__('Quotation'),
|
||||
function() {
|
||||
@ -225,9 +225,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
})
|
||||
}, __("Get Items From"));
|
||||
},
|
||||
}
|
||||
|
||||
delivery_note_btn: function() {
|
||||
delivery_note_btn() {
|
||||
var me = this;
|
||||
this.$delivery_note_btn = this.frm.add_custom_button(__('Delivery Note'),
|
||||
function() {
|
||||
@ -253,12 +253,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
});
|
||||
}, __("Get Items From"));
|
||||
},
|
||||
}
|
||||
|
||||
tc_name: function() {
|
||||
tc_name() {
|
||||
this.get_terms();
|
||||
},
|
||||
customer: function() {
|
||||
}
|
||||
customer() {
|
||||
if (this.frm.doc.is_pos){
|
||||
var pos_profile = this.frm.doc.pos_profile;
|
||||
}
|
||||
@ -289,16 +289,16 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
make_inter_company_invoice: function() {
|
||||
make_inter_company_invoice() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_inter_company_purchase_invoice",
|
||||
frm: me.frm
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
debit_to: function() {
|
||||
debit_to() {
|
||||
var me = this;
|
||||
if(this.frm.doc.debit_to) {
|
||||
me.frm.call({
|
||||
@ -316,14 +316,14 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
allocated_amount: function() {
|
||||
allocated_amount() {
|
||||
this.calculate_total_advance();
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
write_off_outstanding_amount_automatically: function() {
|
||||
write_off_outstanding_amount_automatically() {
|
||||
if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
|
||||
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
|
||||
// this will make outstanding amount 0
|
||||
@ -338,39 +338,39 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
|
||||
this.calculate_outstanding_amount(false);
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
write_off_amount: function() {
|
||||
write_off_amount() {
|
||||
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
|
||||
this.write_off_outstanding_amount_automatically();
|
||||
},
|
||||
}
|
||||
|
||||
items_add: function(doc, cdt, cdn) {
|
||||
items_add(doc, cdt, cdn) {
|
||||
var row = frappe.get_doc(cdt, cdn);
|
||||
this.frm.script_manager.copy_from_first_row("items", row, ["income_account", "cost_center"]);
|
||||
},
|
||||
}
|
||||
|
||||
set_dynamic_labels: function() {
|
||||
this._super();
|
||||
set_dynamic_labels() {
|
||||
super.set_dynamic_labels();
|
||||
this.frm.events.hide_fields(this.frm)
|
||||
},
|
||||
}
|
||||
|
||||
items_on_form_rendered: function() {
|
||||
erpnext.setup_serial_no();
|
||||
},
|
||||
items_on_form_rendered() {
|
||||
erpnext.setup_serial_or_batch_no();
|
||||
}
|
||||
|
||||
packed_items_on_form_rendered: function(doc, grid_row) {
|
||||
erpnext.setup_serial_no();
|
||||
},
|
||||
packed_items_on_form_rendered(doc, grid_row) {
|
||||
erpnext.setup_serial_or_batch_no();
|
||||
}
|
||||
|
||||
make_sales_return: function() {
|
||||
make_sales_return() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
asset: function(frm, cdt, cdn) {
|
||||
asset(frm, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
if(row.asset) {
|
||||
frappe.call({
|
||||
@ -384,18 +384,18 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
is_pos: function(frm){
|
||||
is_pos(frm){
|
||||
this.set_pos_data();
|
||||
},
|
||||
}
|
||||
|
||||
pos_profile: function() {
|
||||
pos_profile() {
|
||||
this.frm.doc.taxes = []
|
||||
this.set_pos_data();
|
||||
},
|
||||
}
|
||||
|
||||
set_pos_data: function() {
|
||||
set_pos_data() {
|
||||
if(this.frm.doc.is_pos) {
|
||||
this.frm.set_value("allocate_advances_automatically", 0);
|
||||
if(!this.frm.doc.company) {
|
||||
@ -425,13 +425,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
}
|
||||
else this.frm.trigger("refresh");
|
||||
},
|
||||
}
|
||||
|
||||
amount: function(){
|
||||
amount(){
|
||||
this.write_off_outstanding_amount_automatically()
|
||||
},
|
||||
}
|
||||
|
||||
change_amount: function(){
|
||||
change_amount(){
|
||||
if(this.frm.doc.paid_amount > this.frm.doc.grand_total){
|
||||
this.calculate_write_off_amount();
|
||||
}else {
|
||||
@ -440,18 +440,18 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
}
|
||||
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
}
|
||||
|
||||
loyalty_amount: function(){
|
||||
loyalty_amount(){
|
||||
this.calculate_outstanding_amount();
|
||||
this.frm.refresh_field("outstanding_amount");
|
||||
this.frm.refresh_field("paid_amount");
|
||||
this.frm.refresh_field("base_paid_amount");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript['Make Delivery Note'] = function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
@ -685,14 +685,16 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
},
|
||||
|
||||
project: function(frm){
|
||||
frm.call({
|
||||
method: "add_timesheet_data",
|
||||
doc: frm.doc,
|
||||
callback: function(r, rt) {
|
||||
refresh_field(['timesheets'])
|
||||
}
|
||||
})
|
||||
frm.refresh();
|
||||
if (!frm.doc.is_return) {
|
||||
frm.call({
|
||||
method: "add_timesheet_data",
|
||||
doc: frm.doc,
|
||||
callback: function(r, rt) {
|
||||
refresh_field(['timesheets'])
|
||||
}
|
||||
})
|
||||
frm.refresh();
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
@ -807,14 +809,27 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
}
|
||||
},
|
||||
|
||||
add_timesheet_row: function(frm, row, exchange_rate) {
|
||||
frm.add_child('timesheets', {
|
||||
'activity_type': row.activity_type,
|
||||
'description': row.description,
|
||||
'time_sheet': row.parent,
|
||||
'billing_hours': row.billing_hours,
|
||||
'billing_amount': flt(row.billing_amount) * flt(exchange_rate),
|
||||
'timesheet_detail': row.name
|
||||
});
|
||||
frm.refresh_field('timesheets');
|
||||
calculate_total_billing_amount(frm);
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if (frm.doc.project) {
|
||||
if (frm.doc.docstatus===0 && !frm.doc.is_return) {
|
||||
frm.add_custom_button(__('Fetch Timesheet'), function() {
|
||||
let d = new frappe.ui.Dialog({
|
||||
title: __('Fetch Timesheet'),
|
||||
fields: [
|
||||
{
|
||||
"label" : "From",
|
||||
"label" : __("From"),
|
||||
"fieldname": "from_time",
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
@ -824,11 +839,18 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
fieldname: 'col_break_1',
|
||||
},
|
||||
{
|
||||
"label" : "To",
|
||||
"label" : __("To"),
|
||||
"fieldname": "to_time",
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
}
|
||||
},
|
||||
{
|
||||
"label" : __("Project"),
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"options": "Project",
|
||||
"default": frm.doc.project
|
||||
},
|
||||
],
|
||||
primary_action: function() {
|
||||
let data = d.get_values();
|
||||
@ -837,27 +859,35 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
args: {
|
||||
from_time: data.from_time,
|
||||
to_time: data.to_time,
|
||||
project: frm.doc.project
|
||||
project: data.project
|
||||
},
|
||||
callback: function(r) {
|
||||
if(!r.exc) {
|
||||
if(r.message.length > 0) {
|
||||
frm.clear_table('timesheets')
|
||||
r.message.forEach((d) => {
|
||||
frm.add_child('timesheets',{
|
||||
'time_sheet': d.parent,
|
||||
'billing_hours': d.billing_hours,
|
||||
'billing_amount': d.billing_amt,
|
||||
'timesheet_detail': d.name
|
||||
if (!r.exc && r.message.length > 0) {
|
||||
frm.clear_table('timesheets')
|
||||
r.message.forEach((d) => {
|
||||
let exchange_rate = 1.0;
|
||||
if (frm.doc.currency != d.currency) {
|
||||
frappe.call({
|
||||
method: 'erpnext.setup.utils.get_exchange_rate',
|
||||
args: {
|
||||
from_currency: d.currency,
|
||||
to_currency: frm.doc.currency
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
exchange_rate = r.message;
|
||||
frm.events.add_timesheet_row(frm, d, exchange_rate);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
frm.refresh_field('timesheets')
|
||||
}
|
||||
else {
|
||||
frappe.msgprint(__('No Timesheet Found.'))
|
||||
}
|
||||
d.hide();
|
||||
} else {
|
||||
frm.events.add_timesheet_row(frm, d, exchange_rate);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
frappe.msgprint(__('No Timesheets found with the selected filters.'))
|
||||
}
|
||||
d.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -748,6 +748,7 @@
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.total_billing_amount > 0",
|
||||
"depends_on": "eval: !doc.is_return",
|
||||
"fieldname": "time_sheet_list",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_days": 1,
|
||||
@ -770,6 +771,7 @@
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Total Billing Amount",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@ -1951,6 +1953,12 @@
|
||||
"label": "Set Target Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_debit_note",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Debit Note"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "grand_total",
|
||||
@ -1969,7 +1977,7 @@
|
||||
"link_fieldname": "consolidated_invoice"
|
||||
}
|
||||
],
|
||||
"modified": "2021-04-15 23:57:58.766651",
|
||||
"modified": "2021-05-20 22:48:33.988881",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
@ -125,6 +125,8 @@ class SalesInvoice(SellingController):
|
||||
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
|
||||
if not self.is_return:
|
||||
self.validate_serial_numbers()
|
||||
else:
|
||||
self.timesheets = []
|
||||
self.update_packing_list()
|
||||
self.set_billing_hours_and_amount()
|
||||
self.update_timesheet_billing_for_project()
|
||||
@ -337,7 +339,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
if "Healthcare" in active_domains:
|
||||
manage_invoice_submit_cancel(self, "on_cancel")
|
||||
|
||||
self.unlink_sales_invoice_from_timesheets()
|
||||
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
|
||||
|
||||
def update_status_updater_args(self):
|
||||
@ -393,6 +395,18 @@ class SalesInvoice(SellingController):
|
||||
if validate_against_credit_limit:
|
||||
check_credit_limit(self.customer, self.company, bypass_credit_limit_check_at_sales_order)
|
||||
|
||||
def unlink_sales_invoice_from_timesheets(self):
|
||||
for row in self.timesheets:
|
||||
timesheet = frappe.get_doc('Timesheet', row.time_sheet)
|
||||
for time_log in timesheet.time_logs:
|
||||
if time_log.sales_invoice == self.name:
|
||||
time_log.sales_invoice = None
|
||||
timesheet.calculate_total_amounts()
|
||||
timesheet.calculate_percentage_billed()
|
||||
timesheet.flags.ignore_validate_update_after_submit = True
|
||||
timesheet.set_status()
|
||||
timesheet.db_update_all()
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_missing_values(self, for_validate=False):
|
||||
pos = self.set_pos_fields(for_validate)
|
||||
@ -427,7 +441,7 @@ class SalesInvoice(SellingController):
|
||||
timesheet.calculate_percentage_billed()
|
||||
timesheet.flags.ignore_validate_update_after_submit = True
|
||||
timesheet.set_status()
|
||||
timesheet.save()
|
||||
timesheet.db_update_all()
|
||||
|
||||
def update_time_sheet_detail(self, timesheet, args, sales_invoice):
|
||||
for data in timesheet.time_logs:
|
||||
@ -741,8 +755,10 @@ class SalesInvoice(SellingController):
|
||||
self.append('timesheets', {
|
||||
'time_sheet': data.parent,
|
||||
'billing_hours': data.billing_hours,
|
||||
'billing_amount': data.billing_amt,
|
||||
'timesheet_detail': data.name
|
||||
'billing_amount': data.billing_amount,
|
||||
'timesheet_detail': data.name,
|
||||
'activity_type': data.activity_type,
|
||||
'description': data.description
|
||||
})
|
||||
|
||||
self.calculate_billing_amount_for_timesheet()
|
||||
@ -1111,7 +1127,7 @@ class SalesInvoice(SellingController):
|
||||
if not item.serial_no:
|
||||
continue
|
||||
|
||||
for serial_no in item.serial_no.split("\n"):
|
||||
for serial_no in get_serial_nos(item.serial_no):
|
||||
if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
|
||||
frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
|
||||
|
||||
@ -1755,15 +1771,10 @@ def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, wa
|
||||
item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
|
||||
|
||||
def get_delivery_note_details(internal_reference):
|
||||
so_item_map = {}
|
||||
|
||||
si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
|
||||
filters={'parent': internal_reference})
|
||||
|
||||
for d in si_item_details:
|
||||
so_item_map.setdefault(d.name, d.so_detail)
|
||||
|
||||
return so_item_map
|
||||
return {d.name: d.so_detail for d in si_item_details if d.so_detail}
|
||||
|
||||
def get_sales_invoice_details(internal_reference):
|
||||
dn_item_map = {}
|
||||
|
@ -1,172 +1,78 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2016-06-14 19:21:34.321662",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"actions": [],
|
||||
"creation": "2016-06-14 19:21:34.321662",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"activity_type",
|
||||
"description",
|
||||
"billing_hours",
|
||||
"billing_amount",
|
||||
"time_sheet",
|
||||
"timesheet_detail"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "time_sheet",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Time Sheet",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Timesheet",
|
||||
"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,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "time_sheet",
|
||||
"fieldtype": "Link",
|
||||
"in_global_search": 1,
|
||||
"in_list_view": 1,
|
||||
"label": "Time Sheet",
|
||||
"options": "Timesheet",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "billing_hours",
|
||||
"fieldtype": "Float",
|
||||
"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": "Billing Hours",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"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,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "billing_hours",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Billing Hours",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "billing_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": "Billing Amount",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"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,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "billing_amount",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Billing Amount",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "timesheet_detail",
|
||||
"fieldtype": "Data",
|
||||
"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": "Timesheet Detail",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"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,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "timesheet_detail",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Timesheet Detail",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "activity_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Activity Type",
|
||||
"options": "Activity Type",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"in_list_view": 1,
|
||||
"label": "Description",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"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": "2019-02-18 18:50:44.770361",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Timesheet",
|
||||
"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,
|
||||
"track_views": 0
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-05-20 22:33:57.234846",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Timesheet",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
@ -21,7 +21,10 @@ def get_party_details(inv):
|
||||
else:
|
||||
party_type = 'Supplier'
|
||||
party = inv.supplier
|
||||
|
||||
|
||||
if not party:
|
||||
frappe.throw(_("Please select {0} first").format(party_type))
|
||||
|
||||
return party_type, party
|
||||
|
||||
def get_party_tax_withholding_details(inv, tax_withholding_category=None):
|
||||
@ -324,7 +327,7 @@ def get_tds_amount_from_ldc(ldc, parties, fiscal_year, pan_no, tax_details, post
|
||||
net_total, ldc.certificate_limit
|
||||
):
|
||||
tds_amount = get_ltds_amount(net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details)
|
||||
|
||||
|
||||
return tds_amount
|
||||
|
||||
def get_debit_note_amount(suppliers, fiscal_year_details, company=None):
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"attach_print": 0,
|
||||
"channel": "Email",
|
||||
"condition": "doc.auto_created",
|
||||
"creation": "2018-04-25 14:19:05.440361",
|
||||
"days_in_advance": 0,
|
||||
|
@ -364,7 +364,7 @@ class ReceivablePayableReport(object):
|
||||
payment_terms_details = frappe.db.sql("""
|
||||
select
|
||||
si.name, si.party_account_currency, si.currency, si.conversion_rate,
|
||||
ps.due_date, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount
|
||||
ps.due_date, ps.payment_term, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount
|
||||
from `tab{0}` si, `tabPayment Schedule` ps
|
||||
where
|
||||
si.name = ps.parent and
|
||||
@ -394,7 +394,7 @@ class ReceivablePayableReport(object):
|
||||
"due_date": d.due_date,
|
||||
"invoiced": invoiced,
|
||||
"invoice_grand_total": row.invoiced,
|
||||
"payment_term": d.description,
|
||||
"payment_term": d.description or d.payment_term,
|
||||
"paid": d.paid_amount + d.discounted_amount,
|
||||
"credit_note": 0.0,
|
||||
"outstanding": invoiced - d.paid_amount - d.discounted_amount
|
||||
|
@ -5,7 +5,8 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt, cint
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data,
|
||||
get_filtered_list_for_consolidated_report)
|
||||
|
||||
def execute(filters=None):
|
||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||
@ -132,6 +133,10 @@ def get_report_summary(period_list, asset, liability, equity, provisional_profit
|
||||
if filters.get('accumulated_values'):
|
||||
period_list = [period_list[-1]]
|
||||
|
||||
# from consolidated financial statement
|
||||
if filters.get('accumulated_in_group_company'):
|
||||
period_list = get_filtered_list_for_consolidated_report(filters, period_list)
|
||||
|
||||
for period in period_list:
|
||||
key = period if consolidated else period.key
|
||||
if asset:
|
||||
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, cstr
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data, get_filtered_list_for_consolidated_report)
|
||||
from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from six import iteritems
|
||||
@ -67,9 +67,9 @@ def execute(filters=None):
|
||||
section_data.append(account_data)
|
||||
|
||||
add_total_row_account(data, section_data, cash_flow_account['section_footer'],
|
||||
period_list, company_currency, summary_data)
|
||||
period_list, company_currency, summary_data, filters)
|
||||
|
||||
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data)
|
||||
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters)
|
||||
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
|
||||
|
||||
chart = get_chart_data(columns, data)
|
||||
@ -162,18 +162,26 @@ def get_start_date(period, accumulated_values, company):
|
||||
|
||||
return start_date
|
||||
|
||||
def add_total_row_account(out, data, label, period_list, currency, summary_data, consolidated = False):
|
||||
def add_total_row_account(out, data, label, period_list, currency, summary_data, filters, consolidated=False):
|
||||
total_row = {
|
||||
"account_name": "'" + _("{0}").format(label) + "'",
|
||||
"account": "'" + _("{0}").format(label) + "'",
|
||||
"currency": currency
|
||||
}
|
||||
|
||||
summary_data[label] = 0
|
||||
|
||||
# from consolidated financial statement
|
||||
if filters.get('accumulated_in_group_company'):
|
||||
period_list = get_filtered_list_for_consolidated_report(filters, period_list)
|
||||
|
||||
for row in data:
|
||||
if row.get("parent_account"):
|
||||
for period in period_list:
|
||||
key = period if consolidated else period['key']
|
||||
total_row.setdefault(key, 0.0)
|
||||
total_row[key] += row.get(key, 0.0)
|
||||
summary_data[label] += row.get(key)
|
||||
|
||||
total_row.setdefault("total", 0.0)
|
||||
total_row["total"] += row["total"]
|
||||
@ -181,7 +189,6 @@ def add_total_row_account(out, data, label, period_list, currency, summary_data,
|
||||
out.append(total_row)
|
||||
out.append({})
|
||||
|
||||
summary_data[label] = total_row["total"]
|
||||
|
||||
def get_report_summary(summary_data, currency):
|
||||
report_summary = []
|
||||
|
@ -2,118 +2,128 @@
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Consolidated Financial Statement"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("Company"),
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"filter_based_on",
|
||||
"label": __("Filter Based On"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["Fiscal Year", "Date Range"],
|
||||
"default": ["Fiscal Year"],
|
||||
"reqd": 1,
|
||||
on_change: function() {
|
||||
let filter_based_on = frappe.query_report.get_filter_value('filter_based_on');
|
||||
frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range');
|
||||
frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range');
|
||||
frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year');
|
||||
frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year');
|
||||
frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
frappe.query_reports["Consolidated Financial Statement"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname":"company",
|
||||
"label": __("Company"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Company",
|
||||
"default": frappe.defaults.get_user_default("Company"),
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"filter_based_on",
|
||||
"label": __("Filter Based On"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["Fiscal Year", "Date Range"],
|
||||
"default": ["Fiscal Year"],
|
||||
"reqd": 1,
|
||||
on_change: function() {
|
||||
let filter_based_on = frappe.query_report.get_filter_value('filter_based_on');
|
||||
frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range');
|
||||
frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range');
|
||||
frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year');
|
||||
frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year');
|
||||
|
||||
frappe.query_report.refresh();
|
||||
frappe.query_report.refresh();
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"period_start_date",
|
||||
"label": __("Start Date"),
|
||||
"fieldtype": "Date",
|
||||
"hidden": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"period_end_date",
|
||||
"label": __("End Date"),
|
||||
"fieldtype": "Date",
|
||||
"hidden": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"from_fiscal_year",
|
||||
"label": __("Start Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"to_fiscal_year",
|
||||
"label": __("End Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname":"report",
|
||||
"label": __("Report"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["Profit and Loss Statement", "Balance Sheet", "Cash Flow"],
|
||||
"default": "Balance Sheet",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "presentation_currency",
|
||||
"label": __("Currency"),
|
||||
"fieldtype": "Select",
|
||||
"options": erpnext.get_presentation_currency_list(),
|
||||
"default": frappe.defaults.get_user_default("Currency")
|
||||
},
|
||||
{
|
||||
"fieldname":"accumulated_in_group_company",
|
||||
"label": __("Accumulated Values in Group Company"),
|
||||
"fieldtype": "Check",
|
||||
"default": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "include_default_book_entries",
|
||||
"label": __("Include Default Book Entries"),
|
||||
"fieldtype": "Check",
|
||||
"default": 1
|
||||
}
|
||||
],
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
if (data && column.fieldname=="account") {
|
||||
value = data.account_name || value;
|
||||
|
||||
column.link_onclick =
|
||||
"erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
|
||||
column.is_tree = true;
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"period_start_date",
|
||||
"label": __("Start Date"),
|
||||
"fieldtype": "Date",
|
||||
"hidden": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"period_end_date",
|
||||
"label": __("End Date"),
|
||||
"fieldtype": "Date",
|
||||
"hidden": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"from_fiscal_year",
|
||||
"label": __("Start Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"to_fiscal_year",
|
||||
"label": __("End Year"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Fiscal Year",
|
||||
"default": frappe.defaults.get_user_default("fiscal_year"),
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Finance Book"
|
||||
},
|
||||
{
|
||||
"fieldname":"report",
|
||||
"label": __("Report"),
|
||||
"fieldtype": "Select",
|
||||
"options": ["Profit and Loss Statement", "Balance Sheet", "Cash Flow"],
|
||||
"default": "Balance Sheet",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "presentation_currency",
|
||||
"label": __("Currency"),
|
||||
"fieldtype": "Select",
|
||||
"options": erpnext.get_presentation_currency_list(),
|
||||
"default": frappe.defaults.get_user_default("Currency")
|
||||
},
|
||||
{
|
||||
"fieldname":"accumulated_in_group_company",
|
||||
"label": __("Accumulated Values in Group Company"),
|
||||
"fieldtype": "Check",
|
||||
"default": 0
|
||||
},
|
||||
{
|
||||
"fieldname": "include_default_book_entries",
|
||||
"label": __("Include Default Book Entries"),
|
||||
"fieldtype": "Check",
|
||||
"default": 1
|
||||
}
|
||||
],
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
if (!data.parent_account) {
|
||||
value = $(`<span>${value}</span>`);
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
var $value = $(value).css("font-weight", "bold");
|
||||
if (!data.parent_account) {
|
||||
value = $(`<span>${value}</span>`);
|
||||
|
||||
value = $value.wrap("<p></p>").parent().html();
|
||||
}
|
||||
return value;
|
||||
},
|
||||
onload: function() {
|
||||
let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
|
||||
var $value = $(value).css("font-weight", "bold");
|
||||
|
||||
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
|
||||
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
|
||||
frappe.query_report.set_filter_value({
|
||||
period_start_date: fy.year_start_date,
|
||||
period_end_date: fy.year_end_date
|
||||
value = $value.wrap("<p></p>").parent().html();
|
||||
}
|
||||
return value;
|
||||
},
|
||||
onload: function() {
|
||||
let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
|
||||
|
||||
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
|
||||
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
|
||||
frappe.query_report.set_filter_value({
|
||||
period_start_date: fy.year_start_date,
|
||||
period_end_date: fy.year_end_date
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
@ -94,7 +94,7 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
||||
|
||||
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
|
||||
|
||||
report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, True)
|
||||
report_summary = get_pl_summary(companies, '', income, expense, net_profit_loss, company_currency, filters, True)
|
||||
|
||||
return data, None, chart, report_summary
|
||||
|
||||
@ -149,9 +149,9 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
section_data.append(account_data)
|
||||
|
||||
add_total_row_account(data, section_data, cash_flow_account['section_footer'],
|
||||
companies, company_currency, summary_data, True)
|
||||
companies, company_currency, summary_data, filters, True)
|
||||
|
||||
add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, True)
|
||||
add_total_row_account(data, data, _("Net Change in Cash"), companies, company_currency, summary_data, filters, True)
|
||||
|
||||
report_summary = get_cash_flow_summary(summary_data, company_currency)
|
||||
|
||||
@ -329,8 +329,9 @@ def prepare_data(accounts, start_date, end_date, balance_must_be, companies, com
|
||||
has_value = False
|
||||
total = 0
|
||||
row = frappe._dict({
|
||||
"account_name": _(d.account_name),
|
||||
"account": _(d.account_name),
|
||||
"account_name": ('%s - %s' %(_(d.account_number), _(d.account_name))
|
||||
if d.account_number else _(d.account_name)),
|
||||
"account": _(d.name),
|
||||
"parent_account": _(d.parent_account),
|
||||
"indent": flt(d.indent),
|
||||
"year_start_date": start_date,
|
||||
|
@ -119,10 +119,10 @@ def validate_fiscal_year(fiscal_year, from_fiscal_year, to_fiscal_year):
|
||||
|
||||
def validate_dates(from_date, to_date):
|
||||
if not from_date or not to_date:
|
||||
frappe.throw("From Date and To Date are mandatory")
|
||||
frappe.throw(_("From Date and To Date are mandatory"))
|
||||
|
||||
if to_date < from_date:
|
||||
frappe.throw("To Date cannot be less than From Date")
|
||||
frappe.throw(_("To Date cannot be less than From Date"))
|
||||
|
||||
def get_months(start_date, end_date):
|
||||
diff = (12 * end_date.year + end_date.month) - (12 * start_date.year + start_date.month)
|
||||
@ -522,4 +522,12 @@ def get_columns(periodicity, period_list, accumulated_values=1, company=None):
|
||||
"width": 150
|
||||
})
|
||||
|
||||
return columns
|
||||
return columns
|
||||
|
||||
def get_filtered_list_for_consolidated_report(filters, period_list):
|
||||
filtered_summary_list = []
|
||||
for period in period_list:
|
||||
if period == filters.get('company'):
|
||||
filtered_summary_list.append(period)
|
||||
|
||||
return filtered_summary_list
|
||||
|
@ -116,22 +116,19 @@ def validate_filters(filters):
|
||||
frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method"))
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s".format(
|
||||
company=filters.get("company"),
|
||||
from_date=filters.get("from_date"),
|
||||
to_date=filters.get("to_date"))
|
||||
conditions = "company = %(company)s AND posting_date >= %(from_date)s AND posting_date <= %(to_date)s"
|
||||
|
||||
if filters.get("pos_profile"):
|
||||
conditions += " AND pos_profile = %(pos_profile)s".format(pos_profile=filters.get("pos_profile"))
|
||||
conditions += " AND pos_profile = %(pos_profile)s"
|
||||
|
||||
if filters.get("owner"):
|
||||
conditions += " AND owner = %(owner)s".format(owner=filters.get("owner"))
|
||||
conditions += " AND owner = %(owner)s"
|
||||
|
||||
if filters.get("customer"):
|
||||
conditions += " AND customer = %(customer)s".format(customer=filters.get("customer"))
|
||||
conditions += " AND customer = %(customer)s"
|
||||
|
||||
if filters.get("is_return"):
|
||||
conditions += " AND is_return = %(is_return)s".format(is_return=filters.get("is_return"))
|
||||
conditions += " AND is_return = %(is_return)s"
|
||||
|
||||
if filters.get("mode_of_payment"):
|
||||
conditions += """
|
||||
|
@ -5,7 +5,8 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data,
|
||||
get_filtered_list_for_consolidated_report)
|
||||
|
||||
def execute(filters=None):
|
||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||
@ -33,13 +34,17 @@ def execute(filters=None):
|
||||
chart = get_chart_data(filters, columns, income, expense, net_profit_loss)
|
||||
|
||||
currency = filters.presentation_currency or frappe.get_cached_value('Company', filters.company, "default_currency")
|
||||
report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency)
|
||||
report_summary = get_report_summary(period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters)
|
||||
|
||||
return columns, data, None, chart, report_summary
|
||||
|
||||
def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, consolidated=False):
|
||||
def get_report_summary(period_list, periodicity, income, expense, net_profit_loss, currency, filters, consolidated=False):
|
||||
net_income, net_expense, net_profit = 0.0, 0.0, 0.0
|
||||
|
||||
# from consolidated financial statement
|
||||
if filters.get('accumulated_in_group_company'):
|
||||
period_list = get_filtered_list_for_consolidated_report(filters, period_list)
|
||||
|
||||
for period in period_list:
|
||||
key = period if consolidated else period.key
|
||||
if income:
|
||||
|
0
erpnext/accounts/report/tax_detail/__init__.py
Normal file
0
erpnext/accounts/report/tax_detail/__init__.py
Normal file
451
erpnext/accounts/report/tax_detail/tax_detail.js
Normal file
451
erpnext/accounts/report/tax_detail/tax_detail.js
Normal file
@ -0,0 +1,451 @@
|
||||
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
// Contributed by Case Solved and sponsored by Nulight Studios
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.provide('frappe.query_reports');
|
||||
|
||||
frappe.query_reports["Tax Detail"] = {
|
||||
filters: [
|
||||
{
|
||||
fieldname: "company",
|
||||
label: __("Company"),
|
||||
fieldtype: "Link",
|
||||
options: "Company",
|
||||
default: frappe.defaults.get_user_default("company"),
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname: "from_date",
|
||||
label: __("From Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.month_start(frappe.datetime.get_today()),
|
||||
reqd: 1,
|
||||
width: "60px"
|
||||
},
|
||||
{
|
||||
fieldname: "to_date",
|
||||
label: __("To Date"),
|
||||
fieldtype: "Date",
|
||||
default: frappe.datetime.month_end(frappe.datetime.get_today()),
|
||||
reqd: 1,
|
||||
width: "60px"
|
||||
},
|
||||
{
|
||||
fieldname: "report_name",
|
||||
label: __("Report Name"),
|
||||
fieldtype: "Read Only",
|
||||
default: frappe.query_report.report_name,
|
||||
hidden: 1,
|
||||
reqd: 1
|
||||
},
|
||||
{
|
||||
fieldname: "mode",
|
||||
label: __("Mode"),
|
||||
fieldtype: "Read Only",
|
||||
default: "edit",
|
||||
hidden: 1,
|
||||
reqd: 1
|
||||
}
|
||||
],
|
||||
onload: function onload(report) {
|
||||
// Remove Add Column and Save from menu
|
||||
report.page.add_inner_button(__("New Report"), () => new_report(), __("Custom Report"));
|
||||
report.page.add_inner_button(__("Load Report"), () => load_report(), __("Custom Report"));
|
||||
hide_filters(report);
|
||||
}
|
||||
};
|
||||
|
||||
function hide_filters(report) {
|
||||
report.page.page_form[0].querySelectorAll('.form-group.frappe-control').forEach(function setHidden(field) {
|
||||
if (field.dataset.fieldtype == "Read Only") {
|
||||
field.classList.add("hidden");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
erpnext.TaxDetail = class TaxDetail {
|
||||
constructor() {
|
||||
this.patch();
|
||||
this.load_report();
|
||||
}
|
||||
// Monkey patch the QueryReport class
|
||||
patch() {
|
||||
this.qr = frappe.query_report;
|
||||
this.super = {
|
||||
refresh_report: this.qr.refresh_report,
|
||||
show_footer_message: this.qr.show_footer_message
|
||||
}
|
||||
this.qr.refresh_report = () => this.refresh_report();
|
||||
this.qr.show_footer_message = () => this.show_footer_message();
|
||||
}
|
||||
show_footer_message() {
|
||||
// The last thing to run after datatable_render in refresh()
|
||||
this.super.show_footer_message.apply(this.qr);
|
||||
if (this.qr.report_name !== 'Tax Detail') {
|
||||
this.show_help();
|
||||
if (this.loading) {
|
||||
this.set_section('');
|
||||
} else {
|
||||
this.reload_component('');
|
||||
}
|
||||
}
|
||||
this.loading = false;
|
||||
}
|
||||
refresh_report() {
|
||||
// Infrequent report build (onload), load filters & data
|
||||
// super function runs a refresh() serially
|
||||
// already run within frappe.run_serially
|
||||
this.loading = true;
|
||||
this.super.refresh_report.apply(this.qr);
|
||||
if (this.qr.report_name !== 'Tax Detail') {
|
||||
frappe.call({
|
||||
method: 'erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports',
|
||||
args: {name: this.qr.report_name}
|
||||
}).then((r) => {
|
||||
const data = JSON.parse(r.message[this.qr.report_name]['json']);
|
||||
this.create_controls();
|
||||
this.sections = data.sections || {};
|
||||
this.controls['show_detail'].set_input(data.show_detail);
|
||||
});
|
||||
}
|
||||
}
|
||||
load_report() {
|
||||
// One-off report build like titles, menu, etc
|
||||
// Run when this object is created which happens in qr.load_report
|
||||
this.qr.menu_items = this.get_menu_items();
|
||||
}
|
||||
get_menu_items() {
|
||||
// Replace Save action
|
||||
let new_items = [];
|
||||
const save = __('Save');
|
||||
|
||||
for (let item of this.qr.menu_items) {
|
||||
if (item.label === save) {
|
||||
new_items.push({
|
||||
label: save,
|
||||
action: () => this.save_report(),
|
||||
standard: false
|
||||
});
|
||||
} else {
|
||||
new_items.push(item);
|
||||
}
|
||||
}
|
||||
return new_items;
|
||||
}
|
||||
save_report() {
|
||||
this.check_datatable();
|
||||
if (this.qr.report_name !== 'Tax Detail') {
|
||||
frappe.call({
|
||||
method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report',
|
||||
args: {
|
||||
reference_report: 'Tax Detail',
|
||||
report_name: this.qr.report_name,
|
||||
data: {
|
||||
columns: this.qr.get_visible_columns(),
|
||||
sections: this.sections,
|
||||
show_detail: this.controls['show_detail'].get_input_value()
|
||||
}
|
||||
},
|
||||
freeze: true
|
||||
}).then((r) => {
|
||||
this.set_section('');
|
||||
});
|
||||
}
|
||||
}
|
||||
check_datatable() {
|
||||
if (!this.qr.datatable) {
|
||||
frappe.throw(__('Please change the date range to load data first'));
|
||||
}
|
||||
}
|
||||
set_section(name) {
|
||||
// Sets the given section name and then reloads the data
|
||||
if (name && !this.sections[name]) {
|
||||
this.sections[name] = {};
|
||||
}
|
||||
let options = Object.keys(this.sections);
|
||||
options.unshift('');
|
||||
this.controls['section_name'].$wrapper.find("select").empty().add_options(options);
|
||||
const org_mode = this.qr.get_filter_value('mode');
|
||||
let refresh = false;
|
||||
if (name) {
|
||||
this.controls['section_name'].set_input(name);
|
||||
this.qr.set_filter_value('mode', 'edit');
|
||||
if (org_mode === 'run') {
|
||||
refresh = true;
|
||||
}
|
||||
} else {
|
||||
this.controls['section_name'].set_input('');
|
||||
this.qr.set_filter_value('mode', 'run');
|
||||
if (org_mode === 'edit') {
|
||||
refresh = true;
|
||||
}
|
||||
}
|
||||
if (refresh) {
|
||||
this.qr.refresh();
|
||||
}
|
||||
this.reload_component('');
|
||||
}
|
||||
reload_component(component_name) {
|
||||
const section_name = this.controls['section_name'].get_input_value();
|
||||
if (section_name) {
|
||||
const section = this.sections[section_name];
|
||||
const component_names = Object.keys(section);
|
||||
component_names.unshift('');
|
||||
this.controls['component'].$wrapper.find("select").empty().add_options(component_names);
|
||||
this.controls['component'].set_input(component_name);
|
||||
if (component_name) {
|
||||
this.controls['component_type'].set_input(section[component_name].type);
|
||||
}
|
||||
} else {
|
||||
this.controls['component'].$wrapper.find("select").empty();
|
||||
this.controls['component'].set_input('');
|
||||
}
|
||||
this.set_table_filters();
|
||||
}
|
||||
set_table_filters() {
|
||||
let filters = {};
|
||||
const section_name = this.controls['section_name'].get_input_value();
|
||||
const component_name = this.controls['component'].get_input_value();
|
||||
if (section_name && component_name) {
|
||||
const component_type = this.sections[section_name][component_name].type;
|
||||
if (component_type === 'filter') {
|
||||
filters = this.sections[section_name][component_name]['filters'];
|
||||
}
|
||||
}
|
||||
this.setAppliedFilters(filters);
|
||||
}
|
||||
setAppliedFilters(filters) {
|
||||
if (this.qr.datatable) {
|
||||
Array.from(this.qr.datatable.header.querySelectorAll('.dt-filter')).map(function setFilters(input) {
|
||||
let idx = input.dataset.colIndex;
|
||||
if (filters[idx]) {
|
||||
input.value = filters[idx];
|
||||
} else {
|
||||
input.value = null;
|
||||
}
|
||||
});
|
||||
this.qr.datatable.columnmanager.applyFilter(filters);
|
||||
}
|
||||
}
|
||||
delete(name, type) {
|
||||
if (type === 'section') {
|
||||
delete this.sections[name];
|
||||
const new_section = Object.keys(this.sections)[0] || '';
|
||||
this.set_section(new_section);
|
||||
}
|
||||
if (type === 'component') {
|
||||
const cur_section = this.controls['section_name'].get_input_value();
|
||||
delete this.sections[cur_section][name];
|
||||
this.reload_component('');
|
||||
}
|
||||
}
|
||||
create_controls() {
|
||||
let controls = {};
|
||||
// SELECT in data.js
|
||||
controls['section_name'] = this.qr.page.add_field({
|
||||
label: __('Section'),
|
||||
fieldtype: 'Select',
|
||||
fieldname: 'section_name',
|
||||
change: (e) => {
|
||||
this.set_section(this.controls['section_name'].get_input_value());
|
||||
}
|
||||
});
|
||||
// BUTTON in button.js
|
||||
controls['new_section'] = this.qr.page.add_field({
|
||||
label: __('New Section'),
|
||||
fieldtype: 'Button',
|
||||
fieldname: 'new_section',
|
||||
click: () => {
|
||||
frappe.prompt({
|
||||
label: __('Section Name'),
|
||||
fieldname: 'name',
|
||||
fieldtype: 'Data'
|
||||
}, (values) => {
|
||||
this.set_section(values.name);
|
||||
});
|
||||
}
|
||||
});
|
||||
controls['delete_section'] = this.qr.page.add_field({
|
||||
label: __('Delete Section'),
|
||||
fieldtype: 'Button',
|
||||
fieldname: 'delete_section',
|
||||
click: () => {
|
||||
let cur_section = this.controls['section_name'].get_input_value();
|
||||
if (cur_section) {
|
||||
frappe.confirm(__('Are you sure you want to delete section') + ' ' + cur_section + '?',
|
||||
() => {this.delete(cur_section, 'section')});
|
||||
}
|
||||
}
|
||||
});
|
||||
controls['component'] = this.qr.page.add_field({
|
||||
label: __('Component'),
|
||||
fieldtype: 'Select',
|
||||
fieldname: 'component',
|
||||
change: (e) => {
|
||||
this.reload_component(this.controls['component'].get_input_value());
|
||||
}
|
||||
});
|
||||
controls['component_type'] = this.qr.page.add_field({
|
||||
label: __('Component Type'),
|
||||
fieldtype: 'Select',
|
||||
fieldname: 'component_type',
|
||||
default: 'filter',
|
||||
options: [
|
||||
{label: __('Filtered Row Subtotal'), value: 'filter'},
|
||||
{label: __('Section Subtotal'), value: 'section'}
|
||||
]
|
||||
});
|
||||
controls['add_component'] = this.qr.page.add_field({
|
||||
label: __('Add Component'),
|
||||
fieldtype: 'Button',
|
||||
fieldname: 'add_component',
|
||||
click: () => {
|
||||
this.check_datatable();
|
||||
let section_name = this.controls['section_name'].get_input_value();
|
||||
if (section_name) {
|
||||
const component_type = this.controls['component_type'].get_input_value();
|
||||
let idx = 0;
|
||||
const names = Object.keys(this.sections[section_name]);
|
||||
if (names.length > 0) {
|
||||
const idxs = names.map((key) => parseInt(key.match(/\d+$/)) || 0);
|
||||
idx = Math.max(...idxs) + 1;
|
||||
}
|
||||
const filters = this.qr.datatable.columnmanager.getAppliedFilters();
|
||||
if (component_type === 'filter') {
|
||||
const name = 'Filter' + idx.toString();
|
||||
let data = {
|
||||
type: component_type,
|
||||
filters: filters
|
||||
}
|
||||
this.sections[section_name][name] = data;
|
||||
this.reload_component(name);
|
||||
} else if (component_type === 'section') {
|
||||
if (filters && Object.keys(filters).length !== 0) {
|
||||
frappe.show_alert({
|
||||
message: __('Column filters ignored'),
|
||||
indicator: 'yellow'
|
||||
});
|
||||
}
|
||||
let data = {
|
||||
type: component_type
|
||||
}
|
||||
frappe.prompt({
|
||||
label: __('Section'),
|
||||
fieldname: 'section',
|
||||
fieldtype: 'Select',
|
||||
options: Object.keys(this.sections)
|
||||
}, (values) => {
|
||||
this.sections[section_name][values.section] = data;
|
||||
this.reload_component(values.section);
|
||||
});
|
||||
} else {
|
||||
frappe.throw(__('Please select the Component Type first'));
|
||||
}
|
||||
} else {
|
||||
frappe.throw(__('Please select the Section first'));
|
||||
}
|
||||
}
|
||||
});
|
||||
controls['delete_component'] = this.qr.page.add_field({
|
||||
label: __('Delete Component'),
|
||||
fieldtype: 'Button',
|
||||
fieldname: 'delete_component',
|
||||
click: () => {
|
||||
const component = this.controls['component'].get_input_value();
|
||||
if (component) {
|
||||
frappe.confirm(__('Are you sure you want to delete component') + ' ' + component + '?',
|
||||
() => {this.delete(component, 'component')});
|
||||
}
|
||||
}
|
||||
});
|
||||
controls['save'] = this.qr.page.add_field({
|
||||
label: __('Save & Run'),
|
||||
fieldtype: 'Button',
|
||||
fieldname: 'save',
|
||||
click: () => {
|
||||
this.save_report();
|
||||
}
|
||||
});
|
||||
controls['show_detail'] = this.qr.page.add_field({
|
||||
label: __('Show Detail'),
|
||||
fieldtype: 'Check',
|
||||
fieldname: 'show_detail',
|
||||
default: 1
|
||||
});
|
||||
this.controls = controls;
|
||||
}
|
||||
show_help() {
|
||||
const help = __('Your custom report is built from General Ledger Entries within the date range. You can add multiple sections to the report using the New Section button. Each component added to a section adds a subset of the data into the specified section. Beware of duplicated data rows. The Filtered Row component type saves the datatable column filters to specify the added data. The Section component type refers to the data in a previously defined section, but it cannot refer to its parent section. The Amount column is summed to give the section subtotal. Use the Show Detail box to see the data rows included in each section in the final report. Once finished, hit Save & Run. Report contributed by');
|
||||
this.qr.$report_footer.append('<div class="col-md-12"><strong>' + __('Help') + `: </strong>${help}<a href="https://www.casesolved.co.uk"> Case Solved</a></div>`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!window.taxdetail) {
|
||||
window.taxdetail = new erpnext.TaxDetail();
|
||||
}
|
||||
|
||||
function get_reports(cb) {
|
||||
frappe.call({
|
||||
method: 'erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports',
|
||||
freeze: true
|
||||
}).then((r) => {
|
||||
cb(r.message);
|
||||
})
|
||||
}
|
||||
|
||||
function new_report() {
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __('New Report'),
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'report_name',
|
||||
label: __('Report Name'),
|
||||
fieldtype: 'Data',
|
||||
default: 'VAT Return'
|
||||
}
|
||||
],
|
||||
primary_action_label: __('Create'),
|
||||
primary_action: function new_report_pa(values) {
|
||||
frappe.call({
|
||||
method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report',
|
||||
args: {
|
||||
reference_report: 'Tax Detail',
|
||||
report_name: values.report_name,
|
||||
data: {
|
||||
columns: [],
|
||||
sections: {},
|
||||
show_detail: 1
|
||||
}
|
||||
},
|
||||
freeze: true
|
||||
}).then((r) => {
|
||||
frappe.set_route('query-report', values.report_name);
|
||||
});
|
||||
dialog.hide();
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
function load_report() {
|
||||
get_reports(function load_report_cb(reports) {
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __('Load Report'),
|
||||
fields: [
|
||||
{
|
||||
fieldname: 'report_name',
|
||||
label: __('Report Name'),
|
||||
fieldtype: 'Select',
|
||||
options: Object.keys(reports)
|
||||
}
|
||||
],
|
||||
primary_action_label: __('Load'),
|
||||
primary_action: function load_report_pa(values) {
|
||||
dialog.hide();
|
||||
frappe.set_route('query-report', values.report_name);
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
});
|
||||
}
|
32
erpnext/accounts/report/tax_detail/tax_detail.json
Normal file
32
erpnext/accounts/report/tax_detail/tax_detail.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2021-02-19 16:44:21.175113",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2021-02-19 16:44:21.175113",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Tax Detail",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "GL Entry",
|
||||
"report_name": "Tax Detail",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"role": "Accounts Manager"
|
||||
},
|
||||
{
|
||||
"role": "Auditor"
|
||||
}
|
||||
]
|
||||
}
|
296
erpnext/accounts/report/tax_detail/tax_detail.py
Normal file
296
erpnext/accounts/report/tax_detail/tax_detail.py
Normal file
@ -0,0 +1,296 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
# Contributed by Case Solved and sponsored by Nulight Studios
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
from frappe import _
|
||||
|
||||
# NOTE: Payroll is implemented using Journal Entries which are included as GL Entries
|
||||
|
||||
# field lists in multiple doctypes will be coalesced
|
||||
required_sql_fields = {
|
||||
("GL Entry", 1): ["posting_date"],
|
||||
("Account",): ["root_type", "account_type"],
|
||||
("GL Entry", 2): ["account", "voucher_type", "voucher_no", "debit", "credit"],
|
||||
("Purchase Invoice Item", "Sales Invoice Item"): ["base_net_amount", "item_tax_rate", "item_tax_template", "item_group", "item_name"],
|
||||
("Purchase Invoice", "Sales Invoice"): ["taxes_and_charges", "tax_category"],
|
||||
}
|
||||
|
||||
|
||||
def execute(filters=None):
|
||||
if not filters:
|
||||
return [], []
|
||||
|
||||
fieldlist = required_sql_fields
|
||||
fieldstr = get_fieldstr(fieldlist)
|
||||
|
||||
gl_entries = frappe.db.sql("""
|
||||
select {fieldstr}
|
||||
from `tabGL Entry` ge
|
||||
inner join `tabAccount` a on
|
||||
ge.account=a.name and ge.company=a.company
|
||||
left join `tabSales Invoice` si on
|
||||
ge.company=si.company and ge.voucher_type='Sales Invoice' and ge.voucher_no=si.name
|
||||
left join `tabSales Invoice Item` sii on
|
||||
a.root_type='Income' and si.name=sii.parent
|
||||
left join `tabPurchase Invoice` pi on
|
||||
ge.company=pi.company and ge.voucher_type='Purchase Invoice' and ge.voucher_no=pi.name
|
||||
left join `tabPurchase Invoice Item` pii on
|
||||
a.root_type='Expense' and pi.name=pii.parent
|
||||
where
|
||||
ge.company=%(company)s and
|
||||
ge.posting_date>=%(from_date)s and
|
||||
ge.posting_date<=%(to_date)s
|
||||
order by ge.posting_date, ge.voucher_no
|
||||
""".format(fieldstr=fieldstr), filters, as_dict=1)
|
||||
|
||||
report_data = modify_report_data(gl_entries)
|
||||
summary = None
|
||||
if filters['mode'] == 'run' and filters['report_name'] != 'Tax Detail':
|
||||
report_data, summary = run_report(filters['report_name'], report_data)
|
||||
|
||||
# return columns, data, message, chart, report_summary
|
||||
return get_columns(fieldlist), report_data, None, None, summary
|
||||
|
||||
def run_report(report_name, data):
|
||||
"Applies the sections and filters saved in the custom report"
|
||||
report_config = json.loads(frappe.get_doc('Report', report_name).json)
|
||||
# Columns indexed from 1 wrt colno
|
||||
columns = report_config.get('columns')
|
||||
sections = report_config.get('sections', {})
|
||||
show_detail = report_config.get('show_detail', 1)
|
||||
report = {}
|
||||
new_data = []
|
||||
summary = []
|
||||
for section_name, section in sections.items():
|
||||
report[section_name] = {'rows': [], 'subtotal': 0.0}
|
||||
for component_name, component in section.items():
|
||||
if component['type'] == 'filter':
|
||||
for row in data:
|
||||
matched = True
|
||||
for colno, filter_string in component['filters'].items():
|
||||
filter_field = columns[int(colno) - 1]['fieldname']
|
||||
if not filter_match(row[filter_field], filter_string):
|
||||
matched = False
|
||||
break
|
||||
if matched:
|
||||
report[section_name]['rows'] += [row]
|
||||
report[section_name]['subtotal'] += row['amount']
|
||||
if component['type'] == 'section':
|
||||
if component_name == section_name:
|
||||
frappe.throw(_("A report component cannot refer to its parent section") + ": " + section_name)
|
||||
try:
|
||||
report[section_name]['rows'] += report[component_name]['rows']
|
||||
report[section_name]['subtotal'] += report[component_name]['subtotal']
|
||||
except KeyError:
|
||||
frappe.throw(_("A report component can only refer to an earlier section") + ": " + section_name)
|
||||
|
||||
if show_detail:
|
||||
new_data += report[section_name]['rows']
|
||||
new_data += [{'voucher_no': section_name, 'amount': report[section_name]['subtotal']}]
|
||||
summary += [{'label': section_name, 'datatype': 'Currency', 'value': report[section_name]['subtotal']}]
|
||||
if show_detail:
|
||||
new_data += [{}]
|
||||
return new_data or data, summary or None
|
||||
|
||||
def filter_match(value, string):
|
||||
"Approximation to datatable filters"
|
||||
import datetime
|
||||
if string == '':
|
||||
return True
|
||||
if value is None:
|
||||
value = -999999999999999
|
||||
elif isinstance(value, datetime.date):
|
||||
return True
|
||||
|
||||
if isinstance(value, str):
|
||||
value = value.lower()
|
||||
string = string.lower()
|
||||
if string[0] == '<':
|
||||
return True if string[1:].strip() else False
|
||||
elif string[0] == '>':
|
||||
return False if string[1:].strip() else True
|
||||
elif string[0] == '=':
|
||||
return string[1:] in value if string[1:] else False
|
||||
elif string[0:2] == '!=':
|
||||
return string[2:] not in value
|
||||
elif len(string.split(':')) == 2:
|
||||
pre, post = string.split(':')
|
||||
return (True if not pre.strip() and post.strip() in value else False)
|
||||
else:
|
||||
return string in value
|
||||
else:
|
||||
if string[0] in ['<', '>', '=']:
|
||||
operator = string[0]
|
||||
if operator == '=':
|
||||
operator = '=='
|
||||
string = string[1:].strip()
|
||||
elif string[0:2] == '!=':
|
||||
operator = '!='
|
||||
string = string[2:].strip()
|
||||
elif len(string.split(':')) == 2:
|
||||
pre, post = string.split(':')
|
||||
try:
|
||||
return (True if float(pre) <= value and float(post) >= value else False)
|
||||
except ValueError:
|
||||
return (False if pre.strip() else True)
|
||||
else:
|
||||
return string in str(value)
|
||||
|
||||
try:
|
||||
num = float(string) if string.strip() else 0
|
||||
return frappe.safe_eval(f'{value} {operator} {num}')
|
||||
except ValueError:
|
||||
if operator == '<':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def abbrev(dt):
|
||||
return ''.join(l[0].lower() for l in dt.split(' ')) + '.'
|
||||
|
||||
def doclist(dt, dfs):
|
||||
return [abbrev(dt) + f for f in dfs]
|
||||
|
||||
def as_split(fields):
|
||||
for field in fields:
|
||||
split = field.split(' as ')
|
||||
yield (split[0], split[1] if len(split) > 1 else split[0])
|
||||
|
||||
def coalesce(doctypes, fields):
|
||||
coalesce = []
|
||||
for name, new_name in as_split(fields):
|
||||
sharedfields = ', '.join(abbrev(dt) + name for dt in doctypes)
|
||||
coalesce += [f'coalesce({sharedfields}) as {new_name}']
|
||||
return coalesce
|
||||
|
||||
def get_fieldstr(fieldlist):
|
||||
fields = []
|
||||
for doctypes, docfields in fieldlist.items():
|
||||
if len(doctypes) == 1 or isinstance(doctypes[1], int):
|
||||
fields += doclist(doctypes[0], docfields)
|
||||
else:
|
||||
fields += coalesce(doctypes, docfields)
|
||||
return ', '.join(fields)
|
||||
|
||||
def get_columns(fieldlist):
|
||||
columns = {}
|
||||
for doctypes, docfields in fieldlist.items():
|
||||
fieldmap = {name: new_name for name, new_name in as_split(docfields)}
|
||||
for doctype in doctypes:
|
||||
if isinstance(doctype, int):
|
||||
break
|
||||
meta = frappe.get_meta(doctype)
|
||||
# get column field metadata from the db
|
||||
fieldmeta = {}
|
||||
for field in meta.get('fields'):
|
||||
if field.fieldname in fieldmap.keys():
|
||||
new_name = fieldmap[field.fieldname]
|
||||
fieldmeta[new_name] = {
|
||||
"label": _(field.label),
|
||||
"fieldname": new_name,
|
||||
"fieldtype": field.fieldtype,
|
||||
"options": field.options
|
||||
}
|
||||
# edit the columns to match the modified data
|
||||
for field in fieldmap.values():
|
||||
col = modify_report_columns(doctype, field, fieldmeta[field])
|
||||
if col:
|
||||
columns[col["fieldname"]] = col
|
||||
# use of a dict ensures duplicate columns are removed
|
||||
return list(columns.values())
|
||||
|
||||
def modify_report_columns(doctype, field, column):
|
||||
"Because data is rearranged into other columns"
|
||||
if doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
|
||||
if field in ["item_tax_rate", "base_net_amount"]:
|
||||
return None
|
||||
|
||||
if doctype == "GL Entry" and field in ["debit", "credit"]:
|
||||
column.update({"label": _("Amount"), "fieldname": "amount"})
|
||||
|
||||
if field == "taxes_and_charges":
|
||||
column.update({"label": _("Taxes and Charges Template")})
|
||||
return column
|
||||
|
||||
def modify_report_data(data):
|
||||
import json
|
||||
new_data = []
|
||||
for line in data:
|
||||
if line.debit:
|
||||
line.amount = -line.debit
|
||||
else:
|
||||
line.amount = line.credit
|
||||
# Remove Invoice GL Tax Entries and generate Tax entries from the invoice lines
|
||||
if "Invoice" in line.voucher_type:
|
||||
if line.account_type not in ("Tax", "Round Off"):
|
||||
new_data += [line]
|
||||
if line.item_tax_rate:
|
||||
tax_rates = json.loads(line.item_tax_rate)
|
||||
for account, rate in tax_rates.items():
|
||||
tax_line = line.copy()
|
||||
tax_line.account_type = "Tax"
|
||||
tax_line.account = account
|
||||
if line.voucher_type == "Sales Invoice":
|
||||
line.amount = line.base_net_amount
|
||||
tax_line.amount = line.base_net_amount * (rate / 100)
|
||||
if line.voucher_type == "Purchase Invoice":
|
||||
line.amount = -line.base_net_amount
|
||||
tax_line.amount = -line.base_net_amount * (rate / 100)
|
||||
new_data += [tax_line]
|
||||
else:
|
||||
new_data += [line]
|
||||
return new_data
|
||||
|
||||
|
||||
# JS client utilities
|
||||
|
||||
custom_report_dict = {
|
||||
'ref_doctype': 'GL Entry',
|
||||
'report_type': 'Custom Report',
|
||||
'reference_report': 'Tax Detail'
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_custom_reports(name=None):
|
||||
filters = custom_report_dict.copy()
|
||||
if name:
|
||||
filters['name'] = name
|
||||
reports = frappe.get_list('Report',
|
||||
filters = filters,
|
||||
fields = ['name', 'json'],
|
||||
as_list=False
|
||||
)
|
||||
reports_dict = {rep.pop('name'): rep for rep in reports}
|
||||
# Prevent custom reports with the same name
|
||||
reports_dict['Tax Detail'] = {'json': None}
|
||||
return reports_dict
|
||||
|
||||
@frappe.whitelist()
|
||||
def save_custom_report(reference_report, report_name, data):
|
||||
if reference_report != 'Tax Detail':
|
||||
frappe.throw(_("The wrong report is referenced."))
|
||||
if report_name == 'Tax Detail':
|
||||
frappe.throw(_("The parent report cannot be overwritten."))
|
||||
|
||||
doc = {
|
||||
'doctype': 'Report',
|
||||
'report_name': report_name,
|
||||
'is_standard': 'No',
|
||||
'module': 'Accounts',
|
||||
'json': data
|
||||
}
|
||||
doc.update(custom_report_dict)
|
||||
|
||||
try:
|
||||
newdoc = frappe.get_doc(doc)
|
||||
newdoc.insert()
|
||||
frappe.msgprint(_("Report created successfully"))
|
||||
except frappe.exceptions.DuplicateEntryError:
|
||||
dbdoc = frappe.get_doc('Report', report_name)
|
||||
dbdoc.update(doc)
|
||||
dbdoc.save()
|
||||
frappe.msgprint(_("Report updated successfully"))
|
||||
return report_name
|
840
erpnext/accounts/report/tax_detail/test_tax_detail.json
Normal file
840
erpnext/accounts/report/tax_detail/test_tax_detail.json
Normal file
@ -0,0 +1,840 @@
|
||||
[
|
||||
{
|
||||
"account_manager": null,
|
||||
"accounts": [],
|
||||
"companies": [],
|
||||
"credit_limits": [],
|
||||
"customer_details": null,
|
||||
"customer_group": "All Customer Groups",
|
||||
"customer_name": "_Test Customer",
|
||||
"customer_pos_id": null,
|
||||
"customer_primary_address": null,
|
||||
"customer_primary_contact": null,
|
||||
"customer_type": "Company",
|
||||
"default_bank_account": null,
|
||||
"default_commission_rate": 0.0,
|
||||
"default_currency": null,
|
||||
"default_price_list": null,
|
||||
"default_sales_partner": null,
|
||||
"disabled": 0,
|
||||
"dn_required": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Customer",
|
||||
"email_id": null,
|
||||
"gender": null,
|
||||
"image": null,
|
||||
"industry": null,
|
||||
"is_frozen": 0,
|
||||
"is_internal_customer": 0,
|
||||
"language": "en",
|
||||
"lead_name": null,
|
||||
"loyalty_program": null,
|
||||
"loyalty_program_tier": null,
|
||||
"market_segment": null,
|
||||
"mobile_no": null,
|
||||
"modified": "2021-02-15 05:18:03.624724",
|
||||
"name": "_Test Customer",
|
||||
"naming_series": "CUST-.YYYY.-",
|
||||
"pan": null,
|
||||
"parent": null,
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"payment_terms": null,
|
||||
"primary_address": null,
|
||||
"represents_company": "",
|
||||
"sales_team": [],
|
||||
"salutation": null,
|
||||
"so_required": 0,
|
||||
"tax_category": null,
|
||||
"tax_id": null,
|
||||
"tax_withholding_category": null,
|
||||
"territory": "All Territories",
|
||||
"website": null
|
||||
},{
|
||||
"accounts": [],
|
||||
"allow_purchase_invoice_creation_without_purchase_order": 0,
|
||||
"allow_purchase_invoice_creation_without_purchase_receipt": 0,
|
||||
"companies": [],
|
||||
"country": "United Kingdom",
|
||||
"default_bank_account": null,
|
||||
"default_currency": null,
|
||||
"default_price_list": null,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Supplier",
|
||||
"hold_type": "",
|
||||
"image": null,
|
||||
"is_frozen": 0,
|
||||
"is_internal_supplier": 0,
|
||||
"is_transporter": 0,
|
||||
"language": "en",
|
||||
"modified": "2021-03-31 16:47:10.109316",
|
||||
"name": "_Test Supplier",
|
||||
"naming_series": "SUP-.YYYY.-",
|
||||
"on_hold": 0,
|
||||
"pan": null,
|
||||
"parent": null,
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"payment_terms": null,
|
||||
"prevent_pos": 0,
|
||||
"prevent_rfqs": 0,
|
||||
"release_date": null,
|
||||
"represents_company": null,
|
||||
"supplier_details": null,
|
||||
"supplier_group": "Raw Material",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"supplier_type": "Company",
|
||||
"tax_category": null,
|
||||
"tax_id": null,
|
||||
"tax_withholding_category": null,
|
||||
"warn_pos": 0,
|
||||
"warn_rfqs": 0,
|
||||
"website": null
|
||||
},{
|
||||
"account_currency": "GBP",
|
||||
"account_name": "Debtors",
|
||||
"account_number": "",
|
||||
"account_type": "Receivable",
|
||||
"balance_must_be": "",
|
||||
"company": "_T",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Account",
|
||||
"freeze_account": "No",
|
||||
"include_in_gross": 0,
|
||||
"inter_company_account": 0,
|
||||
"is_group": 0,
|
||||
"lft": 58,
|
||||
"modified": "2021-03-26 04:44:19.955468",
|
||||
"name": "Debtors - _T",
|
||||
"old_parent": null,
|
||||
"parent": null,
|
||||
"parent_account": "Application of Funds (Assets) - _T",
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"report_type": "Balance Sheet",
|
||||
"rgt": 59,
|
||||
"root_type": "Asset",
|
||||
"tax_rate": 0.0
|
||||
},{
|
||||
"account_currency": "GBP",
|
||||
"account_name": "Sales",
|
||||
"account_number": "",
|
||||
"account_type": "Income Account",
|
||||
"balance_must_be": "",
|
||||
"company": "_T",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Account",
|
||||
"freeze_account": "No",
|
||||
"include_in_gross": 0,
|
||||
"inter_company_account": 0,
|
||||
"is_group": 0,
|
||||
"lft": 291,
|
||||
"modified": "2021-03-26 04:50:21.697703",
|
||||
"name": "Sales - _T",
|
||||
"old_parent": null,
|
||||
"parent": null,
|
||||
"parent_account": "Income - _T",
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"report_type": "Profit and Loss",
|
||||
"rgt": 292,
|
||||
"root_type": "Income",
|
||||
"tax_rate": 0.0
|
||||
},{
|
||||
"account_currency": "GBP",
|
||||
"account_name": "VAT on Sales",
|
||||
"account_number": "",
|
||||
"account_type": "Tax",
|
||||
"balance_must_be": "",
|
||||
"company": "_T",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Account",
|
||||
"freeze_account": "No",
|
||||
"include_in_gross": 0,
|
||||
"inter_company_account": 0,
|
||||
"is_group": 0,
|
||||
"lft": 317,
|
||||
"modified": "2021-03-26 04:50:21.697703",
|
||||
"name": "VAT on Sales - _T",
|
||||
"old_parent": null,
|
||||
"parent": null,
|
||||
"parent_account": "Source of Funds (Liabilities) - _T",
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"report_type": "Balance Sheet",
|
||||
"rgt": 318,
|
||||
"root_type": "Liability",
|
||||
"tax_rate": 0.0
|
||||
},{
|
||||
"account_currency": "GBP",
|
||||
"account_name": "Cost of Goods Sold",
|
||||
"account_number": "",
|
||||
"account_type": "Cost of Goods Sold",
|
||||
"balance_must_be": "",
|
||||
"company": "_T",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Account",
|
||||
"freeze_account": "No",
|
||||
"include_in_gross": 0,
|
||||
"inter_company_account": 0,
|
||||
"is_group": 0,
|
||||
"lft": 171,
|
||||
"modified": "2021-03-26 04:44:19.994857",
|
||||
"name": "Cost of Goods Sold - _T",
|
||||
"old_parent": null,
|
||||
"parent": null,
|
||||
"parent_account": "Expenses - _T",
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"report_type": "Profit and Loss",
|
||||
"rgt": 172,
|
||||
"root_type": "Expense",
|
||||
"tax_rate": 0.0
|
||||
},{
|
||||
"account_currency": "GBP",
|
||||
"account_name": "VAT on Purchases",
|
||||
"account_number": "",
|
||||
"account_type": "Tax",
|
||||
"balance_must_be": "",
|
||||
"company": "_T",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Account",
|
||||
"freeze_account": "No",
|
||||
"include_in_gross": 0,
|
||||
"inter_company_account": 0,
|
||||
"is_group": 0,
|
||||
"lft": 80,
|
||||
"modified": "2021-03-26 04:44:19.961983",
|
||||
"name": "VAT on Purchases - _T",
|
||||
"old_parent": null,
|
||||
"parent": null,
|
||||
"parent_account": "Application of Funds (Assets) - _T",
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"report_type": "Balance Sheet",
|
||||
"rgt": 81,
|
||||
"root_type": "Asset",
|
||||
"tax_rate": 0.0
|
||||
},{
|
||||
"account_currency": "GBP",
|
||||
"account_name": "Creditors",
|
||||
"account_number": "",
|
||||
"account_type": "Payable",
|
||||
"balance_must_be": "",
|
||||
"company": "_T",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Account",
|
||||
"freeze_account": "No",
|
||||
"include_in_gross": 0,
|
||||
"inter_company_account": 0,
|
||||
"is_group": 0,
|
||||
"lft": 302,
|
||||
"modified": "2021-03-26 04:50:21.697703",
|
||||
"name": "Creditors - _T",
|
||||
"old_parent": null,
|
||||
"parent": null,
|
||||
"parent_account": "Source of Funds (Liabilities) - _T",
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"report_type": "Balance Sheet",
|
||||
"rgt": 303,
|
||||
"root_type": "Liability",
|
||||
"tax_rate": 0.0
|
||||
},{
|
||||
"additional_discount_percentage": 0.0,
|
||||
"address_display": null,
|
||||
"adjust_advance_taxes": 0,
|
||||
"advances": [],
|
||||
"against_expense_account": "Cost of Goods Sold - _T",
|
||||
"allocate_advances_automatically": 0,
|
||||
"amended_from": null,
|
||||
"apply_discount_on": "Grand Total",
|
||||
"apply_tds": 0,
|
||||
"auto_repeat": null,
|
||||
"base_discount_amount": 0.0,
|
||||
"base_grand_total": 511.68,
|
||||
"base_in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
|
||||
"base_net_total": 426.4,
|
||||
"base_paid_amount": 0.0,
|
||||
"base_rounded_total": 511.68,
|
||||
"base_rounding_adjustment": 0.0,
|
||||
"base_taxes_and_charges_added": 85.28,
|
||||
"base_taxes_and_charges_deducted": 0.0,
|
||||
"base_total": 426.4,
|
||||
"base_total_taxes_and_charges": 85.28,
|
||||
"base_write_off_amount": 0.0,
|
||||
"bill_date": null,
|
||||
"bill_no": null,
|
||||
"billing_address": null,
|
||||
"billing_address_display": null,
|
||||
"buying_price_list": "Standard Buying",
|
||||
"cash_bank_account": null,
|
||||
"clearance_date": null,
|
||||
"company": "_T",
|
||||
"contact_display": null,
|
||||
"contact_email": null,
|
||||
"contact_mobile": null,
|
||||
"contact_person": null,
|
||||
"conversion_rate": 1.0,
|
||||
"cost_center": null,
|
||||
"credit_to": "Creditors - _T",
|
||||
"currency": "GBP",
|
||||
"disable_rounded_total": 0,
|
||||
"discount_amount": 0.0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Purchase Invoice",
|
||||
"due_date": null,
|
||||
"from_date": null,
|
||||
"grand_total": 511.68,
|
||||
"group_same_items": 0,
|
||||
"hold_comment": null,
|
||||
"ignore_pricing_rule": 0,
|
||||
"in_words": "GBP Five Hundred And Eleven and Sixty Eight Pence only.",
|
||||
"inter_company_invoice_reference": null,
|
||||
"is_internal_supplier": 0,
|
||||
"is_opening": "No",
|
||||
"is_paid": 0,
|
||||
"is_return": 0,
|
||||
"is_subcontracted": "No",
|
||||
"items": [
|
||||
{
|
||||
"allow_zero_valuation_rate": 0,
|
||||
"amount": 426.4,
|
||||
"asset_category": null,
|
||||
"asset_location": null,
|
||||
"base_amount": 426.4,
|
||||
"base_net_amount": 426.4,
|
||||
"base_net_rate": 5.33,
|
||||
"base_price_list_rate": 5.33,
|
||||
"base_rate": 5.33,
|
||||
"base_rate_with_margin": 0.0,
|
||||
"batch_no": null,
|
||||
"bom": null,
|
||||
"brand": null,
|
||||
"conversion_factor": 0.0,
|
||||
"cost_center": "Main - _T",
|
||||
"deferred_expense_account": null,
|
||||
"description": "<div class=\"ql-editor read-mode\"><p>Fluid to make widgets</p></div>",
|
||||
"discount_amount": 0.0,
|
||||
"discount_percentage": 0.0,
|
||||
"enable_deferred_expense": 0,
|
||||
"expense_account": "Cost of Goods Sold - _T",
|
||||
"from_warehouse": null,
|
||||
"image": null,
|
||||
"include_exploded_items": 0,
|
||||
"is_fixed_asset": 0,
|
||||
"is_free_item": 0,
|
||||
"item_code": null,
|
||||
"item_group": null,
|
||||
"item_name": "Widget Fluid 1Litre",
|
||||
"item_tax_amount": 0.0,
|
||||
"item_tax_rate": "{\"VAT on Purchases - _T\": 20.0}",
|
||||
"item_tax_template": null,
|
||||
"landed_cost_voucher_amount": 0.0,
|
||||
"manufacturer": null,
|
||||
"manufacturer_part_no": null,
|
||||
"margin_rate_or_amount": 0.0,
|
||||
"margin_type": "",
|
||||
"net_amount": 426.4,
|
||||
"net_rate": 5.33,
|
||||
"page_break": 0,
|
||||
"parent": null,
|
||||
"parentfield": "items",
|
||||
"parenttype": "Purchase Invoice",
|
||||
"po_detail": null,
|
||||
"pr_detail": null,
|
||||
"price_list_rate": 5.33,
|
||||
"pricing_rules": null,
|
||||
"project": null,
|
||||
"purchase_invoice_item": null,
|
||||
"purchase_order": null,
|
||||
"purchase_receipt": null,
|
||||
"qty": 80.0,
|
||||
"quality_inspection": null,
|
||||
"rate": 5.33,
|
||||
"rate_with_margin": 0.0,
|
||||
"received_qty": 0.0,
|
||||
"rejected_qty": 0.0,
|
||||
"rejected_serial_no": null,
|
||||
"rejected_warehouse": null,
|
||||
"rm_supp_cost": 0.0,
|
||||
"sales_invoice_item": null,
|
||||
"serial_no": null,
|
||||
"service_end_date": null,
|
||||
"service_start_date": null,
|
||||
"service_stop_date": null,
|
||||
"stock_qty": 0.0,
|
||||
"stock_uom": "Nos",
|
||||
"stock_uom_rate": 0.0,
|
||||
"total_weight": 0.0,
|
||||
"uom": "Nos",
|
||||
"valuation_rate": 0.0,
|
||||
"warehouse": null,
|
||||
"weight_per_unit": 0.0,
|
||||
"weight_uom": null
|
||||
}
|
||||
],
|
||||
"language": "en",
|
||||
"letter_head": null,
|
||||
"mode_of_payment": null,
|
||||
"modified": "2021-04-03 03:33:09.180453",
|
||||
"name": null,
|
||||
"naming_series": "ACC-PINV-.YYYY.-",
|
||||
"net_total": 426.4,
|
||||
"on_hold": 0,
|
||||
"other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Purchases</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Widget Fluid 1Litre</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 426.40\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 85.28\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
|
||||
"outstanding_amount": 511.68,
|
||||
"paid_amount": 0.0,
|
||||
"parent": null,
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"party_account_currency": "GBP",
|
||||
"payment_schedule": [],
|
||||
"payment_terms_template": null,
|
||||
"plc_conversion_rate": 1.0,
|
||||
"posting_date": null,
|
||||
"posting_time": "16:59:56.789522",
|
||||
"price_list_currency": "GBP",
|
||||
"pricing_rules": [],
|
||||
"project": null,
|
||||
"rejected_warehouse": null,
|
||||
"release_date": null,
|
||||
"remarks": "No Remarks",
|
||||
"represents_company": null,
|
||||
"return_against": null,
|
||||
"rounded_total": 511.68,
|
||||
"rounding_adjustment": 0.0,
|
||||
"scan_barcode": null,
|
||||
"select_print_heading": null,
|
||||
"set_from_warehouse": null,
|
||||
"set_posting_time": 0,
|
||||
"set_warehouse": null,
|
||||
"shipping_address": null,
|
||||
"shipping_address_display": "",
|
||||
"shipping_rule": null,
|
||||
"status": "Unpaid",
|
||||
"supplied_items": [],
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_address": null,
|
||||
"supplier_name": "_Test Supplier",
|
||||
"supplier_warehouse": "Stores - _T",
|
||||
"tax_category": null,
|
||||
"tax_id": null,
|
||||
"tax_withholding_category": null,
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "VAT on Purchases - _T",
|
||||
"add_deduct_tax": "Add",
|
||||
"base_tax_amount": 85.28,
|
||||
"base_tax_amount_after_discount_amount": 85.28,
|
||||
"base_total": 511.68,
|
||||
"category": "Total",
|
||||
"charge_type": "On Net Total",
|
||||
"cost_center": "Main - _T",
|
||||
"description": "VAT on Purchases",
|
||||
"included_in_print_rate": 0,
|
||||
"item_wise_tax_detail": "{\"Widget Fluid 1Litre\":[20.0,85.28]}",
|
||||
"parent": null,
|
||||
"parentfield": "taxes",
|
||||
"parenttype": "Purchase Invoice",
|
||||
"rate": 0.0,
|
||||
"row_id": null,
|
||||
"tax_amount": 85.28,
|
||||
"tax_amount_after_discount_amount": 85.28,
|
||||
"total": 511.68
|
||||
}
|
||||
],
|
||||
"taxes_and_charges": null,
|
||||
"taxes_and_charges_added": 85.28,
|
||||
"taxes_and_charges_deducted": 0.0,
|
||||
"tc_name": null,
|
||||
"terms": null,
|
||||
"title": "_Purchase Invoice",
|
||||
"to_date": null,
|
||||
"total": 426.4,
|
||||
"total_advance": 0.0,
|
||||
"total_net_weight": 0.0,
|
||||
"total_qty": 80.0,
|
||||
"total_taxes_and_charges": 85.28,
|
||||
"unrealized_profit_loss_account": null,
|
||||
"update_stock": 0,
|
||||
"write_off_account": null,
|
||||
"write_off_amount": 0.0,
|
||||
"write_off_cost_center": null
|
||||
},{
|
||||
"account_for_change_amount": null,
|
||||
"additional_discount_percentage": 0.0,
|
||||
"address_display": null,
|
||||
"advances": [],
|
||||
"against_income_account": "Sales - _T",
|
||||
"allocate_advances_automatically": 0,
|
||||
"amended_from": null,
|
||||
"apply_discount_on": "Grand Total",
|
||||
"auto_repeat": null,
|
||||
"base_change_amount": 0.0,
|
||||
"base_discount_amount": 0.0,
|
||||
"base_grand_total": 868.25,
|
||||
"base_in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
|
||||
"base_net_total": 825.0,
|
||||
"base_paid_amount": 0.0,
|
||||
"base_rounded_total": 868.25,
|
||||
"base_rounding_adjustment": 0.0,
|
||||
"base_total": 825.0,
|
||||
"base_total_taxes_and_charges": 43.25,
|
||||
"base_write_off_amount": 0.0,
|
||||
"c_form_applicable": "No",
|
||||
"c_form_no": null,
|
||||
"campaign": null,
|
||||
"cash_bank_account": null,
|
||||
"change_amount": 0.0,
|
||||
"commission_rate": 0.0,
|
||||
"company": "_T",
|
||||
"company_address": null,
|
||||
"company_address_display": null,
|
||||
"company_tax_id": null,
|
||||
"contact_display": null,
|
||||
"contact_email": null,
|
||||
"contact_mobile": null,
|
||||
"contact_person": null,
|
||||
"conversion_rate": 1.0,
|
||||
"cost_center": null,
|
||||
"currency": "GBP",
|
||||
"customer": "_Test Customer",
|
||||
"customer_address": null,
|
||||
"customer_group": "All Customer Groups",
|
||||
"customer_name": "_Test Customer",
|
||||
"debit_to": "Debtors - _T",
|
||||
"discount_amount": 0.0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Sales Invoice",
|
||||
"due_date": null,
|
||||
"from_date": null,
|
||||
"grand_total": 868.25,
|
||||
"group_same_items": 0,
|
||||
"ignore_pricing_rule": 0,
|
||||
"in_words": "GBP Eight Hundred And Sixty Eight and Twenty Five Pence only.",
|
||||
"inter_company_invoice_reference": null,
|
||||
"is_consolidated": 0,
|
||||
"is_discounted": 0,
|
||||
"is_internal_customer": 0,
|
||||
"is_opening": "No",
|
||||
"is_pos": 0,
|
||||
"is_return": 0,
|
||||
"items": [
|
||||
{
|
||||
"actual_batch_qty": 0.0,
|
||||
"actual_qty": 0.0,
|
||||
"allow_zero_valuation_rate": 0,
|
||||
"amount": 200.0,
|
||||
"asset": null,
|
||||
"barcode": null,
|
||||
"base_amount": 200.0,
|
||||
"base_net_amount": 200.0,
|
||||
"base_net_rate": 50.0,
|
||||
"base_price_list_rate": 0.0,
|
||||
"base_rate": 50.0,
|
||||
"base_rate_with_margin": 0.0,
|
||||
"batch_no": null,
|
||||
"brand": null,
|
||||
"conversion_factor": 1.0,
|
||||
"cost_center": "Main - _T",
|
||||
"customer_item_code": null,
|
||||
"deferred_revenue_account": null,
|
||||
"delivered_by_supplier": 0,
|
||||
"delivered_qty": 0.0,
|
||||
"delivery_note": null,
|
||||
"description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
|
||||
"discount_amount": 0.0,
|
||||
"discount_percentage": 0.0,
|
||||
"dn_detail": null,
|
||||
"enable_deferred_revenue": 0,
|
||||
"expense_account": null,
|
||||
"finance_book": null,
|
||||
"image": null,
|
||||
"income_account": "Sales - _T",
|
||||
"incoming_rate": 0.0,
|
||||
"is_fixed_asset": 0,
|
||||
"is_free_item": 0,
|
||||
"item_code": null,
|
||||
"item_group": null,
|
||||
"item_name": "Dunlop tyres",
|
||||
"item_tax_rate": "{\"VAT on Sales - _T\": 20.0}",
|
||||
"item_tax_template": null,
|
||||
"margin_rate_or_amount": 0.0,
|
||||
"margin_type": "",
|
||||
"net_amount": 200.0,
|
||||
"net_rate": 50.0,
|
||||
"page_break": 0,
|
||||
"parent": null,
|
||||
"parentfield": "items",
|
||||
"parenttype": "Sales Invoice",
|
||||
"price_list_rate": 0.0,
|
||||
"pricing_rules": null,
|
||||
"project": null,
|
||||
"qty": 4.0,
|
||||
"quality_inspection": null,
|
||||
"rate": 50.0,
|
||||
"rate_with_margin": 0.0,
|
||||
"sales_invoice_item": null,
|
||||
"sales_order": null,
|
||||
"serial_no": null,
|
||||
"service_end_date": null,
|
||||
"service_start_date": null,
|
||||
"service_stop_date": null,
|
||||
"so_detail": null,
|
||||
"stock_qty": 4.0,
|
||||
"stock_uom": "Nos",
|
||||
"stock_uom_rate": 50.0,
|
||||
"target_warehouse": null,
|
||||
"total_weight": 0.0,
|
||||
"uom": "Nos",
|
||||
"warehouse": null,
|
||||
"weight_per_unit": 0.0,
|
||||
"weight_uom": null
|
||||
},
|
||||
{
|
||||
"actual_batch_qty": 0.0,
|
||||
"actual_qty": 0.0,
|
||||
"allow_zero_valuation_rate": 0,
|
||||
"amount": 65.0,
|
||||
"asset": null,
|
||||
"barcode": null,
|
||||
"base_amount": 65.0,
|
||||
"base_net_amount": 65.0,
|
||||
"base_net_rate": 65.0,
|
||||
"base_price_list_rate": 0.0,
|
||||
"base_rate": 65.0,
|
||||
"base_rate_with_margin": 0.0,
|
||||
"batch_no": null,
|
||||
"brand": null,
|
||||
"conversion_factor": 1.0,
|
||||
"cost_center": "Main - _T",
|
||||
"customer_item_code": null,
|
||||
"deferred_revenue_account": null,
|
||||
"delivered_by_supplier": 0,
|
||||
"delivered_qty": 0.0,
|
||||
"delivery_note": null,
|
||||
"description": "<div class=\"ql-editor read-mode\"><p>Used</p></div>",
|
||||
"discount_amount": 0.0,
|
||||
"discount_percentage": 0.0,
|
||||
"dn_detail": null,
|
||||
"enable_deferred_revenue": 0,
|
||||
"expense_account": null,
|
||||
"finance_book": null,
|
||||
"image": null,
|
||||
"income_account": "Sales - _T",
|
||||
"incoming_rate": 0.0,
|
||||
"is_fixed_asset": 0,
|
||||
"is_free_item": 0,
|
||||
"item_code": "",
|
||||
"item_group": null,
|
||||
"item_name": "Continental tyres",
|
||||
"item_tax_rate": "{\"VAT on Sales - _T\": 5.0}",
|
||||
"item_tax_template": null,
|
||||
"margin_rate_or_amount": 0.0,
|
||||
"margin_type": "",
|
||||
"net_amount": 65.0,
|
||||
"net_rate": 65.0,
|
||||
"page_break": 0,
|
||||
"parent": null,
|
||||
"parentfield": "items",
|
||||
"parenttype": "Sales Invoice",
|
||||
"price_list_rate": 0.0,
|
||||
"pricing_rules": null,
|
||||
"project": null,
|
||||
"qty": 1.0,
|
||||
"quality_inspection": null,
|
||||
"rate": 65.0,
|
||||
"rate_with_margin": 0.0,
|
||||
"sales_invoice_item": null,
|
||||
"sales_order": null,
|
||||
"serial_no": null,
|
||||
"service_end_date": null,
|
||||
"service_start_date": null,
|
||||
"service_stop_date": null,
|
||||
"so_detail": null,
|
||||
"stock_qty": 1.0,
|
||||
"stock_uom": null,
|
||||
"stock_uom_rate": 65.0,
|
||||
"target_warehouse": null,
|
||||
"total_weight": 0.0,
|
||||
"uom": "Nos",
|
||||
"warehouse": null,
|
||||
"weight_per_unit": 0.0,
|
||||
"weight_uom": null
|
||||
},
|
||||
{
|
||||
"actual_batch_qty": 0.0,
|
||||
"actual_qty": 0.0,
|
||||
"allow_zero_valuation_rate": 0,
|
||||
"amount": 560.0,
|
||||
"asset": null,
|
||||
"barcode": null,
|
||||
"base_amount": 560.0,
|
||||
"base_net_amount": 560.0,
|
||||
"base_net_rate": 70.0,
|
||||
"base_price_list_rate": 0.0,
|
||||
"base_rate": 70.0,
|
||||
"base_rate_with_margin": 0.0,
|
||||
"batch_no": null,
|
||||
"brand": null,
|
||||
"conversion_factor": 1.0,
|
||||
"cost_center": "Main - _T",
|
||||
"customer_item_code": null,
|
||||
"deferred_revenue_account": null,
|
||||
"delivered_by_supplier": 0,
|
||||
"delivered_qty": 0.0,
|
||||
"delivery_note": null,
|
||||
"description": "<div class=\"ql-editor read-mode\"><p>New</p></div>",
|
||||
"discount_amount": 0.0,
|
||||
"discount_percentage": 0.0,
|
||||
"dn_detail": null,
|
||||
"enable_deferred_revenue": 0,
|
||||
"expense_account": null,
|
||||
"finance_book": null,
|
||||
"image": null,
|
||||
"income_account": "Sales - _T",
|
||||
"incoming_rate": 0.0,
|
||||
"is_fixed_asset": 0,
|
||||
"is_free_item": 0,
|
||||
"item_code": null,
|
||||
"item_group": null,
|
||||
"item_name": "Toyo tyres",
|
||||
"item_tax_rate": "{\"VAT on Sales - _T\": 0.0}",
|
||||
"item_tax_template": null,
|
||||
"margin_rate_or_amount": 0.0,
|
||||
"margin_type": "",
|
||||
"net_amount": 560.0,
|
||||
"net_rate": 70.0,
|
||||
"page_break": 0,
|
||||
"parent": null,
|
||||
"parentfield": "items",
|
||||
"parenttype": "Sales Invoice",
|
||||
"price_list_rate": 0.0,
|
||||
"pricing_rules": null,
|
||||
"project": null,
|
||||
"qty": 8.0,
|
||||
"quality_inspection": null,
|
||||
"rate": 70.0,
|
||||
"rate_with_margin": 0.0,
|
||||
"sales_invoice_item": null,
|
||||
"sales_order": null,
|
||||
"serial_no": null,
|
||||
"service_end_date": null,
|
||||
"service_start_date": null,
|
||||
"service_stop_date": null,
|
||||
"so_detail": null,
|
||||
"stock_qty": 8.0,
|
||||
"stock_uom": null,
|
||||
"stock_uom_rate": 70.0,
|
||||
"target_warehouse": null,
|
||||
"total_weight": 0.0,
|
||||
"uom": "Nos",
|
||||
"warehouse": null,
|
||||
"weight_per_unit": 0.0,
|
||||
"weight_uom": null
|
||||
}
|
||||
],
|
||||
"language": "en",
|
||||
"letter_head": null,
|
||||
"loyalty_amount": 0.0,
|
||||
"loyalty_points": 0,
|
||||
"loyalty_program": null,
|
||||
"loyalty_redemption_account": null,
|
||||
"loyalty_redemption_cost_center": null,
|
||||
"modified": "2021-02-16 05:18:59.755144",
|
||||
"name": null,
|
||||
"naming_series": "ACC-SINV-.YYYY.-",
|
||||
"net_total": 825.0,
|
||||
"other_charges_calculation": "<div class=\"tax-break-up\" style=\"overflow-x: auto;\">\n\t<table class=\"table table-bordered table-hover\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-left\">Item</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">Taxable Amount</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t<th class=\"text-right\">VAT on Sales</th>\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Dunlop tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 200.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(20.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 40.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Continental tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 65.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(5.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 3.25\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t\t\t<tr>\n\t\t\t\t\t<td>Toyo tyres</td>\n\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\u00a3 560.00\n\t\t\t\t\t\t\n\t\t\t\t\t</td>\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t<td class='text-right'>\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t(0.0%)\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\u00a3 0.00\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t</tr>\n\t\t\t\n\t\t</tbody>\n\t</table>\n</div>",
|
||||
"outstanding_amount": 868.25,
|
||||
"packed_items": [],
|
||||
"paid_amount": 0.0,
|
||||
"parent": null,
|
||||
"parentfield": null,
|
||||
"parenttype": null,
|
||||
"party_account_currency": "GBP",
|
||||
"payment_schedule": [],
|
||||
"payment_terms_template": null,
|
||||
"payments": [],
|
||||
"plc_conversion_rate": 1.0,
|
||||
"po_date": null,
|
||||
"po_no": "",
|
||||
"pos_profile": null,
|
||||
"posting_date": null,
|
||||
"posting_time": "5:19:02.994077",
|
||||
"price_list_currency": "GBP",
|
||||
"pricing_rules": [],
|
||||
"project": null,
|
||||
"redeem_loyalty_points": 0,
|
||||
"remarks": "No Remarks",
|
||||
"represents_company": "",
|
||||
"return_against": null,
|
||||
"rounded_total": 868.25,
|
||||
"rounding_adjustment": 0.0,
|
||||
"sales_partner": null,
|
||||
"sales_team": [],
|
||||
"scan_barcode": null,
|
||||
"select_print_heading": null,
|
||||
"selling_price_list": "Standard Selling",
|
||||
"set_posting_time": 0,
|
||||
"set_target_warehouse": null,
|
||||
"set_warehouse": null,
|
||||
"shipping_address": null,
|
||||
"shipping_address_name": "",
|
||||
"shipping_rule": null,
|
||||
"source": null,
|
||||
"status": "Overdue",
|
||||
"tax_category": "",
|
||||
"tax_id": null,
|
||||
"taxes": [
|
||||
{
|
||||
"account_head": "VAT on Sales - _T",
|
||||
"base_tax_amount": 43.25,
|
||||
"base_tax_amount_after_discount_amount": 43.25,
|
||||
"base_total": 868.25,
|
||||
"charge_type": "On Net Total",
|
||||
"cost_center": "Main - _T",
|
||||
"description": "VAT on Sales",
|
||||
"included_in_print_rate": 0,
|
||||
"item_wise_tax_detail": "{\"Dunlop tyres\":[20.0,40.0],\"Continental tyres\":[5.0,3.25],\"Toyo tyres\":[0.0,0.0]}",
|
||||
"parent": null,
|
||||
"parentfield": "taxes",
|
||||
"parenttype": "Sales Invoice",
|
||||
"rate": 0.0,
|
||||
"row_id": null,
|
||||
"tax_amount": 43.25,
|
||||
"tax_amount_after_discount_amount": 43.25,
|
||||
"total": 868.25
|
||||
}
|
||||
],
|
||||
"taxes_and_charges": null,
|
||||
"tc_name": null,
|
||||
"terms": null,
|
||||
"territory": "All Territories",
|
||||
"timesheets": [],
|
||||
"title": "_Sales Invoice",
|
||||
"to_date": null,
|
||||
"total": 825.0,
|
||||
"total_advance": 0.0,
|
||||
"total_billing_amount": 0.0,
|
||||
"total_commission": 0.0,
|
||||
"total_net_weight": 0.0,
|
||||
"total_qty": 13.0,
|
||||
"total_taxes_and_charges": 43.25,
|
||||
"unrealized_profit_loss_account": null,
|
||||
"update_billed_amount_in_sales_order": 0,
|
||||
"update_stock": 0,
|
||||
"write_off_account": null,
|
||||
"write_off_amount": 0.0,
|
||||
"write_off_cost_center": null,
|
||||
"write_off_outstanding_amount_automatically": 0
|
||||
}
|
||||
]
|
178
erpnext/accounts/report/tax_detail/test_tax_detail.py
Normal file
178
erpnext/accounts/report/tax_detail/test_tax_detail.py
Normal file
@ -0,0 +1,178 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
from frappe.utils import getdate, add_to_date, get_first_day, get_last_day, get_year_start, get_year_ending
|
||||
from .tax_detail import filter_match, save_custom_report
|
||||
|
||||
class TestTaxDetail(unittest.TestCase):
|
||||
def load_testdocs(self):
|
||||
from erpnext.accounts.utils import get_fiscal_year, FiscalYearError
|
||||
datapath, _ = os.path.splitext(os.path.realpath(__file__))
|
||||
with open(datapath + '.json', 'r') as fp:
|
||||
docs = json.load(fp)
|
||||
|
||||
now = getdate()
|
||||
self.from_date = get_first_day(now)
|
||||
self.to_date = get_last_day(now)
|
||||
|
||||
try:
|
||||
get_fiscal_year(now, company="_T")
|
||||
except FiscalYearError:
|
||||
docs = [{
|
||||
"companies": [{
|
||||
"company": "_T",
|
||||
"parent": "_Test Fiscal",
|
||||
"parentfield": "companies",
|
||||
"parenttype": "Fiscal Year"
|
||||
}],
|
||||
"doctype": "Fiscal Year",
|
||||
"year": "_Test Fiscal",
|
||||
"year_end_date": get_year_ending(now),
|
||||
"year_start_date": get_year_start(now)
|
||||
}] + docs
|
||||
|
||||
docs = [{
|
||||
"abbr": "_T",
|
||||
"company_name": "_T",
|
||||
"country": "United Kingdom",
|
||||
"default_currency": "GBP",
|
||||
"doctype": "Company",
|
||||
"name": "_T"
|
||||
}] + docs
|
||||
|
||||
for doc in docs:
|
||||
try:
|
||||
db_doc = frappe.get_doc(doc)
|
||||
if 'Invoice' in db_doc.doctype:
|
||||
db_doc.due_date = add_to_date(now, days=1)
|
||||
db_doc.insert()
|
||||
# Create GL Entries:
|
||||
db_doc.submit()
|
||||
else:
|
||||
db_doc.insert()
|
||||
except frappe.exceptions.DuplicateEntryError:
|
||||
pass
|
||||
|
||||
def load_defcols(self):
|
||||
self.company = frappe.get_doc('Company', '_T')
|
||||
custom_report = frappe.get_doc('Report', 'Tax Detail')
|
||||
self.default_columns, _ = custom_report.run_query_report(
|
||||
filters={
|
||||
'from_date': '2021-03-01',
|
||||
'to_date': '2021-03-31',
|
||||
'company': self.company.name,
|
||||
'mode': 'run',
|
||||
'report_name': 'Tax Detail'
|
||||
}, user=frappe.session.user)
|
||||
|
||||
def rm_testdocs(self):
|
||||
"Remove the Company and all data"
|
||||
from erpnext.setup.doctype.company.delete_company_transactions import delete_company_transactions
|
||||
delete_company_transactions(self.company.name)
|
||||
self.company.delete()
|
||||
|
||||
|
||||
def test_report(self):
|
||||
self.load_testdocs()
|
||||
self.load_defcols()
|
||||
report_name = save_custom_report(
|
||||
'Tax Detail',
|
||||
'_Test Tax Detail',
|
||||
json.dumps({
|
||||
'columns': self.default_columns,
|
||||
'sections': {
|
||||
'Box1':{'Filter0':{'type':'filter','filters':{'4':'VAT on Sales'}}},
|
||||
'Box2':{'Filter0':{'type':'filter','filters':{'4':'Acquisition'}}},
|
||||
'Box3':{'Box1':{'type':'section'},'Box2':{'type':'section'}},
|
||||
'Box4':{'Filter0':{'type':'filter','filters':{'4':'VAT on Purchases'}}},
|
||||
'Box5':{'Box3':{'type':'section'},'Box4':{'type':'section'}},
|
||||
'Box6':{'Filter0':{'type':'filter','filters':{'3':'!=Tax','4':'Sales'}}},
|
||||
'Box7':{'Filter0':{'type':'filter','filters':{'2':'Expense','3':'!=Tax'}}},
|
||||
'Box8':{'Filter0':{'type':'filter','filters':{'3':'!=Tax','4':'Sales','12':'EU'}}},
|
||||
'Box9':{'Filter0':{'type':'filter','filters':{'2':'Expense','3':'!=Tax','12':'EU'}}}
|
||||
},
|
||||
'show_detail': 1
|
||||
}))
|
||||
data = frappe.desk.query_report.run(report_name,
|
||||
filters={
|
||||
'from_date': self.from_date,
|
||||
'to_date': self.to_date,
|
||||
'company': self.company.name,
|
||||
'mode': 'run',
|
||||
'report_name': report_name
|
||||
}, user=frappe.session.user)
|
||||
|
||||
self.assertListEqual(data.get('columns'), self.default_columns)
|
||||
expected = (('Box1', 43.25), ('Box2', 0.0), ('Box3', 43.25), ('Box4', -85.28), ('Box5', -42.03),
|
||||
('Box6', 825.0), ('Box7', -426.40), ('Box8', 0.0), ('Box9', 0.0))
|
||||
exrow = iter(expected)
|
||||
for row in data.get('result'):
|
||||
if row.get('voucher_no') and not row.get('posting_date'):
|
||||
label, value = next(exrow)
|
||||
self.assertDictEqual(row, {'voucher_no': label, 'amount': value})
|
||||
self.assertListEqual(data.get('report_summary'),
|
||||
[{'label': label, 'datatype': 'Currency', 'value': value} for label, value in expected])
|
||||
|
||||
self.rm_testdocs()
|
||||
|
||||
def test_filter_match(self):
|
||||
# None - treated as -inf number except range
|
||||
self.assertTrue(filter_match(None, '!='))
|
||||
self.assertTrue(filter_match(None, '<'))
|
||||
self.assertTrue(filter_match(None, '<jjj'))
|
||||
self.assertTrue(filter_match(None, ' : '))
|
||||
self.assertTrue(filter_match(None, ':56'))
|
||||
self.assertTrue(filter_match(None, ':de'))
|
||||
self.assertFalse(filter_match(None, '3.4'))
|
||||
self.assertFalse(filter_match(None, '='))
|
||||
self.assertFalse(filter_match(None, '=3.4'))
|
||||
self.assertFalse(filter_match(None, '>3.4'))
|
||||
self.assertFalse(filter_match(None, ' <'))
|
||||
self.assertFalse(filter_match(None, 'ew'))
|
||||
self.assertFalse(filter_match(None, ' '))
|
||||
self.assertFalse(filter_match(None, ' f :'))
|
||||
|
||||
# Numbers
|
||||
self.assertTrue(filter_match(3.4, '3.4'))
|
||||
self.assertTrue(filter_match(3.4, '.4'))
|
||||
self.assertTrue(filter_match(3.4, '3'))
|
||||
self.assertTrue(filter_match(-3.4, '< -3'))
|
||||
self.assertTrue(filter_match(-3.4, '> -4'))
|
||||
self.assertTrue(filter_match(3.4, '= 3.4 '))
|
||||
self.assertTrue(filter_match(3.4, '!=4.5'))
|
||||
self.assertTrue(filter_match(3.4, ' 3 : 4 '))
|
||||
self.assertTrue(filter_match(0.0, ' : '))
|
||||
self.assertFalse(filter_match(3.4, '=4.5'))
|
||||
self.assertFalse(filter_match(3.4, ' = 3.4 '))
|
||||
self.assertFalse(filter_match(3.4, '!=3.4'))
|
||||
self.assertFalse(filter_match(3.4, '>6'))
|
||||
self.assertFalse(filter_match(3.4, '<-4.5'))
|
||||
self.assertFalse(filter_match(3.4, '4.5'))
|
||||
self.assertFalse(filter_match(3.4, '5:9'))
|
||||
|
||||
# Strings
|
||||
self.assertTrue(filter_match('ACC-SINV-2021-00001', 'SINV'))
|
||||
self.assertTrue(filter_match('ACC-SINV-2021-00001', 'sinv'))
|
||||
self.assertTrue(filter_match('ACC-SINV-2021-00001', '-2021'))
|
||||
self.assertTrue(filter_match(' ACC-SINV-2021-00001', ' acc'))
|
||||
self.assertTrue(filter_match('ACC-SINV-2021-00001', '=2021'))
|
||||
self.assertTrue(filter_match('ACC-SINV-2021-00001', '!=zz'))
|
||||
self.assertTrue(filter_match('ACC-SINV-2021-00001', '< zzz '))
|
||||
self.assertTrue(filter_match('ACC-SINV-2021-00001', ' : sinv '))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' sinv :'))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' acc'))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', '= 2021 '))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', '!=sinv'))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' >'))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', '>aa'))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' <'))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', '< '))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', ' ='))
|
||||
self.assertFalse(filter_match('ACC-SINV-2021-00001', '='))
|
||||
|
||||
# Date - always match
|
||||
self.assertTrue(filter_match(datetime.date(2021, 3, 19), ' kdsjkldfs '))
|
@ -438,22 +438,34 @@
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "DATEV Export",
|
||||
"link_to": "DATEV",
|
||||
"label": "Tax Detail",
|
||||
"link_to": "Tax Detail",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "UAE VAT 201",
|
||||
"link_to": "UAE VAT 201",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "DATEV Export",
|
||||
"link_to": "DATEV",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"only_for": "Germany",
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "UAE VAT 201",
|
||||
"link_to": "UAE VAT 201",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"only_for": "United Arab Emirates",
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@ -684,6 +696,7 @@
|
||||
"is_query_report": 0,
|
||||
"label": "Goods and Services Tax (GST India)",
|
||||
"onboard": 0,
|
||||
"only_for": "India",
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
@ -1052,7 +1065,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2021-05-12 11:48:01.905144",
|
||||
"modified": "2021-05-13 13:44:56.249888",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounting",
|
||||
|
@ -195,8 +195,7 @@ class Asset(AccountsController):
|
||||
# If depreciation is already completed (for double declining balance)
|
||||
if skip_row: continue
|
||||
|
||||
depreciation_amount = self.get_depreciation_amount(value_after_depreciation,
|
||||
d.total_number_of_depreciations, d)
|
||||
depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d)
|
||||
|
||||
if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
|
||||
schedule_date = add_months(d.depreciation_start_date,
|
||||
@ -208,7 +207,7 @@ class Asset(AccountsController):
|
||||
|
||||
# For first row
|
||||
if has_pro_rata and n==0:
|
||||
depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount,
|
||||
depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
|
||||
self.available_for_use_date, d.depreciation_start_date)
|
||||
|
||||
# For first depr schedule date will be the start date
|
||||
@ -220,7 +219,7 @@ class Asset(AccountsController):
|
||||
to_date = add_months(self.available_for_use_date,
|
||||
n * cint(d.frequency_of_depreciation))
|
||||
|
||||
depreciation_amount, days, months = get_pro_rata_amt(d,
|
||||
depreciation_amount, days, months = self.get_pro_rata_amt(d,
|
||||
depreciation_amount, schedule_date, to_date)
|
||||
|
||||
monthly_schedule_date = add_months(schedule_date, 1)
|
||||
@ -365,24 +364,6 @@ class Asset(AccountsController):
|
||||
def get_value_after_depreciation(self, idx):
|
||||
return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation)
|
||||
|
||||
def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row):
|
||||
precision = self.precision("gross_purchase_amount")
|
||||
|
||||
if row.depreciation_method in ("Straight Line", "Manual"):
|
||||
depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked))
|
||||
|
||||
if not depreciation_left:
|
||||
frappe.msgprint(_("All the depreciations has been booked"))
|
||||
depreciation_amount = flt(row.expected_value_after_useful_life)
|
||||
return depreciation_amount
|
||||
|
||||
depreciation_amount = (flt(row.value_after_depreciation) -
|
||||
flt(row.expected_value_after_useful_life)) / depreciation_left
|
||||
else:
|
||||
depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision)
|
||||
|
||||
return depreciation_amount
|
||||
|
||||
def validate_expected_value_after_useful_life(self):
|
||||
for row in self.get('finance_books'):
|
||||
accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount
|
||||
@ -575,6 +556,13 @@ class Asset(AccountsController):
|
||||
|
||||
return 100 * (1 - flt(depreciation_rate, float_precision))
|
||||
|
||||
def get_pro_rata_amt(self, row, depreciation_amount, from_date, to_date):
|
||||
days = date_diff(to_date, from_date)
|
||||
months = month_diff(to_date, from_date)
|
||||
total_days = get_total_days(to_date, row.frequency_of_depreciation)
|
||||
|
||||
return (depreciation_amount * flt(days)) / flt(total_days), days, months
|
||||
|
||||
def update_maintenance_status():
|
||||
assets = frappe.get_all(
|
||||
"Asset", filters={"docstatus": 1, "maintenance_required": 1}
|
||||
@ -758,15 +746,20 @@ def make_asset_movement(assets, purpose=None):
|
||||
def is_cwip_accounting_enabled(asset_category):
|
||||
return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting"))
|
||||
|
||||
def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
|
||||
days = date_diff(to_date, from_date)
|
||||
months = month_diff(to_date, from_date)
|
||||
total_days = get_total_days(to_date, row.frequency_of_depreciation)
|
||||
|
||||
return (depreciation_amount * flt(days)) / flt(total_days), days, months
|
||||
|
||||
def get_total_days(date, frequency):
|
||||
period_start_date = add_months(date,
|
||||
cint(frequency) * -1)
|
||||
|
||||
return date_diff(date, period_start_date)
|
||||
|
||||
@erpnext.allow_regional
|
||||
def get_depreciation_amount(asset, depreciable_value, row):
|
||||
depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
|
||||
|
||||
if row.depreciation_method in ("Straight Line", "Manual"):
|
||||
depreciation_amount = (flt(row.value_after_depreciation) -
|
||||
flt(row.expected_value_after_useful_life)) / depreciation_left
|
||||
else:
|
||||
depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100))
|
||||
|
||||
return depreciation_amount
|
@ -635,6 +635,45 @@ class TestAsset(unittest.TestCase):
|
||||
frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
|
||||
frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
|
||||
|
||||
def test_discounted_wdv_depreciation_rate_for_indian_region(self):
|
||||
# set indian company
|
||||
company_flag = frappe.flags.company
|
||||
frappe.flags.company = "_Test Company"
|
||||
|
||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||
qty=1, rate=8000.0, location="Test Location")
|
||||
|
||||
asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
|
||||
asset = frappe.get_doc('Asset', asset_name)
|
||||
asset.calculate_depreciation = 1
|
||||
asset.available_for_use_date = '2030-06-12'
|
||||
asset.purchase_date = '2030-01-01'
|
||||
asset.append("finance_books", {
|
||||
"expected_value_after_useful_life": 1000,
|
||||
"depreciation_method": "Written Down Value",
|
||||
"total_number_of_depreciations": 3,
|
||||
"frequency_of_depreciation": 12,
|
||||
"depreciation_start_date": "2030-12-31"
|
||||
})
|
||||
asset.save(ignore_permissions=True)
|
||||
|
||||
self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
|
||||
|
||||
expected_schedules = [
|
||||
["2030-12-31", 1106.85, 1106.85],
|
||||
["2031-12-31", 3446.58, 4553.43],
|
||||
["2032-12-31", 1723.29, 6276.72],
|
||||
["2033-06-12", 723.28, 7000.00]
|
||||
]
|
||||
|
||||
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
|
||||
for d in asset.get("schedules")]
|
||||
|
||||
self.assertEqual(schedules, expected_schedules)
|
||||
|
||||
# reset indian company
|
||||
frappe.flags.company = company_flag
|
||||
|
||||
def create_asset_data():
|
||||
if not frappe.db.exists("Asset Category", "Computers"):
|
||||
create_asset_category()
|
||||
|
@ -4,7 +4,7 @@
|
||||
frappe.ui.form.on('Asset Category', {
|
||||
onload: function(frm) {
|
||||
frm.add_fetch('company_name', 'accumulated_depreciation_account', 'accumulated_depreciation_account');
|
||||
frm.add_fetch('company_name', 'depreciation_expense_account', 'accumulated_depreciation_account');
|
||||
frm.add_fetch('company_name', 'depreciation_expense_account', 'depreciation_expense_account');
|
||||
|
||||
frm.set_query('fixed_asset_account', 'accounts', function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
|
@ -61,8 +61,8 @@ frappe.ui.form.on("Purchase Order Item", {
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
||||
setup: function() {
|
||||
erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends erpnext.buying.BuyingController {
|
||||
setup() {
|
||||
this.frm.custom_make_buttons = {
|
||||
'Purchase Receipt': 'Purchase Receipt',
|
||||
'Purchase Invoice': 'Purchase Invoice',
|
||||
@ -70,13 +70,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
'Payment Entry': 'Payment',
|
||||
}
|
||||
|
||||
this._super();
|
||||
super.setup();
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
refresh: function(doc, cdt, cdn) {
|
||||
refresh(doc, cdt, cdn) {
|
||||
var me = this;
|
||||
this._super();
|
||||
super.refresh();
|
||||
var allow_receipt = false;
|
||||
var is_drop_ship = false;
|
||||
|
||||
@ -182,9 +182,9 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
} else if(doc.docstatus===0) {
|
||||
cur_frm.cscript.add_from_mappers();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
get_items_from_open_material_requests: function() {
|
||||
get_items_from_open_material_requests() {
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order_based_on_supplier",
|
||||
args: {
|
||||
@ -202,17 +202,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
},
|
||||
get_query_method: "erpnext.stock.doctype.material_request.material_request.get_material_requests_based_on_supplier"
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
validate: function() {
|
||||
validate() {
|
||||
set_schedule_date(this.frm);
|
||||
},
|
||||
}
|
||||
|
||||
has_unsupplied_items: function() {
|
||||
has_unsupplied_items() {
|
||||
return this.frm.doc['supplied_items'].some(item => item.required_qty != item.supplied_qty)
|
||||
},
|
||||
}
|
||||
|
||||
make_stock_entry: function() {
|
||||
make_stock_entry() {
|
||||
var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; });
|
||||
var me = this;
|
||||
|
||||
@ -326,9 +326,9 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
me.dialog.hide();
|
||||
});
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
_make_rm_stock_entry: function(rm_items) {
|
||||
_make_rm_stock_entry(rm_items) {
|
||||
frappe.call({
|
||||
method:"erpnext.buying.doctype.purchase_order.purchase_order.make_rm_stock_entry",
|
||||
args: {
|
||||
@ -341,31 +341,31 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
make_inter_company_order: function(frm) {
|
||||
make_inter_company_order(frm) {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_inter_company_sales_order",
|
||||
frm: frm
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
make_purchase_receipt: function() {
|
||||
make_purchase_receipt() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
|
||||
frm: cur_frm,
|
||||
freeze_message: __("Creating Purchase Receipt ...")
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
make_purchase_invoice: function() {
|
||||
make_purchase_invoice() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
add_from_mappers: function() {
|
||||
add_from_mappers() {
|
||||
var me = this;
|
||||
this.frm.add_custom_button(__('Material Request'),
|
||||
function() {
|
||||
@ -471,13 +471,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
}
|
||||
});
|
||||
}, __("Tools"));
|
||||
},
|
||||
}
|
||||
|
||||
tc_name: function() {
|
||||
tc_name() {
|
||||
this.get_terms();
|
||||
},
|
||||
}
|
||||
|
||||
items_add: function(doc, cdt, cdn) {
|
||||
items_add(doc, cdt, cdn) {
|
||||
var row = frappe.get_doc(cdt, cdn);
|
||||
if(doc.schedule_date) {
|
||||
row.schedule_date = doc.schedule_date;
|
||||
@ -485,13 +485,13 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
} else {
|
||||
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
unhold_purchase_order: function(){
|
||||
unhold_purchase_order(){
|
||||
cur_frm.cscript.update_status("Resume", "Draft")
|
||||
},
|
||||
}
|
||||
|
||||
hold_purchase_order: function(){
|
||||
hold_purchase_order(){
|
||||
var me = this;
|
||||
var d = new frappe.ui.Dialog({
|
||||
title: __('Reason for Hold'),
|
||||
@ -523,31 +523,31 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
}
|
||||
});
|
||||
d.show();
|
||||
},
|
||||
}
|
||||
|
||||
unclose_purchase_order: function(){
|
||||
unclose_purchase_order(){
|
||||
cur_frm.cscript.update_status('Re-open', 'Submitted')
|
||||
},
|
||||
}
|
||||
|
||||
close_purchase_order: function(){
|
||||
close_purchase_order(){
|
||||
cur_frm.cscript.update_status('Close', 'Closed')
|
||||
},
|
||||
}
|
||||
|
||||
delivered_by_supplier: function(){
|
||||
delivered_by_supplier(){
|
||||
cur_frm.cscript.update_status('Deliver', 'Delivered')
|
||||
},
|
||||
}
|
||||
|
||||
items_on_form_rendered: function() {
|
||||
set_schedule_date(this.frm);
|
||||
},
|
||||
|
||||
schedule_date: function() {
|
||||
items_on_form_rendered() {
|
||||
set_schedule_date(this.frm);
|
||||
}
|
||||
});
|
||||
|
||||
schedule_date() {
|
||||
set_schedule_date(this.frm);
|
||||
}
|
||||
};
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.buying.PurchaseOrderController({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.update_status= function(label, status){
|
||||
frappe.call({
|
||||
|
@ -205,10 +205,10 @@ frappe.ui.form.on("Request for Quotation Supplier",{
|
||||
|
||||
})
|
||||
|
||||
erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.extend({
|
||||
refresh: function() {
|
||||
erpnext.buying.RequestforQuotationController = class RequestforQuotationController extends erpnext.buying.BuyingController {
|
||||
refresh() {
|
||||
var me = this;
|
||||
this._super();
|
||||
super.refresh();
|
||||
if (this.frm.doc.docstatus===0) {
|
||||
this.frm.add_custom_button(__('Material Request'),
|
||||
function() {
|
||||
@ -302,17 +302,17 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
||||
me.get_suppliers_button(me.frm);
|
||||
}, __("Tools"));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
calculate_taxes_and_totals: function() {
|
||||
calculate_taxes_and_totals() {
|
||||
return;
|
||||
},
|
||||
}
|
||||
|
||||
tc_name: function() {
|
||||
tc_name() {
|
||||
this.get_terms();
|
||||
},
|
||||
}
|
||||
|
||||
get_suppliers_button: function (frm) {
|
||||
get_suppliers_button (frm) {
|
||||
var doc = frm.doc;
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __("Get Suppliers"),
|
||||
@ -410,8 +410,8 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.buying.RequestforQuotationController({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.buying.RequestforQuotationController({frm: cur_frm}));
|
||||
|
@ -4,19 +4,19 @@
|
||||
// attach required files
|
||||
{% include 'erpnext/public/js/controllers/buying.js' %};
|
||||
|
||||
erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({
|
||||
setup: function() {
|
||||
erpnext.buying.SupplierQuotationController = class SupplierQuotationController extends erpnext.buying.BuyingController {
|
||||
setup() {
|
||||
this.frm.custom_make_buttons = {
|
||||
'Purchase Order': 'Purchase Order',
|
||||
'Quotation': 'Quotation'
|
||||
}
|
||||
|
||||
this._super();
|
||||
},
|
||||
super.setup();
|
||||
}
|
||||
|
||||
refresh: function() {
|
||||
refresh() {
|
||||
var me = this;
|
||||
this._super();
|
||||
super.refresh();
|
||||
|
||||
if (this.frm.doc.__islocal && !this.frm.doc.valid_till) {
|
||||
this.frm.set_value('valid_till', frappe.datetime.add_months(this.frm.doc.transaction_date, 1));
|
||||
@ -77,25 +77,25 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
||||
})
|
||||
}, __("Get Items From"));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
make_purchase_order: function() {
|
||||
make_purchase_order() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
make_quotation: function() {
|
||||
}
|
||||
make_quotation() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_quotation",
|
||||
frm: cur_frm
|
||||
})
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
$.extend(cur_frm.cscript, new erpnext.buying.SupplierQuotationController({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.buying.SupplierQuotationController({frm: cur_frm}));
|
||||
|
||||
cur_frm.fields_dict['items'].grid.get_field('project').get_query =
|
||||
function(doc, cdt, cdn) {
|
||||
|
73
erpnext/change_log/v13/v13_3_0.md
Normal file
73
erpnext/change_log/v13/v13_3_0.md
Normal file
@ -0,0 +1,73 @@
|
||||
# Version 13.3.0 Release Notes
|
||||
|
||||
### Features & Enhancements
|
||||
|
||||
- Purchase receipt creation from purchase invoice ([#25126](https://github.com/frappe/erpnext/pull/25126))
|
||||
- New Document Transaction Deletion ([#25354](https://github.com/frappe/erpnext/pull/25354))
|
||||
- Employee Referral ([#24997](https://github.com/frappe/erpnext/pull/24997))
|
||||
- Add Create Expense Claim button in Delivery Trip ([#25526](https://github.com/frappe/erpnext/pull/25526))
|
||||
- Reduced rate of asset depreciation as per IT Act ([#25648](https://github.com/frappe/erpnext/pull/25648))
|
||||
- Improve DATEV export ([#25238](https://github.com/frappe/erpnext/pull/25238))
|
||||
- Add pick batch button ([#25413](https://github.com/frappe/erpnext/pull/25413))
|
||||
- Enable custom field search on POS ([#25421](https://github.com/frappe/erpnext/pull/25421))
|
||||
- New check field in subscriptions for (not) submitting invoices ([#25394](https://github.com/frappe/erpnext/pull/25394))
|
||||
- Show POS reserved stock in stock projected qty report ([#25593](https://github.com/frappe/erpnext/pull/25593))
|
||||
- e-way bill validity field ([#25555](https://github.com/frappe/erpnext/pull/25555))
|
||||
- Significant reduction in time taken to save sales documents ([#25475](https://github.com/frappe/erpnext/pull/25475))
|
||||
|
||||
### Fixes
|
||||
|
||||
- Bank statement import via google sheet ([#25677](https://github.com/frappe/erpnext/pull/25677))
|
||||
- Invoices not getting fetched during payment reconciliation ([#25598](https://github.com/frappe/erpnext/pull/25598))
|
||||
- Error on applying TDS without party ([#25632](https://github.com/frappe/erpnext/pull/25632))
|
||||
- Allow to cancel loan with cancelled repayment entry ([#25507](https://github.com/frappe/erpnext/pull/25507))
|
||||
- Can't open general ledger from consolidated financial report ([#25542](https://github.com/frappe/erpnext/pull/25542))
|
||||
- Add 'Partially Received' to Status drop-down list in Material Request ([#24857](https://github.com/frappe/erpnext/pull/24857))
|
||||
- Updated item filters for material request ([#25531](https://github.com/frappe/erpnext/pull/25531))
|
||||
- Added validation in stock entry to check duplicate serial nos ([#25611](https://github.com/frappe/erpnext/pull/25611))
|
||||
- Update shopify api version ([#25600](https://github.com/frappe/erpnext/pull/25600))
|
||||
- Dialog variable assignment after definition in POS ([#25680](https://github.com/frappe/erpnext/pull/25680))
|
||||
- Added tax_types list ([#25587](https://github.com/frappe/erpnext/pull/25587))
|
||||
- Include search fields in Project Link field query ([#25505](https://github.com/frappe/erpnext/pull/25505))
|
||||
- Item stock levels displaying inconsistently ([#25506](https://github.com/frappe/erpnext/pull/25506))
|
||||
- Change today to now to get data for reposting ([#25703](https://github.com/frappe/erpnext/pull/25703))
|
||||
- Parameter for get_filtered_list_for_consolidated_report in consolidated balance sheet ([#25700](https://github.com/frappe/erpnext/pull/25700))
|
||||
- Minor fixes in loan ([#25546](https://github.com/frappe/erpnext/pull/25546))
|
||||
- Fieldname when updating docfield property ([#25516](https://github.com/frappe/erpnext/pull/25516))
|
||||
- Use get_serial_nos for splitting ([#25590](https://github.com/frappe/erpnext/pull/25590))
|
||||
- Show item's full name on hover over item in POS ([#25554](https://github.com/frappe/erpnext/pull/25554))
|
||||
- Stock ledger entry created against draft stock entry ([#25540](https://github.com/frappe/erpnext/pull/25540))
|
||||
- Incorrect expense account set in pos invoice ([#25543](https://github.com/frappe/erpnext/pull/25543))
|
||||
- Stock balance and batch-wise balance history report showing different closing stock ([#25575](https://github.com/frappe/erpnext/pull/25575))
|
||||
- Make strings translatable ([#25521](https://github.com/frappe/erpnext/pull/25521))
|
||||
- Serial no changed after saving stock reconciliation ([#25541](https://github.com/frappe/erpnext/pull/25541))
|
||||
- Ignore fraction difference while making round off gl entry ([#25438](https://github.com/frappe/erpnext/pull/25438))
|
||||
- Sync shopify customer addresses ([#25481](https://github.com/frappe/erpnext/pull/25481))
|
||||
- Total stock summary report not working ([#25551](https://github.com/frappe/erpnext/pull/25551))
|
||||
- Rename field has not updated value of deposit and withdrawal fields ([#25545](https://github.com/frappe/erpnext/pull/25545))
|
||||
- Unexpected keyword argument 'merge_logs' ([#25489](https://github.com/frappe/erpnext/pull/25489))
|
||||
- Validation message of quality inspection in purchase receipt ([#25667](https://github.com/frappe/erpnext/pull/25667))
|
||||
- Added is_stock_item filter ([#25530](https://github.com/frappe/erpnext/pull/25530))
|
||||
- Fetch total stock at company in PO ([#25532](https://github.com/frappe/erpnext/pull/25532))
|
||||
- Updated filters for process statement of accounts ([#25384](https://github.com/frappe/erpnext/pull/25384))
|
||||
- Incorrect expense account set in pos invoice ([#25571](https://github.com/frappe/erpnext/pull/25571))
|
||||
- Client script breaking while settings tax labels ([#25653](https://github.com/frappe/erpnext/pull/25653))
|
||||
- Empty payment term column in accounts receivable report ([#25556](https://github.com/frappe/erpnext/pull/25556))
|
||||
- Designation insufficient permission on lead doctype. ([#25331](https://github.com/frappe/erpnext/pull/25331))
|
||||
- Force https for shopify webhook registration ([#25630](https://github.com/frappe/erpnext/pull/25630))
|
||||
- Patch regional fields for old companies ([#25673](https://github.com/frappe/erpnext/pull/25673))
|
||||
- Woocommerce order sync issue ([#25692](https://github.com/frappe/erpnext/pull/25692))
|
||||
- Allow to receive same serial numbers multiple times ([#25471](https://github.com/frappe/erpnext/pull/25471))
|
||||
- Update Allocated amount after Paid Amount is changed in PE ([#25515](https://github.com/frappe/erpnext/pull/25515))
|
||||
- Updating Standard Notification's channel field ([#25564](https://github.com/frappe/erpnext/pull/25564))
|
||||
- Report summary showing inflated values when values are accumulated in Group Company ([#25577](https://github.com/frappe/erpnext/pull/25577))
|
||||
- UI fixes related to overflowing payment section ([#25652](https://github.com/frappe/erpnext/pull/25652))
|
||||
- List invoices in Payment Reconciliation Payment ([#25524](https://github.com/frappe/erpnext/pull/25524))
|
||||
- Ageing errors in PSOA ([#25490](https://github.com/frappe/erpnext/pull/25490))
|
||||
- Prevent spurious defaults for items when making prec from dnote ([#25559](https://github.com/frappe/erpnext/pull/25559))
|
||||
- Stock reconciliation getting time out error during submission ([#25557](https://github.com/frappe/erpnext/pull/25557))
|
||||
- Timesheet filter date exclusive issue ([#25626](https://github.com/frappe/erpnext/pull/25626))
|
||||
- Update cost center in the item table fetched from POS Profile ([#25609](https://github.com/frappe/erpnext/pull/25609))
|
||||
- Updated modified time in purchase invoice to pull new fields ([#25678](https://github.com/frappe/erpnext/pull/25678))
|
||||
- Stock and Accounts Settings form refactor ([#25534](https://github.com/frappe/erpnext/pull/25534))
|
||||
- Payment amount showing in foreign currency ([#25292](https://github.com/frappe/erpnext/pull/25292))
|
@ -838,9 +838,10 @@ class BuyingController(StockController):
|
||||
if not self.get("items"):
|
||||
return
|
||||
|
||||
earliest_schedule_date = min([d.schedule_date for d in self.get("items")])
|
||||
if earliest_schedule_date:
|
||||
self.schedule_date = earliest_schedule_date
|
||||
if any(d.schedule_date for d in self.get("items")):
|
||||
# Select earliest schedule_date.
|
||||
self.schedule_date = min(d.schedule_date for d in self.get("items")
|
||||
if d.schedule_date is not None)
|
||||
|
||||
if self.schedule_date:
|
||||
for d in self.get('items'):
|
||||
|
@ -292,11 +292,14 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
||||
cond = """(`tabProject`.customer = %s or
|
||||
ifnull(`tabProject`.customer,"")="") and""" %(frappe.db.escape(filters.get("customer")))
|
||||
|
||||
fields = get_fields("Project", ["name"])
|
||||
fields = get_fields("Project", ["name", "project_name"])
|
||||
searchfields = frappe.get_meta("Project").get_search_fields()
|
||||
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
|
||||
|
||||
return frappe.db.sql("""select {fields} from `tabProject`
|
||||
where `tabProject`.status not in ("Completed", "Cancelled")
|
||||
and {cond} `tabProject`.name like %(txt)s {match_cond}
|
||||
where
|
||||
`tabProject`.status not in ("Completed", "Cancelled")
|
||||
and {cond} {match_cond} {scond}
|
||||
order by
|
||||
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
|
||||
idx desc,
|
||||
@ -304,6 +307,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
||||
limit {start}, {page_len}""".format(
|
||||
fields=", ".join(['`tabProject`.{0}'.format(f) for f in fields]),
|
||||
cond=cond,
|
||||
scond=searchfields,
|
||||
match_cond=get_match_cond(doctype),
|
||||
start=start,
|
||||
page_len=page_len), {
|
||||
|
@ -100,6 +100,10 @@ status_map = {
|
||||
["Queued", "eval:self.status == 'Queued'"],
|
||||
["Failed", "eval:self.status == 'Failed'"],
|
||||
["Cancelled", "eval:self.docstatus == 2"],
|
||||
],
|
||||
"Transaction Deletion Record": [
|
||||
["Draft", None],
|
||||
["Completed", "eval:self.docstatus == 1"],
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -379,8 +379,7 @@ class StockController(AccountsController):
|
||||
link = frappe.utils.get_link_to_form('Quality Inspection', d.quality_inspection)
|
||||
frappe.throw(_("Quality Inspection: {0} is not submitted for the item: {1} in row {2}").format(link, d.item_code, d.idx), QualityInspectionNotSubmittedError)
|
||||
|
||||
qa_failed = any([r.status=="Rejected" for r in qa_doc.readings])
|
||||
if qa_failed:
|
||||
if qa_doc.status != 'Accepted':
|
||||
frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
|
||||
.format(d.idx, d.item_code), QualityInspectionRejectedError)
|
||||
elif qa_required :
|
||||
|
@ -4,8 +4,8 @@
|
||||
frappe.provide("erpnext");
|
||||
cur_frm.email_field = "email_id";
|
||||
|
||||
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
setup: function () {
|
||||
erpnext.LeadController = class LeadController extends frappe.ui.form.Controller {
|
||||
setup () {
|
||||
this.frm.make_methods = {
|
||||
'Customer': this.make_customer,
|
||||
'Quotation': this.make_quotation,
|
||||
@ -13,9 +13,9 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
};
|
||||
|
||||
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||
},
|
||||
}
|
||||
|
||||
onload: function () {
|
||||
onload () {
|
||||
this.frm.set_query("customer", function (doc, cdt, cdn) {
|
||||
return { query: "erpnext.controllers.queries.customer_query" }
|
||||
});
|
||||
@ -27,9 +27,9 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
this.frm.set_query("contact_by", function (doc, cdt, cdn) {
|
||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
refresh: function () {
|
||||
refresh () {
|
||||
let doc = this.frm.doc;
|
||||
erpnext.toggle_naming_series();
|
||||
frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
|
||||
@ -45,47 +45,47 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
} else {
|
||||
frappe.contacts.clear_address_and_contact(this.frm);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
make_customer: function () {
|
||||
make_customer () {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
make_opportunity: function () {
|
||||
make_opportunity () {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
make_quotation: function () {
|
||||
make_quotation () {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
organization_lead: function () {
|
||||
organization_lead () {
|
||||
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||
this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
|
||||
},
|
||||
}
|
||||
|
||||
company_name: function () {
|
||||
company_name () {
|
||||
if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) {
|
||||
this.frm.set_value("lead_name", this.frm.doc.company_name);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
contact_date: function () {
|
||||
contact_date () {
|
||||
if (this.frm.doc.contact_date) {
|
||||
let d = moment(this.frm.doc.contact_date);
|
||||
d.add(1, "day");
|
||||
this.frm.set_value("ends_on", d.format(frappe.defaultDatetimeFormat));
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
|
||||
|
@ -145,8 +145,8 @@ frappe.ui.form.on("Opportunity", {
|
||||
})
|
||||
|
||||
// TODO commonify this code
|
||||
erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
onload: function() {
|
||||
erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller {
|
||||
onload() {
|
||||
|
||||
if(!this.frm.doc.status) {
|
||||
frm.set_value('status', 'Open');
|
||||
@ -159,9 +159,9 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
}
|
||||
|
||||
this.setup_queries();
|
||||
},
|
||||
}
|
||||
|
||||
setup_queries: function() {
|
||||
setup_queries() {
|
||||
var me = this;
|
||||
|
||||
if(this.frm.fields_dict.contact_by.df.options.match(/^User/)) {
|
||||
@ -185,17 +185,17 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
else if (me.frm.doc.opportunity_from == "Customer") {
|
||||
me.frm.set_query('party_name', erpnext.queries['customer']);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
create_quotation: function() {
|
||||
create_quotation() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation",
|
||||
frm: cur_frm
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
@ -213,4 +213,4 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,8 +72,8 @@ frappe.ui.form.on('Student Attendance Tool', {
|
||||
});
|
||||
|
||||
|
||||
education.StudentsEditor = Class.extend({
|
||||
init: function(frm, wrapper, students) {
|
||||
education.StudentsEditor = class StudentsEditor {
|
||||
constructor(frm, wrapper, students) {
|
||||
this.wrapper = wrapper;
|
||||
this.frm = frm;
|
||||
if(students.length > 0) {
|
||||
@ -81,8 +81,8 @@ education.StudentsEditor = Class.extend({
|
||||
} else {
|
||||
this.show_empty_state();
|
||||
}
|
||||
},
|
||||
make: function(frm, students) {
|
||||
}
|
||||
make(frm, students) {
|
||||
var me = this;
|
||||
|
||||
$(this.wrapper).empty();
|
||||
@ -173,13 +173,13 @@ education.StudentsEditor = Class.extend({
|
||||
});
|
||||
|
||||
$(htmls.join("")).appendTo(me.wrapper);
|
||||
},
|
||||
}
|
||||
|
||||
show_empty_state: function() {
|
||||
show_empty_state() {
|
||||
$(this.wrapper).html(
|
||||
`<div class="text-center text-muted" style="line-height: 100px;">
|
||||
${__("No Students in")} ${this.frm.doc.student_group}
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -335,13 +335,13 @@ def get_url(shopify_settings):
|
||||
|
||||
if not last_order_id:
|
||||
if shopify_settings.sync_based_on == 'Date':
|
||||
url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&created_at_min={0}&since_id=0".format(
|
||||
url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&created_at_min={0}&since_id=0".format(
|
||||
get_datetime(shopify_settings.from_date)), shopify_settings)
|
||||
else:
|
||||
url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(
|
||||
url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(
|
||||
shopify_settings.from_order_id), shopify_settings)
|
||||
else:
|
||||
url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
|
||||
url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
|
||||
|
||||
return url
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, base64, hashlib, hmac, json
|
||||
from frappe.utils import cstr
|
||||
from frappe import _
|
||||
|
||||
def verify_request():
|
||||
@ -146,22 +147,19 @@ def rename_address(address, customer):
|
||||
|
||||
def link_items(items_list, woocommerce_settings, sys_lang):
|
||||
for item_data in items_list:
|
||||
item_woo_com_id = item_data.get("product_id")
|
||||
item_woo_com_id = cstr(item_data.get("product_id"))
|
||||
|
||||
if frappe.get_value("Item", {"woocommerce_id": item_woo_com_id}):
|
||||
#Edit Item
|
||||
item = frappe.get_doc("Item", {"woocommerce_id": item_woo_com_id})
|
||||
else:
|
||||
if not frappe.db.get_value("Item", {"woocommerce_id": item_woo_com_id}, 'name'):
|
||||
#Create Item
|
||||
item = frappe.new_doc("Item")
|
||||
item.item_code = _("woocommerce - {0}", sys_lang).format(item_woo_com_id)
|
||||
item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang)
|
||||
item.item_group = _("WooCommerce Products", sys_lang)
|
||||
|
||||
item.item_name = item_data.get("name")
|
||||
item.item_code = _("woocommerce - {0}", sys_lang).format(item_data.get("product_id"))
|
||||
item.woocommerce_id = item_data.get("product_id")
|
||||
item.item_group = _("WooCommerce Products", sys_lang)
|
||||
item.stock_uom = woocommerce_settings.uom or _("Nos", sys_lang)
|
||||
item.flags.ignore_mandatory = True
|
||||
item.save()
|
||||
item.item_name = item_data.get("name")
|
||||
item.woocommerce_id = item_woo_com_id
|
||||
item.flags.ignore_mandatory = True
|
||||
item.save()
|
||||
|
||||
def create_sales_order(order, woocommerce_settings, customer_name, sys_lang):
|
||||
new_sales_order = frappe.new_doc("Sales Order")
|
||||
@ -194,12 +192,12 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l
|
||||
|
||||
for item in order.get("line_items"):
|
||||
woocomm_item_id = item.get("product_id")
|
||||
found_item = frappe.get_doc("Item", {"woocommerce_id": woocomm_item_id})
|
||||
found_item = frappe.get_doc("Item", {"woocommerce_id": cstr(woocomm_item_id)})
|
||||
|
||||
ordered_items_tax = item.get("total_tax")
|
||||
|
||||
new_sales_order.append("items",{
|
||||
"item_code": found_item.item_code,
|
||||
new_sales_order.append("items", {
|
||||
"item_code": found_item.name,
|
||||
"item_name": found_item.item_name,
|
||||
"description": found_item.item_name,
|
||||
"delivery_date": new_sales_order.delivery_date,
|
||||
@ -207,7 +205,7 @@ def set_items_in_sales_order(new_sales_order, woocommerce_settings, order, sys_l
|
||||
"qty": item.get("quantity"),
|
||||
"rate": item.get("price"),
|
||||
"warehouse": woocommerce_settings.warehouse or default_warehouse
|
||||
})
|
||||
})
|
||||
|
||||
add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account)
|
||||
|
||||
|
@ -30,14 +30,14 @@ class ShopifySettings(Document):
|
||||
webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
|
||||
# url = get_shopify_url('admin/webhooks.json', self)
|
||||
created_webhooks = [d.method for d in self.webhooks]
|
||||
url = get_shopify_url('admin/api/2020-04/webhooks.json', self)
|
||||
url = get_shopify_url('admin/api/2021-04/webhooks.json', self)
|
||||
for method in webhooks:
|
||||
session = get_request_session()
|
||||
try:
|
||||
res = session.post(url, data=json.dumps({
|
||||
"webhook": {
|
||||
"topic": method,
|
||||
"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'),
|
||||
"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data', force_https=True),
|
||||
"format": "json"
|
||||
}
|
||||
}), headers=get_header(self))
|
||||
@ -56,7 +56,7 @@ class ShopifySettings(Document):
|
||||
deleted_webhooks = []
|
||||
|
||||
for d in self.webhooks:
|
||||
url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self)
|
||||
url = get_shopify_url('admin/api/2021-04/webhooks/{0}.json'.format(d.webhook_id), self)
|
||||
try:
|
||||
res = session.delete(url, headers=get_header(self))
|
||||
res.raise_for_status()
|
||||
|
@ -32,10 +32,12 @@ def create_customer(shopify_customer, shopify_settings):
|
||||
raise e
|
||||
|
||||
def create_customer_address(customer, shopify_customer):
|
||||
if not shopify_customer.get("addresses"):
|
||||
return
|
||||
addresses = shopify_customer.get("addresses", [])
|
||||
|
||||
for i, address in enumerate(shopify_customer.get("addresses")):
|
||||
if not addresses and "default_address" in shopify_customer:
|
||||
addresses.append(shopify_customer["default_address"])
|
||||
|
||||
for i, address in enumerate(addresses):
|
||||
address_title, address_type = get_address_title_and_type(customer.customer_name, i)
|
||||
try :
|
||||
frappe.get_doc({
|
||||
|
@ -8,7 +8,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo
|
||||
shopify_variants_attr_list = ["option1", "option2", "option3"]
|
||||
|
||||
def sync_item_from_shopify(shopify_settings, item):
|
||||
url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
|
||||
url = get_shopify_url("admin/api/2021-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
|
||||
session = get_request_session()
|
||||
|
||||
try:
|
||||
|
@ -28,7 +28,7 @@ def validate_webhooks_request(doctype, hmac_key, secret_key='secret'):
|
||||
|
||||
return innerfn
|
||||
|
||||
def get_webhook_address(connector_name, method, exclude_uri=False):
|
||||
def get_webhook_address(connector_name, method, exclude_uri=False, force_https=False):
|
||||
endpoint = "erpnext.erpnext_integrations.connectors.{0}.{1}".format(connector_name, method)
|
||||
|
||||
if exclude_uri:
|
||||
@ -39,7 +39,11 @@ def get_webhook_address(connector_name, method, exclude_uri=False):
|
||||
except RuntimeError:
|
||||
url = "http://localhost:8000"
|
||||
|
||||
server_url = '{uri.scheme}://{uri.netloc}/api/method/{endpoint}'.format(uri=urlparse(url), endpoint=endpoint)
|
||||
url_data = urlparse(url)
|
||||
scheme = "https" if force_https else url_data.scheme
|
||||
netloc = url_data.netloc
|
||||
|
||||
server_url = f"{scheme}://{netloc}/api/method/{endpoint}"
|
||||
|
||||
return server_url
|
||||
|
||||
|
@ -9,14 +9,14 @@ frappe.ui.form.on('Exercise Type', {
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.ExerciseEditor = Class.extend({
|
||||
init: function(frm, wrapper) {
|
||||
erpnext.ExerciseEditor = class ExerciseEditor {
|
||||
constructor(frm, wrapper) {
|
||||
this.wrapper = wrapper;
|
||||
this.frm = frm;
|
||||
this.make(frm, wrapper);
|
||||
},
|
||||
}
|
||||
|
||||
make: function(frm, wrapper) {
|
||||
make(frm, wrapper) {
|
||||
$(this.wrapper).empty();
|
||||
|
||||
this.exercise_toolbar = $('<p>\
|
||||
@ -38,9 +38,9 @@ erpnext.ExerciseEditor = Class.extend({
|
||||
this.make_cards(frm);
|
||||
this.make_buttons(frm);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
make_cards: function(frm) {
|
||||
make_cards(frm) {
|
||||
var me = this;
|
||||
$(me.exercise_cards).empty();
|
||||
|
||||
@ -60,9 +60,9 @@ erpnext.ExerciseEditor = Class.extend({
|
||||
</div>
|
||||
</div>`, {image_src: step.image, title: step.title, description: step.description, col_id: "col-"+i, card_id: "card-"+i, id: i})).appendTo(me.row);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
make_buttons: function(frm) {
|
||||
make_buttons(frm) {
|
||||
let me = this;
|
||||
$('.btn-edit').on('click', function() {
|
||||
let id = $(this).attr('data-id');
|
||||
@ -82,9 +82,9 @@ erpnext.ExerciseEditor = Class.extend({
|
||||
frm.dirty();
|
||||
}, 300);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
show_add_card_dialog: function(frm) {
|
||||
show_add_card_dialog(frm) {
|
||||
let me = this;
|
||||
let d = new frappe.ui.Dialog({
|
||||
title: __('Add Exercise Step'),
|
||||
@ -137,9 +137,9 @@ erpnext.ExerciseEditor = Class.extend({
|
||||
primary_action_label: __('Add')
|
||||
});
|
||||
d.show();
|
||||
},
|
||||
}
|
||||
|
||||
show_edit_card_dialog: function(frm, id) {
|
||||
show_edit_card_dialog(frm, id) {
|
||||
let new_dialog = new frappe.ui.Dialog({
|
||||
title: __("Edit Exercise Step"),
|
||||
fields: [
|
||||
@ -183,4 +183,4 @@ erpnext.ExerciseEditor = Class.extend({
|
||||
});
|
||||
new_dialog.show();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -15,10 +15,11 @@ app_logo_url = "/assets/erpnext/images/erpnext-logo.svg"
|
||||
|
||||
develop_version = '13.x.x-develop'
|
||||
|
||||
app_include_js = "/assets/js/erpnext.min.js"
|
||||
app_include_css = "/assets/css/erpnext.css"
|
||||
web_include_js = "/assets/js/erpnext-web.min.js"
|
||||
web_include_css = "/assets/css/erpnext-web.css"
|
||||
app_include_js = "erpnext.bundle.js"
|
||||
app_include_css = "erpnext.bundle.css"
|
||||
web_include_js = "erpnext-web.bundle.js"
|
||||
web_include_css = "erpnext-web.bundle.css"
|
||||
email_css = "email_erpnext.bundle.css"
|
||||
|
||||
doctype_js = {
|
||||
"Address": "public/js/address.js",
|
||||
@ -426,7 +427,8 @@ regional_overrides = {
|
||||
'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
|
||||
'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
|
||||
'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries',
|
||||
'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields'
|
||||
'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields',
|
||||
'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount'
|
||||
},
|
||||
'United Arab Emirates': {
|
||||
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
|
||||
|
@ -182,6 +182,10 @@
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Sales User"
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
@ -191,4 +195,4 @@
|
||||
"track_changes": 0,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
frappe.provide("erpnext.hr");
|
||||
erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
|
||||
setup: function() {
|
||||
erpnext.hr.EmployeeController = class EmployeeController extends frappe.ui.form.Controller {
|
||||
setup() {
|
||||
this.frm.fields_dict.user_id.get_query = function(doc, cdt, cdn) {
|
||||
return {
|
||||
query: "frappe.core.doctype.user.user.user_query",
|
||||
@ -12,30 +12,30 @@ erpnext.hr.EmployeeController = frappe.ui.form.Controller.extend({
|
||||
}
|
||||
this.frm.fields_dict.reports_to.get_query = function(doc, cdt, cdn) {
|
||||
return { query: "erpnext.controllers.queries.employee_query"} }
|
||||
},
|
||||
}
|
||||
|
||||
refresh: function() {
|
||||
refresh() {
|
||||
var me = this;
|
||||
erpnext.toggle_naming_series();
|
||||
},
|
||||
}
|
||||
|
||||
date_of_birth: function() {
|
||||
date_of_birth() {
|
||||
return cur_frm.call({
|
||||
method: "get_retirement_date",
|
||||
args: {date_of_birth: this.frm.doc.date_of_birth}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
salutation: function() {
|
||||
salutation() {
|
||||
if(this.frm.doc.salutation) {
|
||||
this.frm.set_value("gender", {
|
||||
"Mr": "Male",
|
||||
"Ms": "Female"
|
||||
}[this.frm.doc.salutation]);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
frappe.ui.form.on('Employee',{
|
||||
setup: function(frm) {
|
||||
frm.set_query("leave_policy", function() {
|
||||
|
@ -68,13 +68,13 @@ erpnext.employee_attendance_tool = {
|
||||
}
|
||||
}
|
||||
|
||||
erpnext.MarkedEmployee = Class.extend({
|
||||
init: function(frm, wrapper, employee) {
|
||||
erpnext.MarkedEmployee = class MarkedEmployee {
|
||||
constructor(frm, wrapper, employee) {
|
||||
this.wrapper = wrapper;
|
||||
this.frm = frm;
|
||||
this.make(frm, employee);
|
||||
},
|
||||
make: function(frm, employee) {
|
||||
}
|
||||
make(frm, employee) {
|
||||
var me = this;
|
||||
$(this.wrapper).empty();
|
||||
|
||||
@ -104,16 +104,16 @@ erpnext.MarkedEmployee = Class.extend({
|
||||
})).appendTo(row);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
erpnext.EmployeeSelector = Class.extend({
|
||||
init: function(frm, wrapper, employee) {
|
||||
erpnext.EmployeeSelector = class EmployeeSelector {
|
||||
constructor(frm, wrapper, employee) {
|
||||
this.wrapper = wrapper;
|
||||
this.frm = frm;
|
||||
this.make(frm, employee);
|
||||
},
|
||||
make: function(frm, employee) {
|
||||
}
|
||||
make(frm, employee) {
|
||||
var me = this;
|
||||
|
||||
$(this.wrapper).empty();
|
||||
@ -266,6 +266,6 @@ erpnext.EmployeeSelector = Class.extend({
|
||||
|
||||
mark_employee_toolbar.appendTo($(this.wrapper));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
"column_break_5",
|
||||
"expense_approver",
|
||||
"approval_status",
|
||||
"delivery_trip",
|
||||
"is_paid",
|
||||
"expense_details",
|
||||
"expenses",
|
||||
@ -365,13 +366,20 @@
|
||||
"label": "Total Taxes and Charges",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.delivery_trip",
|
||||
"fieldname": "delivery_trip",
|
||||
"fieldtype": "Link",
|
||||
"label": "Delivery Trip",
|
||||
"options": "Delivery Trip"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-money",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"modified": "2021-05-04 05:35:12.040199",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Expense Claim",
|
||||
|
@ -5,19 +5,19 @@
|
||||
|
||||
frappe.provide("erpnext.hr");
|
||||
|
||||
erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
|
||||
onload: function() {
|
||||
erpnext.hr.AttendanceControlPanel = class AttendanceControlPanel extends frappe.ui.form.Controller {
|
||||
onload() {
|
||||
this.frm.set_value("att_fr_date", frappe.datetime.get_today());
|
||||
this.frm.set_value("att_to_date", frappe.datetime.get_today());
|
||||
},
|
||||
}
|
||||
|
||||
refresh: function() {
|
||||
refresh() {
|
||||
this.frm.disable_save();
|
||||
this.show_upload();
|
||||
this.setup_import_progress();
|
||||
},
|
||||
}
|
||||
|
||||
get_template:function() {
|
||||
get_template() {
|
||||
if(!this.frm.doc.att_fr_date || !this.frm.doc.att_to_date) {
|
||||
frappe.msgprint(__("Attendance From Date and Attendance To Date is mandatory"));
|
||||
return;
|
||||
@ -28,7 +28,7 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
|
||||
from_date: this.frm.doc.att_fr_date,
|
||||
to_date: this.frm.doc.att_to_date,
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
show_upload() {
|
||||
var $wrapper = $(cur_frm.fields_dict.upload_html.wrapper).empty();
|
||||
@ -36,7 +36,7 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
|
||||
wrapper: $wrapper,
|
||||
method: 'erpnext.hr.doctype.upload_attendance.upload_attendance.upload'
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
setup_import_progress() {
|
||||
var $log_wrapper = $(this.frm.fields_dict.import_log.wrapper).empty();
|
||||
@ -64,6 +64,6 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
cur_frm.cscript = new erpnext.hr.AttendanceControlPanel({frm: cur_frm});
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"attach_print": 0,
|
||||
"channel": "Email",
|
||||
"creation": "2017-08-11 03:17:11.769210",
|
||||
"days_in_advance": 0,
|
||||
"docstatus": 0,
|
||||
|
@ -45,8 +45,8 @@ frappe.ui.form.on('Maintenance Schedule', {
|
||||
})
|
||||
|
||||
// TODO commonify this code
|
||||
erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
|
||||
refresh: function() {
|
||||
erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frappe.ui.form.Controller {
|
||||
refresh() {
|
||||
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
|
||||
|
||||
var me = this;
|
||||
@ -76,21 +76,21 @@ erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
|
||||
});
|
||||
}, __('Create'));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
start_date: function(doc, cdt, cdn) {
|
||||
start_date(doc, cdt, cdn) {
|
||||
this.set_no_of_visits(doc, cdt, cdn);
|
||||
},
|
||||
}
|
||||
|
||||
end_date: function(doc, cdt, cdn) {
|
||||
end_date(doc, cdt, cdn) {
|
||||
this.set_no_of_visits(doc, cdt, cdn);
|
||||
},
|
||||
}
|
||||
|
||||
periodicity: function(doc, cdt, cdn) {
|
||||
periodicity(doc, cdt, cdn) {
|
||||
this.set_no_of_visits(doc, cdt, cdn);
|
||||
},
|
||||
}
|
||||
|
||||
set_no_of_visits: function(doc, cdt, cdn) {
|
||||
set_no_of_visits(doc, cdt, cdn) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
|
||||
if (item.start_date && item.end_date && item.periodicity) {
|
||||
@ -112,8 +112,8 @@ erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
|
||||
var no_of_visits = cint(date_diff / days_in_period[item.periodicity]);
|
||||
frappe.model.set_value(item.doctype, item.name, "no_of_visits", no_of_visits);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm}));
|
||||
|
||||
|
@ -41,8 +41,8 @@ frappe.ui.form.on('Maintenance Visit', {
|
||||
})
|
||||
|
||||
// TODO commonify this code
|
||||
erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({
|
||||
refresh: function() {
|
||||
erpnext.maintenance.MaintenanceVisit = class MaintenanceVisit extends frappe.ui.form.Controller {
|
||||
refresh() {
|
||||
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
|
||||
|
||||
var me = this;
|
||||
@ -96,7 +96,7 @@ erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({
|
||||
})
|
||||
}, __("Get Items From"));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.maintenance.MaintenanceVisit({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceVisit({frm: cur_frm}));
|
||||
|
@ -29,7 +29,10 @@ frappe.ui.form.on("BOM", {
|
||||
|
||||
frm.set_query("item", function() {
|
||||
return {
|
||||
query: "erpnext.manufacturing.doctype.bom.bom.item_query"
|
||||
query: "erpnext.manufacturing.doctype.bom.bom.item_query",
|
||||
filters: {
|
||||
"is_stock_item": 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@ -357,16 +360,16 @@ frappe.ui.form.on("BOM", {
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.bom.BomController = erpnext.TransactionController.extend({
|
||||
conversion_rate: function(doc) {
|
||||
erpnext.bom.BomController = class BomController extends erpnext.TransactionController {
|
||||
conversion_rate(doc) {
|
||||
if(this.frm.doc.currency === this.get_company_currency()) {
|
||||
this.frm.set_value("conversion_rate", 1.0);
|
||||
} else {
|
||||
erpnext.bom.update_cost(doc);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
item_code: function(doc, cdt, cdn){
|
||||
item_code(doc, cdt, cdn){
|
||||
var scrap_items = false;
|
||||
var child = locals[cdt][cdn];
|
||||
if (child.doctype == 'BOM Scrap Item') {
|
||||
@ -378,19 +381,19 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
|
||||
}
|
||||
|
||||
get_bom_material_detail(doc, cdt, cdn, scrap_items);
|
||||
},
|
||||
}
|
||||
|
||||
buying_price_list: function(doc) {
|
||||
buying_price_list(doc) {
|
||||
this.apply_price_list();
|
||||
},
|
||||
}
|
||||
|
||||
plc_conversion_rate: function(doc) {
|
||||
plc_conversion_rate(doc) {
|
||||
if (!this.in_apply_price_list) {
|
||||
this.apply_price_list(null, true);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
conversion_factor: function(doc, cdt, cdn) {
|
||||
conversion_factor(doc, cdt, cdn) {
|
||||
if (frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
|
||||
@ -399,10 +402,10 @@ erpnext.bom.BomController = erpnext.TransactionController.extend({
|
||||
this.toggle_conversion_factor(item);
|
||||
this.frm.events.update_cost(this.frm);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm}));
|
||||
extend_cscript(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.hour_rate = function(doc) {
|
||||
erpnext.bom.calculate_op_cost(doc);
|
||||
@ -659,4 +662,4 @@ frappe.ui.form.on("BOM", "with_operations", function(frm) {
|
||||
frm.set_value("operations", []);
|
||||
}
|
||||
toggle_operations(frm);
|
||||
});
|
||||
});
|
||||
|
@ -973,6 +973,9 @@ def item_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
if not has_variants:
|
||||
query_filters["has_variants"] = 0
|
||||
|
||||
if filters and filters.get("is_stock_item"):
|
||||
query_filters["is_stock_item"] = 1
|
||||
|
||||
return frappe.get_all("Item",
|
||||
fields = fields, filters=query_filters,
|
||||
or_filters = or_cond_filters, order_by=order_by,
|
||||
|
@ -774,4 +774,8 @@ erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing
|
||||
erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021
|
||||
erpnext.patches.v13_0.update_shipment_status
|
||||
erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
|
||||
erpnext.patches.v12_0.add_ewaybill_validity_field
|
||||
erpnext.patches.v13_0.germany_make_custom_fields
|
||||
erpnext.patches.v13_0.germany_fill_debtor_creditor_number
|
||||
erpnext.patches.v13_0.set_pos_closing_as_failed
|
||||
erpnext.patches.v13_0.update_timesheet_changes
|
||||
|
16
erpnext/patches/v12_0/add_ewaybill_validity_field.py
Normal file
16
erpnext/patches/v12_0/add_ewaybill_validity_field.py
Normal file
@ -0,0 +1,16 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
||||
|
||||
def execute():
|
||||
company = frappe.get_all('Company', filters = {'country': 'India'})
|
||||
if not company:
|
||||
return
|
||||
|
||||
custom_fields = {
|
||||
'Sales Invoice': [
|
||||
dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
|
||||
depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill')
|
||||
]
|
||||
}
|
||||
create_custom_fields(custom_fields, update=True)
|
@ -44,9 +44,11 @@ def execute():
|
||||
# make current item's tax map
|
||||
item_tax_map = {}
|
||||
for d in old_item_taxes[item_code]:
|
||||
item_tax_map[d.tax_type] = d.tax_rate
|
||||
if d.tax_type not in item_tax_map:
|
||||
item_tax_map[d.tax_type] = d.tax_rate
|
||||
|
||||
item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code)
|
||||
tax_types = []
|
||||
item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code, tax_types=tax_types)
|
||||
|
||||
# update the item tax table
|
||||
frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code)
|
||||
@ -68,7 +70,7 @@ def execute():
|
||||
and item_tax_template is NULL""".format(dt), as_dict=1):
|
||||
item_tax_map = json.loads(d.item_tax_rate)
|
||||
item_tax_template_name = get_item_tax_template(item_tax_templates,
|
||||
item_tax_map, d.item_code, d.parenttype, d.parent)
|
||||
item_tax_map, d.item_code, d.parenttype, d.parent, tax_types=tax_types)
|
||||
frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name)
|
||||
|
||||
frappe.db.auto_commit_on_many_writes = False
|
||||
@ -78,7 +80,7 @@ def execute():
|
||||
settings.determine_address_tax_category_from = "Billing Address"
|
||||
settings.save()
|
||||
|
||||
def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None):
|
||||
def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None, tax_types=None):
|
||||
# search for previously created item tax template by comparing tax maps
|
||||
for template, item_tax_template_map in iteritems(item_tax_templates):
|
||||
if item_tax_map == item_tax_template_map:
|
||||
@ -126,7 +128,9 @@ def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttyp
|
||||
account_type = frappe.get_cached_value("Account", tax_type, "account_type")
|
||||
|
||||
if tax_type and account_type in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'):
|
||||
item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
|
||||
if tax_type not in tax_types:
|
||||
item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate})
|
||||
tax_types.append(tax_type)
|
||||
item_tax_templates.setdefault(item_tax_template.title, {})
|
||||
item_tax_templates[item_tax_template.title][tax_type] = tax_rate
|
||||
if item_tax_template.get("taxes"):
|
||||
|
31
erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
Normal file
31
erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright (c) 2019, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
"""Move account number into the new custom field debtor_creditor_number.
|
||||
|
||||
German companies used to use a dedicated payable/receivable account for
|
||||
every party to mimick party accounts in the external accounting software
|
||||
"DATEV". This is no longer necessary. The reference ID for DATEV will be
|
||||
stored in a new custom field "debtor_creditor_number".
|
||||
"""
|
||||
company_list = frappe.get_all('Company', filters={'country': 'Germany'})
|
||||
|
||||
for company in company_list:
|
||||
party_account_list = frappe.get_all('Party Account', filters={'company': company.name}, fields=['name', 'account', 'debtor_creditor_number'])
|
||||
for party_account in party_account_list:
|
||||
if (not party_account.account) or party_account.debtor_creditor_number:
|
||||
# account empty or debtor_creditor_number already filled
|
||||
continue
|
||||
|
||||
account_number = frappe.db.get_value('Account', party_account.account, 'account_number')
|
||||
if not account_number:
|
||||
continue
|
||||
|
||||
frappe.db.set_value('Party Account', party_account.name, 'debtor_creditor_number', account_number)
|
||||
frappe.db.set_value('Party Account', party_account.name, 'account', '')
|
20
erpnext/patches/v13_0/germany_make_custom_fields.py
Normal file
20
erpnext/patches/v13_0/germany_make_custom_fields.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright (c) 2019, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from erpnext.regional.germany.setup import make_custom_fields
|
||||
|
||||
|
||||
def execute():
|
||||
"""Execute the make_custom_fields method for german companies.
|
||||
|
||||
It is usually run once at setup of a new company. Since it's new, run it
|
||||
once for existing companies as well.
|
||||
"""
|
||||
company_list = frappe.get_all('Company', filters = {'country': 'Germany'})
|
||||
if not company_list:
|
||||
return
|
||||
|
||||
make_custom_fields()
|
25
erpnext/patches/v13_0/update_timesheet_changes.py
Normal file
25
erpnext/patches/v13_0/update_timesheet_changes.py
Normal file
@ -0,0 +1,25 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.utils.rename_field import rename_field
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("projects", "doctype", "timesheet")
|
||||
frappe.reload_doc("projects", "doctype", "timesheet_detail")
|
||||
|
||||
if frappe.db.has_column("Timesheet Detail", "billable"):
|
||||
rename_field("Timesheet Detail", "billable", "is_billable")
|
||||
|
||||
base_currency = frappe.defaults.get_global_default('currency')
|
||||
|
||||
frappe.db.sql("""UPDATE `tabTimesheet Detail`
|
||||
SET base_billing_rate = billing_rate,
|
||||
base_billing_amount = billing_amount,
|
||||
base_costing_rate = costing_rate,
|
||||
base_costing_amount = costing_amount""")
|
||||
|
||||
frappe.db.sql("""UPDATE `tabTimesheet`
|
||||
SET currency = '{0}',
|
||||
exchange_rate = 1.0,
|
||||
base_total_billable_amount = total_billable_amount,
|
||||
base_total_billed_amount = total_billed_amount,
|
||||
base_total_costing_amount = total_costing_amount""".format(base_currency))
|
@ -51,7 +51,7 @@ def execute():
|
||||
|
||||
def get_timelog_data(data):
|
||||
return {
|
||||
'billable': data.billable,
|
||||
'is_billable': data.billable,
|
||||
'from_time': data.from_time,
|
||||
'hours': data.hours,
|
||||
'to_time': data.to_time,
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"attach_print": 0,
|
||||
"channel": "Email",
|
||||
"condition": "doc.docstatus==1",
|
||||
"creation": "2018-05-15 18:52:36.362838",
|
||||
"date_changed": "bonus_payment_date",
|
||||
|
@ -1,4 +1,8 @@
|
||||
frappe.ui.form.on("Activity Type", {
|
||||
onload: function(frm) {
|
||||
frm.set_currency_labels(["billing_rate", "costing_rate"], frappe.defaults.get_global_default('currency'));
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.add_custom_button(__("Activity Cost per Employee"), function() {
|
||||
frappe.route_options = {"activity_type": frm.doc.name};
|
||||
|
@ -458,7 +458,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"max_attachments": 4,
|
||||
"modified": "2020-09-02 11:54:01.223620",
|
||||
"modified": "2021-04-28 16:36:11.654632",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Project",
|
||||
@ -495,11 +495,11 @@
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"search_fields": "customer, status, priority, is_active",
|
||||
"search_fields": "project_name,customer, status, priority, is_active",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"timeline_field": "customer",
|
||||
"title_field": "project_name",
|
||||
"track_seen": 1
|
||||
}
|
||||
}
|
@ -37,7 +37,7 @@ class TestTimesheet(unittest.TestCase):
|
||||
emp = make_employee("test_employee_6@salary.com")
|
||||
|
||||
make_salary_structure_for_timesheet(emp)
|
||||
timesheet = make_timesheet(emp, simulate=True, billable=1)
|
||||
timesheet = make_timesheet(emp, simulate=True, is_billable=1)
|
||||
|
||||
self.assertEqual(timesheet.total_hours, 2)
|
||||
self.assertEqual(timesheet.total_billable_hours, 2)
|
||||
@ -49,7 +49,7 @@ class TestTimesheet(unittest.TestCase):
|
||||
emp = make_employee("test_employee_6@salary.com")
|
||||
|
||||
make_salary_structure_for_timesheet(emp)
|
||||
timesheet = make_timesheet(emp, simulate=True, billable=0)
|
||||
timesheet = make_timesheet(emp, simulate=True, is_billable=0)
|
||||
|
||||
self.assertEqual(timesheet.total_hours, 2)
|
||||
self.assertEqual(timesheet.total_billable_hours, 0)
|
||||
@ -61,7 +61,7 @@ class TestTimesheet(unittest.TestCase):
|
||||
emp = make_employee("test_employee_6@salary.com", company="_Test Company")
|
||||
|
||||
salary_structure = make_salary_structure_for_timesheet(emp)
|
||||
timesheet = make_timesheet(emp, simulate = True, billable=1)
|
||||
timesheet = make_timesheet(emp, simulate = True, is_billable=1)
|
||||
salary_slip = make_salary_slip(timesheet.name)
|
||||
salary_slip.submit()
|
||||
|
||||
@ -82,7 +82,7 @@ class TestTimesheet(unittest.TestCase):
|
||||
def test_sales_invoice_from_timesheet(self):
|
||||
emp = make_employee("test_employee_6@salary.com")
|
||||
|
||||
timesheet = make_timesheet(emp, simulate=True, billable=1)
|
||||
timesheet = make_timesheet(emp, simulate=True, is_billable=1)
|
||||
sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer')
|
||||
sales_invoice.due_date = nowdate()
|
||||
sales_invoice.submit()
|
||||
@ -100,7 +100,7 @@ class TestTimesheet(unittest.TestCase):
|
||||
emp = make_employee("test_employee_6@salary.com")
|
||||
project = frappe.get_value("Project", {"project_name": "_Test Project"})
|
||||
|
||||
timesheet = make_timesheet(emp, simulate=True, billable=1, project=project, company='_Test Company')
|
||||
timesheet = make_timesheet(emp, simulate=True, is_billable=1, project=project, company='_Test Company')
|
||||
sales_invoice = create_sales_invoice(do_not_save=True)
|
||||
sales_invoice.project = project
|
||||
sales_invoice.submit()
|
||||
@ -171,13 +171,13 @@ def make_salary_structure_for_timesheet(employee, company=None):
|
||||
return salary_structure
|
||||
|
||||
|
||||
def make_timesheet(employee, simulate=False, billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
|
||||
def make_timesheet(employee, simulate=False, is_billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
|
||||
update_activity_type(activity_type)
|
||||
timesheet = frappe.new_doc("Timesheet")
|
||||
timesheet.employee = employee
|
||||
timesheet.company = company or '_Test Company'
|
||||
timesheet_detail = timesheet.append('time_logs', {})
|
||||
timesheet_detail.billable = billable
|
||||
timesheet_detail.is_billable = is_billable
|
||||
timesheet_detail.activity_type = activity_type
|
||||
timesheet_detail.from_time = now_datetime()
|
||||
timesheet_detail.hours = 2
|
||||
|
@ -90,17 +90,99 @@ frappe.ui.form.on("Timesheet", {
|
||||
}
|
||||
if(frm.doc.per_billed > 0) {
|
||||
frm.fields_dict["time_logs"].grid.toggle_enable("billing_hours", false);
|
||||
frm.fields_dict["time_logs"].grid.toggle_enable("billable", false);
|
||||
frm.fields_dict["time_logs"].grid.toggle_enable("is_billable", false);
|
||||
}
|
||||
frm.trigger('setup_filters');
|
||||
frm.trigger('set_dynamic_field_label');
|
||||
},
|
||||
|
||||
customer: function(frm) {
|
||||
frm.set_query('parent_project', function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
"customer": doc.customer
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query('project', 'time_logs', function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
"customer": doc.customer
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.refresh();
|
||||
},
|
||||
|
||||
currency: function(frm) {
|
||||
let base_currency = frappe.defaults.get_global_default('currency');
|
||||
if (base_currency != frm.doc.currency) {
|
||||
frappe.call({
|
||||
method: "erpnext.setup.utils.get_exchange_rate",
|
||||
args: {
|
||||
from_currency: frm.doc.currency,
|
||||
to_currency: base_currency
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frm.set_value('exchange_rate', flt(r.message));
|
||||
frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + " = [?] " + base_currency);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
frm.trigger('set_dynamic_field_label');
|
||||
},
|
||||
|
||||
exchange_rate: function(frm) {
|
||||
$.each(frm.doc.time_logs, function(i, d) {
|
||||
calculate_billing_costing_amount(frm, d.doctype, d.name);
|
||||
});
|
||||
calculate_time_and_amount(frm);
|
||||
},
|
||||
|
||||
set_dynamic_field_label: function(frm) {
|
||||
let base_currency = frappe.defaults.get_global_default('currency');
|
||||
frm.set_currency_labels(["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"], base_currency);
|
||||
frm.set_currency_labels(["total_costing_amount", "total_billable_amount", "total_billed_amount"], frm.doc.currency);
|
||||
|
||||
frm.toggle_display(["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"],
|
||||
frm.doc.currency != base_currency);
|
||||
|
||||
if (frm.doc.time_logs.length > 0) {
|
||||
frm.set_currency_labels(["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], base_currency, "time_logs");
|
||||
frm.set_currency_labels(["billing_rate", "billing_amount", "costing_rate", "costing_amount"], frm.doc.currency, "time_logs");
|
||||
|
||||
let time_logs_grid = frm.fields_dict.time_logs.grid;
|
||||
$.each(["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], function(i, d) {
|
||||
if (frappe.meta.get_docfield(time_logs_grid.doctype, d))
|
||||
time_logs_grid.set_column_disp(d, frm.doc.currency != base_currency);
|
||||
});
|
||||
}
|
||||
frm.refresh_fields();
|
||||
},
|
||||
|
||||
make_invoice: function(frm) {
|
||||
let fields = [{
|
||||
"fieldtype": "Link",
|
||||
"label": __("Item Code"),
|
||||
"fieldname": "item_code",
|
||||
"options": "Item"
|
||||
}];
|
||||
|
||||
if (!frm.doc.customer) {
|
||||
fields.push({
|
||||
"fieldtype": "Link",
|
||||
"label": __("Customer"),
|
||||
"fieldname": "customer",
|
||||
"options": "Customer",
|
||||
"default": frm.doc.customer
|
||||
});
|
||||
}
|
||||
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __("Select Item (optional)"),
|
||||
fields: [
|
||||
{"fieldtype": "Link", "label": __("Item Code"), "fieldname": "item_code", "options":"Item"},
|
||||
{"fieldtype": "Link", "label": __("Customer"), "fieldname": "customer", "options":"Customer"}
|
||||
]
|
||||
title: __("Create Sales Invoice"),
|
||||
fields: fields
|
||||
});
|
||||
|
||||
dialog.set_primary_action(__('Create Sales Invoice'), () => {
|
||||
@ -113,7 +195,8 @@ frappe.ui.form.on("Timesheet", {
|
||||
args: {
|
||||
"source_name": frm.doc.name,
|
||||
"item_code": args.item_code,
|
||||
"customer": args.customer
|
||||
"customer": frm.doc.customer || args.customer,
|
||||
"currency": frm.doc.currency
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
@ -136,8 +219,7 @@ frappe.ui.form.on("Timesheet", {
|
||||
|
||||
parent_project: function(frm) {
|
||||
set_project_in_timelog(frm);
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Timesheet Detail", {
|
||||
@ -171,36 +253,34 @@ frappe.ui.form.on("Timesheet Detail", {
|
||||
if(frm.doc.parent_project) {
|
||||
frappe.model.set_value(cdt, cdn, 'project', frm.doc.parent_project);
|
||||
}
|
||||
|
||||
var $trigger_again = $('.form-grid').find('.grid-row').find('.btn-open-row');
|
||||
$trigger_again.on('click', () => {
|
||||
let $timer = $('.form-grid').find('[data-fieldname="timer"]');
|
||||
if ($timer.get(0)) {
|
||||
$timer.append(frappe.render_template("timesheet"));
|
||||
}
|
||||
frm.trigger("control_timer");
|
||||
});
|
||||
},
|
||||
|
||||
hours: function(frm, cdt, cdn) {
|
||||
calculate_end_time(frm, cdt, cdn);
|
||||
calculate_billing_costing_amount(frm, cdt, cdn);
|
||||
calculate_time_and_amount(frm);
|
||||
},
|
||||
|
||||
billing_hours: function(frm, cdt, cdn) {
|
||||
calculate_billing_costing_amount(frm, cdt, cdn);
|
||||
calculate_time_and_amount(frm);
|
||||
},
|
||||
|
||||
billing_rate: function(frm, cdt, cdn) {
|
||||
calculate_billing_costing_amount(frm, cdt, cdn);
|
||||
calculate_time_and_amount(frm);
|
||||
},
|
||||
|
||||
costing_rate: function(frm, cdt, cdn) {
|
||||
calculate_billing_costing_amount(frm, cdt, cdn);
|
||||
calculate_time_and_amount(frm);
|
||||
},
|
||||
|
||||
billable: function(frm, cdt, cdn) {
|
||||
is_billable: function(frm, cdt, cdn) {
|
||||
update_billing_hours(frm, cdt, cdn);
|
||||
update_time_rates(frm, cdt, cdn);
|
||||
calculate_billing_costing_amount(frm, cdt, cdn);
|
||||
calculate_time_and_amount(frm);
|
||||
},
|
||||
|
||||
activity_type: function(frm, cdt, cdn) {
|
||||
@ -208,7 +288,8 @@ frappe.ui.form.on("Timesheet Detail", {
|
||||
method: "erpnext.projects.doctype.timesheet.timesheet.get_activity_cost",
|
||||
args: {
|
||||
employee: frm.doc.employee,
|
||||
activity_type: frm.selected_doc.activity_type
|
||||
activity_type: frm.selected_doc.activity_type,
|
||||
currency: frm.doc.currency
|
||||
},
|
||||
callback: function(r){
|
||||
if(r.message){
|
||||
@ -240,9 +321,9 @@ var calculate_end_time = function(frm, cdt, cdn) {
|
||||
}
|
||||
};
|
||||
|
||||
var update_billing_hours = function(frm, cdt, cdn){
|
||||
var child = locals[cdt][cdn];
|
||||
if(!child.billable) {
|
||||
var update_billing_hours = function(frm, cdt, cdn) {
|
||||
let child = frappe.get_doc(cdt, cdn);
|
||||
if (!child.is_billable) {
|
||||
frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0);
|
||||
} else {
|
||||
// bill all hours by default
|
||||
@ -250,40 +331,44 @@ var update_billing_hours = function(frm, cdt, cdn){
|
||||
}
|
||||
};
|
||||
|
||||
var update_time_rates = function(frm, cdt, cdn){
|
||||
var child = locals[cdt][cdn];
|
||||
if(!child.billable){
|
||||
var update_time_rates = function(frm, cdt, cdn) {
|
||||
let child = frappe.get_doc(cdt, cdn);
|
||||
if (!child.is_billable) {
|
||||
frappe.model.set_value(cdt, cdn, 'billing_rate', 0.0);
|
||||
}
|
||||
};
|
||||
|
||||
var calculate_billing_costing_amount = function(frm, cdt, cdn){
|
||||
var child = locals[cdt][cdn];
|
||||
var billing_amount = 0.0;
|
||||
var costing_amount = 0.0;
|
||||
|
||||
if(child.billing_hours && child.billable){
|
||||
billing_amount = (child.billing_hours * child.billing_rate);
|
||||
var calculate_billing_costing_amount = function(frm, cdt, cdn) {
|
||||
let row = frappe.get_doc(cdt, cdn);
|
||||
let billing_amount = 0.0;
|
||||
let base_billing_amount = 0.0;
|
||||
let exchange_rate = flt(frm.doc.exchange_rate);
|
||||
frappe.model.set_value(cdt, cdn, 'base_billing_rate', flt(row.billing_rate) * exchange_rate);
|
||||
frappe.model.set_value(cdt, cdn, 'base_costing_rate', flt(row.costing_rate) * exchange_rate);
|
||||
if (row.billing_hours && row.is_billable) {
|
||||
base_billing_amount = flt(row.billing_hours) * flt(row.base_billing_rate);
|
||||
billing_amount = flt(row.billing_hours) * flt(row.billing_rate);
|
||||
}
|
||||
costing_amount = flt(child.costing_rate * child.hours);
|
||||
|
||||
frappe.model.set_value(cdt, cdn, 'base_billing_amount', base_billing_amount);
|
||||
frappe.model.set_value(cdt, cdn, 'base_costing_amount', flt(row.base_costing_rate) * flt(row.hours));
|
||||
frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount);
|
||||
frappe.model.set_value(cdt, cdn, 'costing_amount', costing_amount);
|
||||
calculate_time_and_amount(frm);
|
||||
frappe.model.set_value(cdt, cdn, 'costing_amount', flt(row.costing_rate) * flt(row.hours));
|
||||
};
|
||||
|
||||
var calculate_time_and_amount = function(frm) {
|
||||
var tl = frm.doc.time_logs || [];
|
||||
var total_working_hr = 0;
|
||||
var total_billing_hr = 0;
|
||||
var total_billable_amount = 0;
|
||||
var total_costing_amount = 0;
|
||||
let tl = frm.doc.time_logs || [];
|
||||
let total_working_hr = 0;
|
||||
let total_billing_hr = 0;
|
||||
let total_billable_amount = 0;
|
||||
let total_costing_amount = 0;
|
||||
for(var i=0; i<tl.length; i++) {
|
||||
if (tl[i].hours) {
|
||||
total_working_hr += tl[i].hours;
|
||||
total_billable_amount += tl[i].billing_amount;
|
||||
total_costing_amount += tl[i].costing_amount;
|
||||
|
||||
if(tl[i].billable){
|
||||
if (tl[i].is_billable) {
|
||||
total_billing_hr += tl[i].billing_hours;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,9 @@
|
||||
"title",
|
||||
"naming_series",
|
||||
"company",
|
||||
"customer",
|
||||
"currency",
|
||||
"exchange_rate",
|
||||
"sales_invoice",
|
||||
"column_break_3",
|
||||
"salary_slip",
|
||||
@ -30,11 +33,14 @@
|
||||
"total_hours",
|
||||
"billing_details",
|
||||
"total_billable_hours",
|
||||
"total_billed_hours",
|
||||
"total_costing_amount",
|
||||
"base_total_billable_amount",
|
||||
"base_total_billed_amount",
|
||||
"base_total_costing_amount",
|
||||
"column_break_10",
|
||||
"total_billed_hours",
|
||||
"total_billable_amount",
|
||||
"total_billed_amount",
|
||||
"total_costing_amount",
|
||||
"per_billed",
|
||||
"section_break_18",
|
||||
"note",
|
||||
@ -176,7 +182,6 @@
|
||||
"default": "0",
|
||||
"fieldname": "total_hours",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Total Working Hours",
|
||||
"read_only": 1
|
||||
},
|
||||
@ -199,7 +204,6 @@
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "total_billed_hours",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Total Billed Hours",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@ -209,6 +213,7 @@
|
||||
"fieldname": "total_costing_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Costing Amount",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@ -222,6 +227,7 @@
|
||||
"fieldname": "total_billable_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Billable Amount",
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@ -229,6 +235,7 @@
|
||||
"fieldname": "total_billed_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Billed Amount",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@ -236,6 +243,7 @@
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "per_billed",
|
||||
"fieldtype": "Percent",
|
||||
"in_list_view": 1,
|
||||
"label": "% Amount Billed",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
@ -265,13 +273,53 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
"options": "Project"
|
||||
},
|
||||
{
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"label": "Customer",
|
||||
"options": "Customer"
|
||||
},
|
||||
{
|
||||
"fetch_from": "customer.default_currency",
|
||||
"fetch_if_empty": 1,
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
"options": "Currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "base_total_costing_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Costing Amount",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "base_total_billable_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Billable Amount",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "base_total_billed_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Billed Amount",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "Exchange Rate"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-clock-o",
|
||||
"idx": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-01-08 20:51:14.590080",
|
||||
"modified": "2021-05-18 16:10:08.249619",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Projects",
|
||||
"name": "Timesheet",
|
||||
|
@ -14,6 +14,7 @@ from frappe.model.document import Document
|
||||
from erpnext.manufacturing.doctype.workstation.workstation import (check_if_within_operating_hours,
|
||||
WorkstationHolidayError)
|
||||
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
|
||||
class OverlapError(frappe.ValidationError): pass
|
||||
class OverWorkLoggedError(frappe.ValidationError): pass
|
||||
@ -37,9 +38,9 @@ class Timesheet(Document):
|
||||
self.total_hours = 0.0
|
||||
self.total_billable_hours = 0.0
|
||||
self.total_billed_hours = 0.0
|
||||
self.total_billable_amount = 0.0
|
||||
self.total_costing_amount = 0.0
|
||||
self.total_billed_amount = 0.0
|
||||
self.total_billable_amount = self.base_total_billable_amount = 0.0
|
||||
self.total_costing_amount = self.base_total_costing_amount = 0.0
|
||||
self.total_billed_amount = self.base_total_billed_amount = 0.0
|
||||
|
||||
for d in self.get("time_logs"):
|
||||
self.update_billing_hours(d)
|
||||
@ -47,10 +48,13 @@ class Timesheet(Document):
|
||||
|
||||
self.total_hours += flt(d.hours)
|
||||
self.total_costing_amount += flt(d.costing_amount)
|
||||
if d.billable:
|
||||
self.base_total_costing_amount += flt(d.base_costing_amount)
|
||||
if d.is_billable:
|
||||
self.total_billable_hours += flt(d.billing_hours)
|
||||
self.total_billable_amount += flt(d.billing_amount)
|
||||
self.base_total_billable_amount += flt(d.base_billing_amount)
|
||||
self.total_billed_amount += flt(d.billing_amount) if d.sales_invoice else 0.0
|
||||
self.base_total_billed_amount += flt(d.base_billing_amount) if d.sales_invoice else 0.0
|
||||
self.total_billed_hours += flt(d.billing_hours) if d.sales_invoice else 0.0
|
||||
|
||||
def calculate_percentage_billed(self):
|
||||
@ -59,7 +63,7 @@ class Timesheet(Document):
|
||||
self.per_billed = (self.total_billed_amount * 100) / self.total_billable_amount
|
||||
|
||||
def update_billing_hours(self, args):
|
||||
if args.billable:
|
||||
if args.is_billable:
|
||||
if flt(args.billing_hours) == 0.0:
|
||||
args.billing_hours = args.hours
|
||||
else:
|
||||
@ -133,16 +137,20 @@ class Timesheet(Document):
|
||||
def validate_time_logs(self):
|
||||
for data in self.get('time_logs'):
|
||||
self.validate_overlap(data)
|
||||
self.validate_task_project()
|
||||
self.set_project(data)
|
||||
self.validate_project(data)
|
||||
|
||||
def validate_overlap(self, data):
|
||||
settings = frappe.get_single('Projects Settings')
|
||||
self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)
|
||||
self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap)
|
||||
|
||||
def validate_task_project(self):
|
||||
for log in self.time_logs:
|
||||
log.project = log.project or frappe.db.get_value("Task", log.task, "project")
|
||||
def set_project(self, data):
|
||||
data.project = data.project or frappe.db.get_value("Task", data.task, "project")
|
||||
|
||||
def validate_project(self, data):
|
||||
if self.parent_project and self.parent_project != data.project:
|
||||
frappe.throw(_("Row {0}: Project must be same as the one set in the Timesheet: {1}.").format(data.idx, self.parent_project))
|
||||
|
||||
def validate_overlap_for(self, fieldname, args, value, ignore_validation=False):
|
||||
if not value or ignore_validation:
|
||||
@ -189,7 +197,7 @@ class Timesheet(Document):
|
||||
|
||||
def update_cost(self):
|
||||
for data in self.time_logs:
|
||||
if data.activity_type or data.billable:
|
||||
if data.activity_type or data.is_billable:
|
||||
rate = get_activity_cost(self.employee, data.activity_type)
|
||||
hours = data.billing_hours or 0
|
||||
costing_hours = data.billing_hours or data.hours or 0
|
||||
@ -200,20 +208,29 @@ class Timesheet(Document):
|
||||
data.costing_amount = data.costing_rate * costing_hours
|
||||
|
||||
def update_time_rates(self, ts_detail):
|
||||
if not ts_detail.billable:
|
||||
if not ts_detail.is_billable:
|
||||
ts_detail.billing_rate = 0.0
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_projectwise_timesheet_data(project, parent=None, from_time=None, to_time=None):
|
||||
def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to_time=None):
|
||||
condition = ''
|
||||
if project:
|
||||
condition += "and tsd.project = %(project)s"
|
||||
if parent:
|
||||
condition = "AND parent = %(parent)s"
|
||||
condition += "AND tsd.parent = %(parent)s"
|
||||
if from_time and to_time:
|
||||
condition += "AND from_time BETWEEN %(from_time)s AND %(to_time)s"
|
||||
condition += "AND CAST(tsd.from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
|
||||
|
||||
return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
|
||||
from `tabTimesheet Detail` where parenttype = 'Timesheet' and docstatus=1 and project = %(project)s {0} and billable = 1
|
||||
and sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1)
|
||||
return frappe.db.sql("""SELECT tsd.name as name,
|
||||
tsd.parent as parent, tsd.billing_hours as billing_hours,
|
||||
tsd.billing_amount as billing_amount, tsd.activity_type as activity_type,
|
||||
tsd.description as description, ts.currency as currency
|
||||
FROM `tabTimesheet Detail` tsd
|
||||
INNER JOIN `tabTimesheet` ts ON ts.name = tsd.parent
|
||||
WHERE tsd.parenttype = 'Timesheet'
|
||||
and tsd.docstatus=1 {0}
|
||||
and tsd.is_billable = 1
|
||||
and tsd.sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1)
|
||||
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
@ -250,7 +267,7 @@ def get_timesheet_data(name, project):
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_sales_invoice(source_name, item_code=None, customer=None):
|
||||
def make_sales_invoice(source_name, item_code=None, customer=None, currency=None):
|
||||
target = frappe.new_doc("Sales Invoice")
|
||||
timesheet = frappe.get_doc('Timesheet', source_name)
|
||||
|
||||
@ -268,6 +285,9 @@ def make_sales_invoice(source_name, item_code=None, customer=None):
|
||||
if customer:
|
||||
target.customer = customer
|
||||
|
||||
if currency:
|
||||
target.currency = currency
|
||||
|
||||
if item_code:
|
||||
target.append('items', {
|
||||
'item_code': item_code,
|
||||
@ -275,11 +295,16 @@ def make_sales_invoice(source_name, item_code=None, customer=None):
|
||||
'rate': billing_rate
|
||||
})
|
||||
|
||||
target.append('timesheets', {
|
||||
'time_sheet': timesheet.name,
|
||||
'billing_hours': hours,
|
||||
'billing_amount': billing_amount
|
||||
})
|
||||
for time_log in timesheet.time_logs:
|
||||
if time_log.is_billable:
|
||||
target.append('timesheets', {
|
||||
'time_sheet': timesheet.name,
|
||||
'billing_hours': time_log.billing_hours,
|
||||
'billing_amount': time_log.billing_amount,
|
||||
'timesheet_detail': time_log.name,
|
||||
'activity_type': time_log.activity_type,
|
||||
'description': time_log.description
|
||||
})
|
||||
|
||||
target.run_method("calculate_billing_amount_for_timesheet")
|
||||
target.run_method("set_missing_values")
|
||||
@ -309,12 +334,17 @@ def set_missing_values(time_sheet, target):
|
||||
})
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_activity_cost(employee=None, activity_type=None):
|
||||
def get_activity_cost(employee=None, activity_type=None, currency=None):
|
||||
base_currency = frappe.defaults.get_global_default('currency')
|
||||
rate = frappe.db.get_values("Activity Cost", {"employee": employee,
|
||||
"activity_type": activity_type}, ["costing_rate", "billing_rate"], as_dict=True)
|
||||
if not rate:
|
||||
rate = frappe.db.get_values("Activity Type", {"activity_type": activity_type},
|
||||
["costing_rate", "billing_rate"], as_dict=True)
|
||||
if rate and currency and currency!=base_currency:
|
||||
exchange_rate = get_exchange_rate(base_currency, currency)
|
||||
rate[0]["costing_rate"] = rate[0]["costing_rate"] * exchange_rate
|
||||
rate[0]["billing_rate"] = rate[0]["billing_rate"] * exchange_rate
|
||||
|
||||
return rate[0] if rate else {}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -126,7 +126,7 @@ def get_timesheet_details(filters, timesheet_list):
|
||||
timesheet_details = frappe.get_all(
|
||||
"Timesheet Detail",
|
||||
filters = timesheet_details_filter,
|
||||
fields=["from_time", "to_time", "hours", "billable", "billing_hours", "billing_rate", "parent"]
|
||||
fields=["from_time", "to_time", "hours", "is_billable", "billing_hours", "billing_rate", "parent"]
|
||||
)
|
||||
|
||||
timesheet_details_map = frappe._dict()
|
||||
@ -139,7 +139,7 @@ def get_billable_and_total_duration(activity, start_time, end_time):
|
||||
precision = frappe.get_precision("Timesheet Detail", "hours")
|
||||
activity_duration = time_diff_in_hours(end_time, start_time)
|
||||
billing_duration = 0.0
|
||||
if activity.billable:
|
||||
if activity.is_billable:
|
||||
billing_duration = activity.billing_hours
|
||||
if activity_duration != activity.billing_hours:
|
||||
billing_duration = activity_duration * activity.billing_hours / activity.hours
|
||||
|
@ -140,7 +140,7 @@ class EmployeeHoursReport:
|
||||
additional_filters += f"AND tt.{field} = '{self.filters.get(field)}'"
|
||||
|
||||
self.filtered_time_logs = frappe.db.sql('''
|
||||
SELECT tt.employee AS employee, ttd.hours AS hours, ttd.billable AS billable, ttd.project AS project
|
||||
SELECT tt.employee AS employee, ttd.hours AS hours, ttd.is_billable AS is_billable, ttd.project AS project
|
||||
FROM `tabTimesheet Detail` AS ttd
|
||||
JOIN `tabTimesheet` AS tt
|
||||
ON ttd.parent = tt.name
|
||||
@ -153,14 +153,14 @@ class EmployeeHoursReport:
|
||||
def generate_stats_by_employee(self):
|
||||
self.stats_by_employee = frappe._dict()
|
||||
|
||||
for emp, hours, billable, project in self.filtered_time_logs:
|
||||
for emp, hours, is_billable, project in self.filtered_time_logs:
|
||||
self.stats_by_employee.setdefault(
|
||||
emp, frappe._dict()
|
||||
).setdefault('billed_hours', 0.0)
|
||||
|
||||
self.stats_by_employee[emp].setdefault('non_billed_hours', 0.0)
|
||||
|
||||
if billable:
|
||||
if is_billable:
|
||||
self.stats_by_employee[emp]['billed_hours'] += flt(hours, 2)
|
||||
else:
|
||||
self.stats_by_employee[emp]['non_billed_hours'] += flt(hours, 2)
|
||||
|
@ -31,7 +31,7 @@ class TestEmployeeUtilization(unittest.TestCase):
|
||||
timesheet1.append("time_logs", {
|
||||
"activity_type": get_random("Activity Type"),
|
||||
"hours": 5,
|
||||
"billable": 1,
|
||||
"is_billable": 1,
|
||||
"from_time": '2021-04-01 13:30:00.000000',
|
||||
"to_time": '2021-04-01 18:30:00.000000'
|
||||
})
|
||||
@ -46,7 +46,7 @@ class TestEmployeeUtilization(unittest.TestCase):
|
||||
timesheet2.append("time_logs", {
|
||||
"activity_type": get_random("Activity Type"),
|
||||
"hours": 10,
|
||||
"billable": 0,
|
||||
"is_billable": 0,
|
||||
"from_time": '2021-04-01 13:30:00.000000',
|
||||
"to_time": '2021-04-01 23:30:00.000000',
|
||||
"project": cls.test_project.name
|
||||
|
@ -4,6 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
@ -52,8 +53,8 @@ def get_rows(filters):
|
||||
|
||||
def calculate_cost_and_profit(data):
|
||||
for row in data:
|
||||
row.fractional_cost = row.base_gross_pay * row.utilization
|
||||
row.profit = row.base_grand_total - row.base_gross_pay * row.utilization
|
||||
row.fractional_cost = flt(row.base_gross_pay) * flt(row.utilization)
|
||||
row.profit = flt(row.base_grand_total) - flt(row.base_gross_pay) * flt(row.utilization)
|
||||
return data
|
||||
|
||||
def get_conditions(filters):
|
||||
|
@ -14,7 +14,7 @@ class TestProjectProfitability(unittest.TestCase):
|
||||
if not frappe.db.exists('Salary Component', 'Timesheet Component'):
|
||||
frappe.get_doc({'doctype': 'Salary Component', 'salary_component': 'Timesheet Component'}).insert()
|
||||
make_salary_structure_for_timesheet(emp, company='_Test Company')
|
||||
self.timesheet = make_timesheet(emp, simulate = True, billable=1)
|
||||
self.timesheet = make_timesheet(emp, simulate = True, is_billable=1)
|
||||
self.salary_slip = make_salary_slip(self.timesheet.name)
|
||||
self.salary_slip.submit()
|
||||
self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer')
|
||||
|
@ -1,29 +0,0 @@
|
||||
.panel-header {
|
||||
background-color: #fafbfc;
|
||||
border: 1px solid #d1d8dd;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.panel-body {
|
||||
background-color: #fff;
|
||||
border: 1px solid #d1d8dd;
|
||||
border-top: none;
|
||||
border-radius: 0 0 3px 3px;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
.sender-avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 3px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.sender-avatar-placeholder {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 3px;
|
||||
vertical-align: middle;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
color: #d1d8dd;
|
||||
border: 1px solid #d1d8dd;
|
||||
background-color: #fff;
|
||||
}
|
@ -1,408 +0,0 @@
|
||||
.erpnext-footer {
|
||||
margin: 11px auto;
|
||||
text-align: center;
|
||||
}
|
||||
.show-all-reports {
|
||||
margin-top: 5px;
|
||||
font-size: 11px;
|
||||
}
|
||||
/* toolbar */
|
||||
.toolbar-splash {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin: -10px auto;
|
||||
}
|
||||
.erpnext-icon {
|
||||
width: 24px;
|
||||
margin-right: 0px;
|
||||
margin-top: -3px;
|
||||
}
|
||||
.dashboard-list-item {
|
||||
background-color: inherit;
|
||||
padding: 5px 0px;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
}
|
||||
#page-stock-balance .dashboard-list-item {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
.dashboard-list-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.frappe-control[data-fieldname='result_html'] {
|
||||
overflow: scroll;
|
||||
}
|
||||
.assessment-result-tool {
|
||||
table-layout: fixed;
|
||||
}
|
||||
.assessment-result-tool input {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
outline: none;
|
||||
text-align: right;
|
||||
}
|
||||
.assessment-result-tool th {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.assessment-result-tool .total-score,
|
||||
.assessment-result-tool .grade,
|
||||
.assessment-result-tool .score {
|
||||
text-align: right;
|
||||
}
|
||||
/* pos */
|
||||
body[data-route="pos"] .pos-bill-toolbar {
|
||||
padding: 10px 0px;
|
||||
height: 51px;
|
||||
}
|
||||
body[data-route="pos"] .pos-bill-item:hover,
|
||||
body[data-route="pos"] .list-customers-table > .pos-list-row:hover {
|
||||
background-color: #f5f7fa;
|
||||
cursor: pointer;
|
||||
}
|
||||
body[data-route="pos"] .pos-item-qty {
|
||||
display: inline-block;
|
||||
}
|
||||
body[data-route="pos"] .pos-qty-row > div {
|
||||
padding: 5px 0px;
|
||||
}
|
||||
body[data-route="pos"] .pos-qty-btn {
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
font-size: 120%;
|
||||
}
|
||||
body[data-route="pos"] .search-area .form-group {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
body[data-route="pos"] .tax-table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
body[data-route="pos"] .discount-field-col {
|
||||
padding-left: 24px;
|
||||
}
|
||||
body[data-route="pos"] .discount-amount-area .input-group:first-child {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
body[data-route="pos"] .payment-toolbar .row {
|
||||
width: 323px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
body[data-route="pos"] .payment-mode {
|
||||
cursor: pointer;
|
||||
font-family: sans-serif;
|
||||
font-size: 15px;
|
||||
}
|
||||
body[data-route="pos"] .pos-payment-row .col-xs-6 {
|
||||
padding: 15px;
|
||||
}
|
||||
body[data-route="pos"] .pos-payment-row {
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
margin: 2px 0px 5px 0px;
|
||||
height: 60px;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
body[data-route="pos"] .pos-payment-row:hover,
|
||||
body[data-route="pos"] .pos-keyboard-key:hover {
|
||||
background-color: #fafbfc;
|
||||
cursor: pointer;
|
||||
}
|
||||
body[data-route="pos"] .pos-keyboard-key,
|
||||
body[data-route="pos"] .delete-btn {
|
||||
border: 1px solid #d1d8dd;
|
||||
height: 85px;
|
||||
width: 85px;
|
||||
margin: 10px 10px;
|
||||
font-size: 24px;
|
||||
font-weight: 200;
|
||||
background-color: #FDFDFD;
|
||||
border-color: #e8e8e8;
|
||||
}
|
||||
body[data-route="pos"] .numeric-keypad {
|
||||
border: 1px solid #d1d8dd;
|
||||
height: 69px;
|
||||
width: 69px;
|
||||
font-size: 20px;
|
||||
font-weight: 200;
|
||||
background-color: #FDFDFD;
|
||||
border-color: #e8e8e8;
|
||||
margin-left: -4px;
|
||||
}
|
||||
body[data-route="pos"] .pos-pay {
|
||||
height: 69px;
|
||||
width: 69px;
|
||||
font-size: 17px;
|
||||
font-weight: 200;
|
||||
margin-left: -4px;
|
||||
}
|
||||
body[data-route="pos"] .numeric-keypad {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
font-size: 20px;
|
||||
font-weight: 200;
|
||||
border-radius: 0;
|
||||
background-color: #fff;
|
||||
margin-left: -4px;
|
||||
}
|
||||
@media (max-width: 1199px) {
|
||||
body[data-route="pos"] .numeric-keypad {
|
||||
height: 45px;
|
||||
width: 45px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
body[data-route="pos"] .numeric-keypad {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
}
|
||||
}
|
||||
body[data-route="pos"] .numeric_keypad {
|
||||
margin-left: -15px;
|
||||
}
|
||||
body[data-route="pos"] .numeric_keypad > .row > button {
|
||||
border: none;
|
||||
border-right: 1px solid #d1d8dd;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
}
|
||||
body[data-route="pos"] .numeric_keypad > .row > button:first-child {
|
||||
border-left: 1px solid #d1d8dd;
|
||||
}
|
||||
body[data-route="pos"] .numeric_keypad > .row:first-child > button {
|
||||
border-top: 1px solid #d1d8dd;
|
||||
}
|
||||
body[data-route="pos"] .pos-pay {
|
||||
background-color: #5E64FF;
|
||||
border: none;
|
||||
}
|
||||
body[data-route="pos"] .multimode-payments {
|
||||
padding-left: 30px;
|
||||
}
|
||||
body[data-route="pos"] .payment-toolbar {
|
||||
padding-right: 30px;
|
||||
}
|
||||
body[data-route="pos"] .list-row-head.pos-invoice-list {
|
||||
border-top: 1px solid #d1d8dd;
|
||||
}
|
||||
body[data-route="pos"] .modal-dialog {
|
||||
width: 750px;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
body[data-route="pos"] .modal-dialog {
|
||||
width: auto;
|
||||
}
|
||||
body[data-route="pos"] .modal-dialog .modal-content {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
body[data-route="pos"] .amount-row h3 {
|
||||
font-size: 15px;
|
||||
}
|
||||
body[data-route="pos"] .pos-keyboard-key,
|
||||
body[data-route="pos"] .delete-btn {
|
||||
height: 50px;
|
||||
}
|
||||
body[data-route="pos"] .multimode-payments {
|
||||
padding-left: 15px;
|
||||
}
|
||||
body[data-route="pos"] .payment-toolbar {
|
||||
padding-right: 15px;
|
||||
}
|
||||
}
|
||||
body[data-route="pos"] .amount-label {
|
||||
font-size: 16px;
|
||||
}
|
||||
body[data-route="pos"] .selected-payment-mode {
|
||||
background-color: #fafbfc;
|
||||
cursor: pointer;
|
||||
}
|
||||
body[data-route="pos"] .pos-invoice-list {
|
||||
padding: 15px 10px;
|
||||
}
|
||||
body[data-route="pos"] .write_off_amount,
|
||||
body[data-route="pos"] .change_amount {
|
||||
margin: 15px;
|
||||
width: 130px;
|
||||
}
|
||||
body[data-route="pos"] .pos-list-row {
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
padding: 9px 15px;
|
||||
font-size: 12px;
|
||||
margin: 0px;
|
||||
border-bottom: 1px solid #d1d8dd;
|
||||
}
|
||||
body[data-route="pos"] .pos-list-row .cell {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
body[data-route="pos"] .pos-list-row .cell.price-cell {
|
||||
width: 50%;
|
||||
}
|
||||
body[data-route="pos"] .pos-list-row .subject {
|
||||
width: 40%;
|
||||
}
|
||||
body[data-route="pos"] .pos-list-row .list-row-checkbox,
|
||||
body[data-route="pos"] .pos-list-row .list-select-all {
|
||||
margin-right: 7px;
|
||||
}
|
||||
body[data-route="pos"] .pos-bill-header {
|
||||
background-color: #f5f7fa;
|
||||
border: 1px solid #d1d8dd;
|
||||
padding: 13px 15px;
|
||||
}
|
||||
body[data-route="pos"] .pos-list-row.active {
|
||||
background-color: #fffce7;
|
||||
}
|
||||
body[data-route="pos"] .totals-area {
|
||||
border-right: 1px solid #d1d8dd;
|
||||
border-left: 1px solid #d1d8dd;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
body[data-route="pos"] .tax-area .pos-list-row {
|
||||
border: none;
|
||||
}
|
||||
body[data-route="pos"] .item-cart-items {
|
||||
height: calc(100vh - 526px);
|
||||
overflow: auto;
|
||||
border: 1px solid #d1d8dd;
|
||||
border-top: none;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
body[data-route="pos"] .item-cart-items {
|
||||
height: 30vh;
|
||||
}
|
||||
}
|
||||
body[data-route="pos"] .no-items-message {
|
||||
min-height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
body[data-route="pos"] .pos-list-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
body[data-route="pos"] .form-section-heading {
|
||||
padding: 0;
|
||||
}
|
||||
body[data-route="pos"] .item-list {
|
||||
border: 1px solid #d1d8dd;
|
||||
border-top: none;
|
||||
max-height: calc(100vh - 190px);
|
||||
overflow: auto;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
body[data-route="pos"] .item-list {
|
||||
max-height: initial;
|
||||
}
|
||||
}
|
||||
body[data-route="pos"] .item-list .image-field {
|
||||
height: 140px;
|
||||
}
|
||||
body[data-route="pos"] .item-list .image-field .placeholder-text {
|
||||
font-size: 50px;
|
||||
}
|
||||
body[data-route="pos"] .item-list .pos-item-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
body[data-route="pos"] .pos-bill-toolbar {
|
||||
margin-top: 10px;
|
||||
}
|
||||
body[data-route="pos"] .search-item .form-group {
|
||||
margin: 0;
|
||||
}
|
||||
body[data-route="pos"] .item-list-area .pos-bill-header {
|
||||
padding: 5px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
body[data-route="pos"] .pos-selected-item-action .pos-list-row:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
body[data-route="pos"] .pos-selected-item-action > .pos-list-row {
|
||||
border: none;
|
||||
}
|
||||
@media (max-width: 1199px) {
|
||||
body[data-route="pos"] .pos-selected-item-action > .pos-list-row {
|
||||
padding: 5px 15px;
|
||||
}
|
||||
}
|
||||
body[data-route="pos"] .edit-customer-btn {
|
||||
position: absolute;
|
||||
right: 57px;
|
||||
top: 15px;
|
||||
z-index: 100;
|
||||
}
|
||||
body[data-route="pos"] .btn-more {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
background-color: #fafbfc;
|
||||
min-height: 200px;
|
||||
}
|
||||
body[data-route="pos"] .collapse-btn {
|
||||
cursor: pointer;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
body[data-route="pos"] .page-actions {
|
||||
max-width: 110px;
|
||||
}
|
||||
}
|
||||
.price-info {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
margin: 0 0 15px 15px;
|
||||
background-color: rgba(141, 153, 166, 0.6);
|
||||
padding: 5px 9px;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
}
|
||||
.leaderboard .result {
|
||||
border-top: 1px solid #d1d8dd;
|
||||
}
|
||||
.leaderboard .list-item {
|
||||
padding-left: 45px;
|
||||
}
|
||||
.leaderboard .list-item_content {
|
||||
padding-right: 45px;
|
||||
}
|
||||
.exercise-card {
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.30);
|
||||
border-radius: 2px;
|
||||
padding: 6px 6px 6px 8px;
|
||||
margin-top: 10px;
|
||||
height: 100% !important;
|
||||
}
|
||||
.exercise-card .card-img-top {
|
||||
width: 100%;
|
||||
height: 15vw;
|
||||
object-fit: cover;
|
||||
}
|
||||
.exercise-card .btn-edit {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 20px;
|
||||
}
|
||||
.exercise-card .btn-del {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 50px;
|
||||
}
|
||||
.exercise-card .card-body {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.exercise-card .card-footer {
|
||||
padding: 10px;
|
||||
}
|
||||
.exercise-row {
|
||||
height: 100% !important;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.exercise-col {
|
||||
padding: 10px;
|
||||
}
|
@ -1,611 +0,0 @@
|
||||
/* required styles */
|
||||
|
||||
.leaflet-pane,
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow,
|
||||
.leaflet-tile-container,
|
||||
.leaflet-map-pane svg,
|
||||
.leaflet-map-pane canvas,
|
||||
.leaflet-zoom-box,
|
||||
.leaflet-image-layer,
|
||||
.leaflet-layer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.leaflet-container {
|
||||
overflow: hidden;
|
||||
-ms-touch-action: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.leaflet-tile,
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-user-drag: none;
|
||||
}
|
||||
|
||||
|
||||
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
|
||||
|
||||
.leaflet-safari .leaflet-tile {
|
||||
image-rendering: -webkit-optimize-contrast;
|
||||
}
|
||||
|
||||
|
||||
/* hack that prevents hw layers "stretching" when loading new tiles */
|
||||
|
||||
.leaflet-safari .leaflet-tile-container {
|
||||
width: 1600px;
|
||||
height: 1600px;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.leaflet-marker-icon,
|
||||
.leaflet-marker-shadow {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
|
||||
|
||||
|
||||
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
|
||||
|
||||
.leaflet-container .leaflet-overlay-pane svg,
|
||||
.leaflet-container .leaflet-marker-pane img,
|
||||
.leaflet-container .leaflet-tile-pane img,
|
||||
.leaflet-container img.leaflet-image-layer {
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.leaflet-tile {
|
||||
filter: inherit;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.leaflet-tile-loaded {
|
||||
visibility: inherit;
|
||||
}
|
||||
|
||||
.leaflet-zoom-box {
|
||||
width: 0;
|
||||
height: 0;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
z-index: 800;
|
||||
}
|
||||
|
||||
|
||||
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
|
||||
|
||||
.leaflet-overlay-pane svg {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
.leaflet-pane {
|
||||
z-index: 400;
|
||||
}
|
||||
|
||||
.leaflet-tile-pane {
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
.leaflet-overlay-pane {
|
||||
z-index: 400;
|
||||
}
|
||||
|
||||
.leaflet-shadow-pane {
|
||||
z-index: 500;
|
||||
}
|
||||
|
||||
.leaflet-marker-pane {
|
||||
z-index: 600;
|
||||
}
|
||||
|
||||
.leaflet-popup-pane {
|
||||
z-index: 700;
|
||||
}
|
||||
|
||||
.leaflet-map-pane canvas {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.leaflet-map-pane svg {
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
.leaflet-vml-shape {
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.lvml {
|
||||
behavior: url(#default#VML);
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
|
||||
/* control positioning */
|
||||
|
||||
.leaflet-control {
|
||||
position: relative;
|
||||
z-index: 800;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.leaflet-top {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.leaflet-right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.leaflet-bottom {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.leaflet-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.leaflet-control {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.leaflet-right .leaflet-control {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.leaflet-top .leaflet-control {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.leaflet-bottom .leaflet-control {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.leaflet-left .leaflet-control {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.leaflet-right .leaflet-control {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom and fade animations */
|
||||
|
||||
.leaflet-fade-anim .leaflet-tile {
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
.leaflet-fade-anim .leaflet-popup {
|
||||
opacity: 0;
|
||||
-webkit-transition: opacity 0.2s linear;
|
||||
-moz-transition: opacity 0.2s linear;
|
||||
-o-transition: opacity 0.2s linear;
|
||||
transition: opacity 0.2s linear;
|
||||
}
|
||||
|
||||
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.leaflet-zoom-animated {
|
||||
-webkit-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-animated {
|
||||
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0, 0, 0.25, 1);
|
||||
-moz-transition: -moz-transform 0.25s cubic-bezier(0, 0, 0.25, 1);
|
||||
-o-transition: -o-transform 0.25s cubic-bezier(0, 0, 0.25, 1);
|
||||
transition: transform 0.25s cubic-bezier(0, 0, 0.25, 1);
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-tile,
|
||||
.leaflet-pan-anim .leaflet-tile {
|
||||
-webkit-transition: none;
|
||||
-moz-transition: none;
|
||||
-o-transition: none;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.leaflet-zoom-anim .leaflet-zoom-hide {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* cursors */
|
||||
|
||||
.leaflet-interactive {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.leaflet-grab {
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
}
|
||||
|
||||
.leaflet-crosshair,
|
||||
.leaflet-crosshair .leaflet-interactive {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.leaflet-popup-pane,
|
||||
.leaflet-control {
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.leaflet-dragging .leaflet-grab,
|
||||
.leaflet-dragging .leaflet-grab .leaflet-interactive,
|
||||
.leaflet-dragging .leaflet-marker-draggable {
|
||||
cursor: move;
|
||||
cursor: -webkit-grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
}
|
||||
|
||||
|
||||
/* visual tweaks */
|
||||
|
||||
.leaflet-container {
|
||||
background: #ddd;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.leaflet-container a {
|
||||
color: #0078A8;
|
||||
}
|
||||
|
||||
.leaflet-container a.leaflet-active {
|
||||
outline: 2px solid orange;
|
||||
}
|
||||
|
||||
.leaflet-zoom-box {
|
||||
border: 2px dotted #38f;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
|
||||
/* general typography */
|
||||
|
||||
.leaflet-container {
|
||||
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
|
||||
/* general toolbar styles */
|
||||
|
||||
.leaflet-bar {
|
||||
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.65);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.leaflet-bar a,
|
||||
.leaflet-bar a:hover {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.leaflet-bar a,
|
||||
.leaflet-control-layers-toggle {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.leaflet-bar a:hover {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
|
||||
.leaflet-bar a:first-child {
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.leaflet-bar a:last-child {
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.leaflet-bar a.leaflet-disabled {
|
||||
cursor: default;
|
||||
background-color: #f4f4f4;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-bar a {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
|
||||
/* zoom control */
|
||||
|
||||
.leaflet-control-zoom-in,
|
||||
.leaflet-control-zoom-out {
|
||||
font: bold 18px 'Lucida Console', Monaco, monospace;
|
||||
text-indent: 1px;
|
||||
}
|
||||
|
||||
.leaflet-control-zoom-out {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-in {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-zoom-out {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
|
||||
/* layers control */
|
||||
|
||||
.leaflet-control-layers {
|
||||
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.4);
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.leaflet-control-layers-toggle {
|
||||
background-image: url('assets/erpnext/images/leaflet/layers.png');
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.leaflet-retina .leaflet-control-layers-toggle {
|
||||
background-image: url('assets/erpnext/images/leaflet/layers-2x.png');
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-layers-toggle {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
}
|
||||
|
||||
.leaflet-control-layers .leaflet-control-layers-list,
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.leaflet-control-layers-expanded .leaflet-control-layers-list {
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.leaflet-control-layers-expanded {
|
||||
padding: 6px 10px 6px 6px;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.leaflet-control-layers-scrollbar {
|
||||
overflow-y: scroll;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.leaflet-control-layers-selector {
|
||||
margin-top: 2px;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
|
||||
.leaflet-control-layers label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.leaflet-control-layers-separator {
|
||||
height: 0;
|
||||
border-top: 1px solid #ddd;
|
||||
margin: 5px -10px 5px -6px;
|
||||
}
|
||||
|
||||
|
||||
/* attribution and scale controls */
|
||||
|
||||
.leaflet-container .leaflet-control-attribution {
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.leaflet-control-attribution,
|
||||
.leaflet-control-scale-line {
|
||||
padding: 0 5px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.leaflet-control-attribution a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.leaflet-control-attribution a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.leaflet-container .leaflet-control-attribution,
|
||||
.leaflet-container .leaflet-control-scale {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.leaflet-left .leaflet-control-scale {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.leaflet-bottom .leaflet-control-scale {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.leaflet-control-scale-line {
|
||||
border: 2px solid #777;
|
||||
border-top: none;
|
||||
line-height: 1.1;
|
||||
padding: 2px 5px 1px;
|
||||
font-size: 11px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.leaflet-control-scale-line:not(:first-child) {
|
||||
border-top: 2px solid #777;
|
||||
border-bottom: none;
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
|
||||
border-bottom: 2px solid #777;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-attribution,
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.leaflet-touch .leaflet-control-layers,
|
||||
.leaflet-touch .leaflet-bar {
|
||||
border: 2px solid rgba(0, 0, 0, 0.2);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
|
||||
/* popup */
|
||||
|
||||
.leaflet-popup {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.leaflet-popup-content-wrapper {
|
||||
padding: 1px;
|
||||
text-align: left;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.leaflet-popup-content {
|
||||
margin: 13px 19px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.leaflet-popup-content p {
|
||||
margin: 18px 0;
|
||||
}
|
||||
|
||||
.leaflet-popup-tip-container {
|
||||
margin: 0 auto;
|
||||
width: 40px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.leaflet-popup-tip {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
padding: 1px;
|
||||
margin: -10px auto 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-moz-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.leaflet-popup-content-wrapper,
|
||||
.leaflet-popup-tip {
|
||||
background: white;
|
||||
color: #333;
|
||||
box-shadow: 0 3px 14px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.leaflet-container a.leaflet-popup-close-button {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
padding: 4px 4px 0 0;
|
||||
border: none;
|
||||
text-align: center;
|
||||
width: 18px;
|
||||
height: 14px;
|
||||
font: 16px/14px Tahoma, Verdana, sans-serif;
|
||||
color: #c3c3c3;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.leaflet-container a.leaflet-popup-close-button:hover {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.leaflet-popup-scrolled {
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid #ddd;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper {
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
width: 24px;
|
||||
margin: 0 auto;
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
|
||||
filter: progid: DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-popup-tip-container {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-control-zoom,
|
||||
.leaflet-oldie .leaflet-control-layers,
|
||||
.leaflet-oldie .leaflet-popup-content-wrapper,
|
||||
.leaflet-oldie .leaflet-popup-tip {
|
||||
border: 1px solid #999;
|
||||
}
|
||||
|
||||
|
||||
/* div icon */
|
||||
|
||||
.leaflet-div-icon {
|
||||
background: #fff;
|
||||
border: 1px solid #666;
|
||||
}
|
316
erpnext/public/css/leaflet/leaflet.draw.css
vendored
316
erpnext/public/css/leaflet/leaflet.draw.css
vendored
@ -1,316 +0,0 @@
|
||||
/* ================================================================== */
|
||||
|
||||
|
||||
/* Toolbars
|
||||
/* ================================================================== */
|
||||
|
||||
.leaflet-draw-section {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar-top {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar-notop a:first-child {
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar-nobottom a:last-child {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar a {
|
||||
background-image: url('assets/erpnext/images/leaflet/spritesheet.png');
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.leaflet-retina .leaflet-draw-toolbar a {
|
||||
background-image: url('assets/erpnext/images/leaflet/spritesheet-2x.png');
|
||||
background-size: 270px 30px;
|
||||
}
|
||||
|
||||
.leaflet-draw a {
|
||||
display: block;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================== */
|
||||
|
||||
|
||||
/* Toolbar actions menu
|
||||
/* ================================================================== */
|
||||
|
||||
.leaflet-draw-actions {
|
||||
display: none;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
left: 26px;
|
||||
/* leaflet-draw-toolbar.left + leaflet-draw-toolbar.width */
|
||||
top: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.leaflet-right .leaflet-draw-actions {
|
||||
right: 26px;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions li:first-child a {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions li:last-child a {
|
||||
-webkit-border-radius: 0 4px 4px 0;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.leaflet-right .leaflet-draw-actions li:last-child a {
|
||||
-webkit-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.leaflet-right .leaflet-draw-actions li:first-child a {
|
||||
-webkit-border-radius: 4px 0 0 4px;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions a {
|
||||
background-color: #919187;
|
||||
border-left: 1px solid #AAA;
|
||||
color: #FFF;
|
||||
font: 11px/19px "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
line-height: 28px;
|
||||
text-decoration: none;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions-bottom {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions-top {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions-top a,
|
||||
.leaflet-draw-actions-bottom a {
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions a:hover {
|
||||
background-color: #A0A098;
|
||||
}
|
||||
|
||||
.leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================== */
|
||||
|
||||
|
||||
/* Draw toolbar
|
||||
/* ================================================================== */
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-draw-polyline {
|
||||
background-position: -2px -2px;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-draw-polygon {
|
||||
background-position: -31px -2px;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-draw-rectangle {
|
||||
background-position: -62px -2px;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-draw-circle {
|
||||
background-position: -92px -2px;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-draw-marker {
|
||||
background-position: -122px -2px;
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================== */
|
||||
|
||||
|
||||
/* Edit toolbar
|
||||
/* ================================================================== */
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-edit-edit {
|
||||
background-position: -152px -2px;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-edit-remove {
|
||||
background-position: -182px -2px;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-edit-edit.leaflet-disabled {
|
||||
background-position: -212px -2px;
|
||||
}
|
||||
|
||||
.leaflet-draw-toolbar .leaflet-draw-edit-remove.leaflet-disabled {
|
||||
background-position: -242px -2px;
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================== */
|
||||
|
||||
|
||||
/* Drawing styles
|
||||
/* ================================================================== */
|
||||
|
||||
.leaflet-mouse-marker {
|
||||
background-color: #fff;
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.leaflet-draw-tooltip {
|
||||
background: rgb(54, 54, 54);
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid transparent;
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
color: #fff;
|
||||
font: 12px/18px "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||
margin-left: 20px;
|
||||
margin-top: -21px;
|
||||
padding: 4px 8px;
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
white-space: nowrap;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.leaflet-draw-tooltip:before {
|
||||
border-right: 6px solid black;
|
||||
border-right-color: rgba(0, 0, 0, 0.5);
|
||||
border-top: 6px solid transparent;
|
||||
border-bottom: 6px solid transparent;
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 7px;
|
||||
left: -7px;
|
||||
}
|
||||
|
||||
.leaflet-error-draw-tooltip {
|
||||
background-color: #F2DEDE;
|
||||
border: 1px solid #E6B6BD;
|
||||
color: #B94A48;
|
||||
}
|
||||
|
||||
.leaflet-error-draw-tooltip:before {
|
||||
border-right-color: #E6B6BD;
|
||||
}
|
||||
|
||||
.leaflet-draw-tooltip-single {
|
||||
margin-top: -12px
|
||||
}
|
||||
|
||||
.leaflet-draw-tooltip-subtext {
|
||||
color: #f8d5e4;
|
||||
}
|
||||
|
||||
.leaflet-draw-guide-dash {
|
||||
font-size: 1%;
|
||||
opacity: 0.6;
|
||||
position: absolute;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================== */
|
||||
|
||||
|
||||
/* Edit styles
|
||||
/* ================================================================== */
|
||||
|
||||
.leaflet-edit-marker-selected {
|
||||
background: rgba(254, 87, 161, 0.1);
|
||||
border: 4px dashed rgba(254, 87, 161, 0.6);
|
||||
-webkit-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.leaflet-edit-move {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.leaflet-edit-resize {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* ================================================================== */
|
||||
|
||||
|
||||
/* Old IE styles
|
||||
/* ================================================================== */
|
||||
|
||||
.leaflet-oldie .leaflet-draw-toolbar {
|
||||
border: 3px solid #999;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-toolbar a {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-toolbar a:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-actions {
|
||||
left: 32px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-actions li {
|
||||
display: inline;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-edit-marker-selected {
|
||||
border: 4px dashed #fe93c2;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-actions a {
|
||||
background-color: #999;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-actions a:hover {
|
||||
background-color: #a5a5a5;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-actions-top a {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-actions-bottom a {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.leaflet-oldie .leaflet-draw-actions-top.leaflet-draw-actions-bottom a {
|
||||
height: 27px;
|
||||
line-height: 27px;
|
||||
}
|
@ -14,9 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
init: function(wrapper, title) {
|
||||
this._super({
|
||||
erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridReport {
|
||||
constructor(wrapper, title) {
|
||||
super({
|
||||
title: title,
|
||||
parent: $(wrapper).find('.layout-main'),
|
||||
page: wrapper.page,
|
||||
@ -33,8 +33,24 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
setup_columns: function() {
|
||||
|
||||
this.filters = [
|
||||
{fieldtype: "Select", label: __("Company"), link:"Company", fieldname: "company",
|
||||
default_value: __("Select Company..."),
|
||||
filter: function(val, item, opts, me) {
|
||||
if (item.company == val || val == opts.default_value) {
|
||||
return me.apply_zero_filter(val, item, opts, me);
|
||||
}
|
||||
return false;
|
||||
}},
|
||||
{fieldtype: "Select", label: "Fiscal Year", link:"Fiscal Year", fieldname: "fiscal_year",
|
||||
default_value: __("Select Fiscal Year...")},
|
||||
{fieldtype: "Date", label: __("From Date"), fieldname: "from_date"},
|
||||
{fieldtype: "Label", label: __("To")},
|
||||
{fieldtype: "Date", label: __("To Date"), fieldname: "to_date"}
|
||||
]
|
||||
}
|
||||
setup_columns() {
|
||||
this.columns = [
|
||||
{id: "name", name: __("Account"), field: "name", width: 300, cssClass: "cell-title"},
|
||||
{id: "opening_dr", name: __("Opening (Dr)"), field: "opening_dr", width: 100,
|
||||
@ -50,25 +66,10 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
{id: "closing_cr", name: __("Closing (Cr)"), field: "closing_cr", width: 100,
|
||||
formatter: this.currency_formatter}
|
||||
];
|
||||
}
|
||||
|
||||
},
|
||||
filters: [
|
||||
{fieldtype: "Select", label: __("Company"), link:"Company", fieldname: "company",
|
||||
default_value: __("Select Company..."),
|
||||
filter: function(val, item, opts, me) {
|
||||
if (item.company == val || val == opts.default_value) {
|
||||
return me.apply_zero_filter(val, item, opts, me);
|
||||
}
|
||||
return false;
|
||||
}},
|
||||
{fieldtype: "Select", label: "Fiscal Year", link:"Fiscal Year", fieldname: "fiscal_year",
|
||||
default_value: __("Select Fiscal Year...")},
|
||||
{fieldtype: "Date", label: __("From Date"), fieldname: "from_date"},
|
||||
{fieldtype: "Label", label: __("To")},
|
||||
{fieldtype: "Date", label: __("To Date"), fieldname: "to_date"}
|
||||
],
|
||||
setup_filters: function() {
|
||||
this._super();
|
||||
setup_filters() {
|
||||
super.setup_filters();
|
||||
var me = this;
|
||||
// default filters
|
||||
this.filter_inputs.fiscal_year.change(function() {
|
||||
@ -83,8 +84,8 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
});
|
||||
me.show_zero_check()
|
||||
if(me.ignore_closing_entry) me.ignore_closing_entry();
|
||||
},
|
||||
prepare_data: function() {
|
||||
}
|
||||
prepare_data() {
|
||||
var me = this;
|
||||
if(!this.primary_data) {
|
||||
// make accounts list
|
||||
@ -113,12 +114,12 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
this.set_indent();
|
||||
this.prepare_balances();
|
||||
|
||||
},
|
||||
init_account: function(d) {
|
||||
}
|
||||
init_account(d) {
|
||||
this.reset_item_values(d);
|
||||
},
|
||||
}
|
||||
|
||||
prepare_balances: function() {
|
||||
prepare_balances() {
|
||||
var gl = frappe.report_dump.data['GL Entry'];
|
||||
var me = this;
|
||||
|
||||
@ -139,8 +140,8 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
});
|
||||
|
||||
this.update_groups();
|
||||
},
|
||||
update_balances: function(account, posting_date, v) {
|
||||
}
|
||||
update_balances(account, posting_date, v) {
|
||||
// opening
|
||||
if (posting_date < this.opening_date || v.is_opening === "Yes") {
|
||||
if (account.report_type === "Profit and Loss" &&
|
||||
@ -161,8 +162,8 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
var closing_bal = flt(account.opening_dr) - flt(account.opening_cr) +
|
||||
flt(account.debit) - flt(account.credit);
|
||||
this.set_debit_or_credit(account, "closing", closing_bal);
|
||||
},
|
||||
set_debit_or_credit: function(account, field, balance) {
|
||||
}
|
||||
set_debit_or_credit(account, field, balance) {
|
||||
if(balance > 0) {
|
||||
account[field+"_dr"] = balance;
|
||||
account[field+"_cr"] = 0;
|
||||
@ -170,8 +171,8 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
account[field+"_cr"] = Math.abs(balance);
|
||||
account[field+"_dr"] = 0;
|
||||
}
|
||||
},
|
||||
update_groups: function() {
|
||||
}
|
||||
update_groups() {
|
||||
// update groups
|
||||
var me= this;
|
||||
$.each(this.data, function(i, account) {
|
||||
@ -202,9 +203,9 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
set_fiscal_year: function() {
|
||||
set_fiscal_year() {
|
||||
if (this.opening_date > this.closing_date) {
|
||||
frappe.msgprint(__("Opening Date should be before Closing Date"));
|
||||
return;
|
||||
@ -223,9 +224,9 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
frappe.msgprint(__("Opening Date and Closing Date should be within same Fiscal Year"));
|
||||
return;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
show_general_ledger: function(account) {
|
||||
show_general_ledger(account) {
|
||||
frappe.route_options = {
|
||||
account: account,
|
||||
company: this.company,
|
||||
@ -234,4 +235,4 @@ erpnext.AccountTreeGrid = frappe.views.TreeGridReport.extend({
|
||||
};
|
||||
frappe.set_route("query-report", "General Ledger");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user