Merge branch 'develop' into batch_bom_operations

This commit is contained in:
Govind S Menokee 2019-07-16 02:12:24 +05:30 committed by GitHub
commit 2ab9947647
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
164 changed files with 4651 additions and 7926 deletions

View File

@ -121,7 +121,7 @@ frappe.treeview_settings["Account"] = {
},
onrender: function(node) {
if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){
var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr";
var dr_or_cr = in_list(["Liability", "Income", "Equity"], node.data.root_type) ? "Cr" : "Dr";
if (node.data && node.data.balance!==undefined) {
$('<span class="balance-area pull-right text-muted small">'
+ (node.data.balance_in_account_currency ?

View File

@ -9,6 +9,26 @@ frappe.ui.form.on('Accounting Dimension', {
frappe.set_route("List", frm.doc.document_type);
});
}
let button = frm.doc.disabled ? "Enable" : "Disable";
frm.add_custom_button(__(button), function() {
frm.set_value('disabled', 1 - frm.doc.disabled);
frappe.call({
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
args: {
doc: frm.doc
},
freeze: true,
callback: function(r) {
let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled";
frm.save();
frappe.show_alert({message:__(message), indicator:'green'});
}
});
});
},
document_type: function(frm) {
@ -21,13 +41,4 @@ frappe.ui.form.on('Accounting Dimension', {
}
});
},
disabled: function(frm) {
frappe.call({
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension",
args: {
doc: frm.doc
}
});
}
});

View File

@ -38,7 +38,8 @@
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disable"
"label": "Disable",
"read_only": 1
},
{
"default": "0",
@ -53,7 +54,7 @@
"label": "Mandatory For Profit and Loss Account"
}
],
"modified": "2019-05-27 18:18:17.792726",
"modified": "2019-07-07 18:56:19.517450",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting Dimension",

View File

@ -121,11 +121,11 @@ def delete_accounting_dimension(doc):
@frappe.whitelist()
def disable_dimension(doc):
if frappe.flags.in_test:
frappe.enqueue(start_dimension_disabling, doc=doc)
toggle_disabling(doc=doc)
else:
start_dimension_disabling(doc=doc)
frappe.enqueue(toggle_disabling, doc=doc)
def start_dimension_disabling(doc):
def toggle_disabling(doc):
doc = json.loads(doc)
if doc.get('disabled'):

View File

@ -10,6 +10,7 @@
"acc_frozen_upto",
"frozen_accounts_modifier",
"determine_address_tax_category_from",
"over_billing_allowance",
"column_break_4",
"credit_controller",
"check_supplier_invoice_uniqueness",
@ -168,12 +169,18 @@
"fieldname": "automatically_fetch_payment_terms",
"fieldtype": "Check",
"label": "Automatically Fetch Payment Terms"
},
{
"description": "Percentage you are allowed to bill more against the amount ordered. For example: If the order value is $100 for an item and tolerance is set as 10% then you are allowed to bill for $110.",
"fieldname": "over_billing_allowance",
"fieldtype": "Currency",
"label": "Over Billing Allowance (%)"
}
],
"icon": "icon-cog",
"idx": 1,
"issingle": 1,
"modified": "2019-04-28 18:20:55.789946",
"modified": "2019-07-04 18:20:55.789946",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@ -200,4 +207,4 @@
"quick_entry": 1,
"sort_order": "ASC",
"track_changes": 1
}
}

View File

@ -103,7 +103,7 @@ class BankReconciliation(Document):
for d in self.get('payment_entries'):
if d.clearance_date:
if not d.payment_document:
frappe.throw(_("Row #{0}: Payment document is required to complete the trasaction"))
frappe.throw(_("Row #{0}: Payment document is required to complete the transaction"))
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
@ -113,10 +113,8 @@ class BankReconciliation(Document):
if not d.clearance_date:
d.clearance_date = None
frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date)
frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s
where name=%s""".format(d.payment_document),
(d.clearance_date, nowdate(), d.payment_entry))
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
payment_entry.db_set('clearance_date', d.clearance_date)
clearance_date_updated = True

View File

@ -78,7 +78,6 @@ var validate_csv_data = function(frm) {
var create_import_button = function(frm) {
frm.page.set_primary_action(__("Start Import"), function () {
setup_progress_bar(frm);
frappe.call({
method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa",
args: {
@ -86,11 +85,11 @@ var create_import_button = function(frm) {
company: frm.doc.company
},
freeze: true,
freeze_message: __("Creating Accounts..."),
callback: function(r) {
if(!r.exc) {
clearInterval(frm.page["interval"]);
frm.page.set_indicator(__('Import Successfull'), 'blue');
frappe.hide_progress();
create_reset_button(frm);
}
}
@ -126,13 +125,3 @@ var generate_tree_preview = function(frm) {
}
});
};
var setup_progress_bar = function(frm) {
frm.page["seconds_elapsed"] = 0;
frm.page["execution_time"] = (frm.page["total_accounts"] > 100) ? 100 : frm.page["total_accounts"];
frm.page["interval"] = setInterval(function() {
frm.page["seconds_elapsed"] += 1;
frappe.show_progress(__('Creating Accounts'), frm.page["seconds_elapsed"], frm.page["execution_time"]);
}, 250);
};

View File

@ -33,6 +33,9 @@ def import_coa(file_name, company):
def generate_data_from_csv(file_name, as_dict=False):
''' read csv file and return the generated nested tree '''
if not file_name.endswith('.csv'):
frappe.throw("Only CSV files can be used to for importing data. Please check the file format you are trying to upload")
file_doc = frappe.get_doc('File', {"file_url": file_name})
file_path = file_doc.get_full_path()
@ -96,15 +99,27 @@ def build_forest(data):
return [child] + return_parent(data, parent_account)
charts_map, paths = {}, []
line_no = 3
error_messages = []
for i in data:
account_name, _, account_number, is_group, account_type, root_type = i
if not account_name:
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
charts_map[account_name] = {}
if is_group: charts_map[account_name]["is_group"] = is_group
if is_group == 1: charts_map[account_name]["is_group"] = is_group
if account_type: charts_map[account_name]["account_type"] = account_type
if root_type: charts_map[account_name]["root_type"] = root_type
if account_number: charts_map[account_name]["account_number"] = account_number
path = return_parent(data, account_name)[::-1]
paths.append(path) # List of path is created
line_no += 1
if error_messages:
frappe.throw("<br>".join(error_messages))
out = {}
for path in paths:
@ -150,22 +165,27 @@ def validate_root(accounts):
if len(roots) < 4:
return _("Number of root accounts cannot be less than 4")
error_messages = []
for account in roots:
if not account.get("root_type"):
return _("Please enter Root Type for - {0}").format(account.get("account_name"))
elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity"):
return _('Root Type for "{0}" must be one of the Asset, Liability, Income, Expense and Equity').format(account.get("account_name"))
if not account.get("root_type") and account.get("account_name"):
error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name")))
elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity") and account.get("account_name"):
error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name")))
if error_messages:
return "<br>".join(error_messages)
def validate_account_types(accounts):
account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"]
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group']]
account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1]
missing = list(set(account_types_for_ledger) - set(account_types))
if missing:
return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing))
account_types_for_group = ["Bank", "Cash", "Stock"]
account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group']]
account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] not in ('', 1)]
missing = list(set(account_types_for_group) - set(account_groups))
if missing:

View File

@ -1,177 +1,64 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2019-03-07 12:07:09.416101",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"sales_invoice",
"customer",
"column_break_3",
"posting_date",
"outstanding_amount"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "sales_invoice",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Invoice",
"length": 0,
"no_copy": 0,
"options": "Sales Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.customer",
"fieldname": "customer",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Customer",
"length": 0,
"no_copy": 0,
"options": "Customer",
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.posting_date",
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Date",
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_from": "sales_invoice.grand_total",
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Outstanding Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"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
"read_only": 1
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
}
],
"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-03-07 16:38:03.622666",
"modified": "2019-05-30 19:27:29.436153",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Discounted Invoice",
"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
"track_changes": 1
}

View File

@ -21,9 +21,29 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
refresh: function(frm) {
if(frm.doc.docstatus==1) {
frm.add_custom_button(__('Create Journal Entry'), function() {
return frm.events.make_jv(frm);
});
frappe.db.get_value("Journal Entry Account", {
'reference_type': 'Exchange Rate Revaluation',
'reference_name': frm.doc.name,
'docstatus': 1
}, "sum(debit) as sum", (r) =>{
let total_amt = 0;
frm.doc.accounts.forEach(d=> {
total_amt = total_amt + d['new_balance_in_base_currency'];
});
if(total_amt === r.sum) {
frm.add_custom_button(__("Journal Entry"), function(){
frappe.route_options = {
'reference_type': 'Exchange Rate Revaluation',
'reference_name': frm.doc.name
};
frappe.set_route("List", "Journal Entry");
}, __("View"));
} else {
frm.add_custom_button(__('Create Journal Entry'), function() {
return frm.events.make_jv(frm);
});
}
}, 'Journal Entry');
}
},

View File

@ -848,6 +848,39 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "due_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Due Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@ -861,7 +894,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-01-07 07:05:00.366399",
"modified": "2019-05-01 07:05:00.366399",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",

View File

@ -1,744 +1,177 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "ACC-INV-DISC-.YYYY.-.#####",
"beta": 0,
"creation": "2019-03-07 12:01:56.296952",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"posting_date",
"loan_start_date",
"loan_period",
"loan_end_date",
"column_break_3",
"status",
"company",
"section_break_5",
"invoices",
"section_break_7",
"total_amount",
"column_break_9",
"bank_charges",
"section_break_6",
"short_term_loan",
"bank_account",
"bank_charges_account",
"column_break_15",
"accounts_receivable_credit",
"accounts_receivable_discounted",
"accounts_receivable_unpaid",
"amended_from"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Posting Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_start_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Loan Start Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"label": "Loan Start Date"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_period",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Loan Period",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"label": "Loan Period (Days)"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "loan_end_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Loan End Date",
"length": 0,
"no_copy": 1,
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 1,
"options": "Draft\nSanctioned\nDisbursed\nSettled\nCancelled",
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "invoices",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Invoices",
"length": 0,
"no_copy": 0,
"options": "Discounted Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"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
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_charges",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Bank Charges",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"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
"options": "Company:company:default_currency"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldtype": "Section Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "short_term_loan",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Short Term Loan Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Bank Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "bank_charges_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Bank Charges Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_15",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"fieldtype": "Column Break"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_credit",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Accounts Receivable Credit Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_discounted",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Accounts Receivable Discounted Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "accounts_receivable_unpaid",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Accounts Receivable Unpaid Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Invoice Discounting",
"permlevel": 0,
"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
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-08 14:24:31.222027",
"modified": "2019-05-30 19:08:21.199759",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Invoice Discounting",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
@ -748,26 +181,17 @@
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"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
"track_changes": 1
}

View File

@ -28,18 +28,39 @@ class InvoiceDiscounting(AccountsController):
self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices])
def on_submit(self):
self.update_sales_invoice()
self.make_gl_entries()
def on_cancel(self):
self.set_status()
self.update_sales_invoice()
self.make_gl_entries()
def set_status(self):
self.status = "Draft"
if self.docstatus == 1:
self.status = "Sanctioned"
elif self.docstatus == 2:
self.status = "Cancelled"
def set_status(self, status=None):
if status:
self.status = status
self.db_set("status", status)
for d in self.invoices:
frappe.get_doc("Sales Invoice", d.sales_invoice).set_status(update=True, update_modified=False)
else:
self.status = "Draft"
if self.docstatus == 1:
self.status = "Sanctioned"
elif self.docstatus == 2:
self.status = "Cancelled"
def update_sales_invoice(self):
for d in self.invoices:
if self.docstatus == 1:
is_discounted = 1
else:
discounted_invoice = frappe.db.exists({
"doctype": "Discounted Invoice",
"sales_invoice": d.sales_invoice,
"docstatus": 1
})
is_discounted = 1 if discounted_invoice else 0
frappe.db.set_value("Sales Invoice", d.sales_invoice, "is_discounted", is_discounted)
def make_gl_entries(self):
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")

View File

@ -105,24 +105,28 @@ class JournalEntry(AccountsController):
invoice_discounting_list = list(set([d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"]))
for inv_disc in invoice_discounting_list:
short_term_loan_account, id_status = frappe.db.get_value("Invoice Discounting", inv_disc, ["short_term_loan", "status"])
inv_disc_doc = frappe.get_doc("Invoice Discounting", inv_disc)
status = None
for d in self.accounts:
if d.account == short_term_loan_account and d.reference_name == inv_disc:
if d.account == inv_disc_doc.short_term_loan and d.reference_name == inv_disc:
if self.docstatus == 1:
if d.credit > 0:
_validate_invoice_discounting_status(inv_disc, id_status, "Sanctioned", d.idx)
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Sanctioned", d.idx)
status = "Disbursed"
elif d.debit > 0:
_validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx)
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx)
status = "Settled"
else:
if d.credit > 0:
_validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx)
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx)
status = "Sanctioned"
elif d.debit > 0:
_validate_invoice_discounting_status(inv_disc, id_status, "Settled", d.idx)
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Settled", d.idx)
status = "Disbursed"
frappe.db.set_value("Invoice Discounting", inv_disc, "status", status)
break
if status:
inv_disc_doc.set_status(status=status)
def unlink_advance_entry_reference(self):
for d in self.get("accounts"):
@ -498,6 +502,7 @@ class JournalEntry(AccountsController):
self.get_gl_dict({
"account": d.account,
"party_type": d.party_type,
"due_date": self.due_date,
"party": d.party,
"against": d.against_account,
"debit": flt(d.debit, d.precision("debit")),

View File

@ -302,7 +302,7 @@ frappe.ui.form.on('Payment Entry', {
},
() => frm.set_value("party_balance", r.message.party_balance),
() => frm.set_value("party_name", r.message.party_name),
() => frm.events.get_outstanding_documents(frm),
() => frm.clear_table("references"),
() => frm.events.hide_unhide_fields(frm),
() => frm.events.set_dynamic_labels(frm),
() => {
@ -323,9 +323,7 @@ frappe.ui.form.on('Payment Entry', {
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from,
"paid_from_account_currency", "paid_from_account_balance", function(frm) {
if (frm.doc.payment_type == "Receive") {
frm.events.get_outstanding_documents(frm);
} else if (frm.doc.payment_type == "Pay") {
if (frm.doc.payment_type == "Pay") {
frm.events.paid_amount(frm);
}
}
@ -337,9 +335,7 @@ frappe.ui.form.on('Payment Entry', {
frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to,
"paid_to_account_currency", "paid_to_account_balance", function(frm) {
if(frm.doc.payment_type == "Pay") {
frm.events.get_outstanding_documents(frm);
} else if (frm.doc.payment_type == "Receive") {
if (frm.doc.payment_type == "Receive") {
if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
if(frm.doc.source_exchange_rate) {
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
@ -533,26 +529,87 @@ frappe.ui.form.on('Payment Entry', {
frm.events.set_unallocated_amount(frm);
},
get_outstanding_documents: function(frm) {
get_outstanding_invoice: function(frm) {
const today = frappe.datetime.get_today();
const fields = [
{fieldtype:"Section Break", label: __("Posting Date")},
{fieldtype:"Date", label: __("From Date"),
fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)},
{fieldtype:"Column Break"},
{fieldtype:"Date", label: __("To Date"), fieldname:"to_posting_date", default:today},
{fieldtype:"Section Break", label: __("Due Date")},
{fieldtype:"Date", label: __("From Date"), fieldname:"from_due_date"},
{fieldtype:"Column Break"},
{fieldtype:"Date", label: __("To Date"), fieldname:"to_due_date"},
{fieldtype:"Section Break", label: __("Outstanding Amount")},
{fieldtype:"Float", label: __("Greater Than Amount"),
fieldname:"outstanding_amt_greater_than", default: 0},
{fieldtype:"Column Break"},
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
{fieldtype:"Section Break"},
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
];
frappe.prompt(fields, function(filters){
frappe.flags.allocate_payment_amount = true;
frm.events.validate_filters_data(frm, filters);
frm.events.get_outstanding_documents(frm, filters);
}, __("Filters"), __("Get Outstanding Invoices"));
},
validate_filters_data: function(frm, filters) {
const fields = {
'Posting Date': ['from_posting_date', 'to_posting_date'],
'Due Date': ['from_posting_date', 'to_posting_date'],
'Advance Amount': ['from_posting_date', 'to_posting_date'],
};
for (let key in fields) {
let from_field = fields[key][0];
let to_field = fields[key][1];
if (filters[from_field] && !filters[to_field]) {
frappe.throw(__("Error: {0} is mandatory field",
[to_field.replace(/_/g, " ")]
));
} else if (filters[from_field] && filters[from_field] > filters[to_field]) {
frappe.throw(__("{0}: {1} must be less than {2}",
[key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")]
));
}
}
},
get_outstanding_documents: function(frm, filters) {
frm.clear_table("references");
if(!frm.doc.party) return;
if(!frm.doc.party) {
return;
}
frm.events.check_mandatory_to_fetch(frm);
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
var args = {
"posting_date": frm.doc.posting_date,
"company": frm.doc.company,
"party_type": frm.doc.party_type,
"payment_type": frm.doc.payment_type,
"party": frm.doc.party,
"party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to,
"cost_center": frm.doc.cost_center
}
for (let key in filters) {
args[key] = filters[key];
}
frappe.flags.allocate_payment_amount = filters['allocate_payment_amount'];
return frappe.call({
method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents',
args: {
args: {
"posting_date": frm.doc.posting_date,
"company": frm.doc.company,
"party_type": frm.doc.party_type,
"payment_type": frm.doc.payment_type,
"party": frm.doc.party,
"party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to,
"cost_center": frm.doc.cost_center
}
args:args
},
callback: function(r, rt) {
if(r.message) {
@ -608,25 +665,11 @@ frappe.ui.form.on('Payment Entry', {
frm.events.allocate_party_amount_against_ref_docs(frm,
(frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount));
}
});
},
allocate_payment_amount: function(frm) {
if(frm.doc.payment_type == 'Internal Transfer'){
return
}
if(frm.doc.references.length == 0){
frm.events.get_outstanding_documents(frm);
}
if(frm.doc.payment_type == 'Internal Transfer') {
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount);
} else {
frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount);
}
},
allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
var total_positive_outstanding_including_order = 0;
var total_negative_outstanding = 0;
@ -677,7 +720,7 @@ frappe.ui.form.on('Payment Entry', {
$.each(frm.doc.references || [], function(i, row) {
row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount
if(frm.doc.allocate_payment_amount){
if(frappe.flags.allocate_payment_amount){
if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) {
if(row.outstanding_amount >= allocated_positive_outstanding) {
row.allocated_amount = allocated_positive_outstanding;
@ -958,7 +1001,7 @@ frappe.ui.form.on('Payment Entry', {
},
() => {
if(frm.doc.payment_type != "Internal") {
frm.events.get_outstanding_documents(frm);
frm.clear_table("references");
}
}
]);

View File

@ -40,7 +40,7 @@
"target_exchange_rate",
"base_received_amount",
"section_break_14",
"allocate_payment_amount",
"get_outstanding_invoice",
"references",
"section_break_34",
"total_allocated_amount",
@ -325,19 +325,15 @@
"reqd": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "references",
"depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)",
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"label": "Reference"
},
{
"default": "1",
"depends_on": "eval:in_list(['Pay', 'Receive'], doc.payment_type)",
"fieldname": "allocate_payment_amount",
"fieldtype": "Check",
"label": "Allocate Payment Amount"
"fieldname": "get_outstanding_invoice",
"fieldtype": "Button",
"label": "Get Outstanding Invoice"
},
{
"fieldname": "references",
@ -570,7 +566,7 @@
}
],
"is_submittable": 1,
"modified": "2019-05-25 22:02:40.575822",
"modified": "2019-05-27 15:53:21.108857",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@ -574,8 +574,8 @@ def get_outstanding_reference_documents(args):
# Get negative outstanding sales /purchase invoices
negative_outstanding_invoices = []
if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"):
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"),
args.get("party"), args.get("party_account"), party_account_currency, company_currency)
negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"),
args.get("party_account"), args.get("company"), party_account_currency, company_currency)
# Get positive outstanding sales /purchase invoices/ Fees
condition = ""
@ -585,10 +585,23 @@ def get_outstanding_reference_documents(args):
# Add cost center condition
if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account():
condition += " and cost_center='%s'" % args.get("cost_center")
condition += " and cost_center='%s'" % args.get("cost_center")
date_fields_dict = {
'posting_date': ['from_posting_date', 'to_posting_date'],
'due_date': ['from_due_date', 'to_due_date']
}
for fieldname, date_fields in date_fields_dict.items():
if args.get(date_fields[0]) and args.get(date_fields[1]):
condition += " and {0} between '{1}' and '{2}'".format(fieldname,
args.get(date_fields[0]), args.get(date_fields[1]))
if args.get("company"):
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"),
args.get("party_account"), condition=condition)
args.get("party_account"), filters=args, condition=condition, limit=100)
for d in outstanding_invoices:
d["exchange_rate"] = 1
@ -606,12 +619,19 @@ def get_outstanding_reference_documents(args):
orders_to_be_billed = []
if (args.get("party_type") != "Student"):
orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"),
args.get("party"), party_account_currency, company_currency)
args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args)
return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed
if not data:
frappe.msgprint(_("No outstanding invoices found for the {0} <b>{1}</b>.")
.format(args.get("party_type").lower(), args.get("party")))
return data
def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency, cost_center=None):
def get_orders_to_be_billed(posting_date, party_type, party,
company, party_account_currency, company_currency, cost_center=None, filters=None):
if party_type == "Customer":
voucher_type = 'Sales Order'
elif party_type == "Supplier":
@ -641,6 +661,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
where
{party_type} = %s
and docstatus = 1
and company = %s
and ifnull(status, "") != "Closed"
and {ref_field} > advance_paid
and abs(100 - per_billed) > 0.01
@ -652,10 +673,14 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"condition": condition
}), party, as_dict=True)
}), (party, company), as_dict=True)
order_list = []
for d in orders:
if not (d.outstanding_amount >= filters.get("outstanding_amt_greater_than")
and d.outstanding_amount <= filters.get("outstanding_amt_less_than")):
continue
d["voucher_type"] = voucher_type
# This assumes that the exchange rate required is the one in the SO
d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date)
@ -663,7 +688,8 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
return order_list
def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency, cost_center=None):
def get_negative_outstanding_invoices(party_type, party, party_account,
company, party_account_currency, company_currency, cost_center=None):
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
supplier_condition = ""
if voucher_type == "Purchase Invoice":
@ -684,7 +710,8 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac
from
`tab{voucher_type}`
where
{party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0
{party_type} = %s and {party_account} = %s and docstatus = 1 and
company = %s and outstanding_amount < 0
{supplier_condition}
order by
posting_date, name
@ -696,7 +723,7 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac
"party_type": scrub(party_type),
"party_account": "debit_to" if party_type == "Customer" else "credit_to",
"cost_center": cost_center
}), (party, party_account), as_dict=True)
}), (party, party_account, company), as_dict=True)
@frappe.whitelist()
@ -924,7 +951,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=
pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency
pe.paid_amount = paid_amount
pe.received_amount = received_amount
pe.allocate_payment_amount = 1
pe.letter_head = doc.get("letter_head")
if pe.party_type in ["Customer", "Supplier"]:

View File

@ -8,6 +8,10 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) {
return { filters: { selling: 1 } };
});
frm.set_query("tc_name", function() {
return { filters: { selling: 1 } };
});
erpnext.queries.setup_queries(frm, "Warehouse", function() {
return erpnext.queries.warehouse(frm.doc);
});

View File

@ -337,7 +337,8 @@ class PurchaseInvoice(BuyingController):
if not self.is_return:
self.update_against_document_in_jv()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
self.update_billing_status_in_pr()
# Updating stock ledger should always be called after updating prevdoc status,
# because updating ordered qty in bin depends upon updated ordered qty in PO
@ -416,6 +417,7 @@ class PurchaseInvoice(BuyingController):
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"due_date": self.due_date,
"against": self.against_expense_account,
"credit": grand_total_in_company_currency,
"credit_in_account_currency": grand_total_in_company_currency \
@ -484,9 +486,13 @@ class PurchaseInvoice(BuyingController):
"credit": flt(item.rm_supp_cost)
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()):
expense_account = (item.expense_account
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
gl_entries.append(
self.get_gl_dict({
"account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account,
"account": expense_account,
"against": self.supplier,
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
"debit_in_account_currency": (flt(item.base_net_amount,
@ -769,7 +775,8 @@ class PurchaseInvoice(BuyingController):
if not self.is_return:
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
self.update_billing_status_in_pr()
# Updating stock ledger should always be called after updating prevdoc status,
# because updating ordered qty in bin depends upon updated ordered qty in PO

View File

@ -83,10 +83,14 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
}
if (doc.outstanding_amount>0 && !cint(doc.is_return)) {
if (doc.outstanding_amount>0) {
cur_frm.add_custom_button(__('Payment Request'), function() {
me.make_payment_request();
}, __('Create'));
cur_frm.add_custom_button(__('Invoice Discounting'), function() {
cur_frm.events.create_invoice_discounting(cur_frm);
}, __('Create'));
}
if (doc.docstatus === 1) {
@ -187,9 +191,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
method: "erpnext.selling.doctype.quotation.quotation.make_sales_invoice",
source_doctype: "Quotation",
target: me.frm,
setters: {
customer: me.frm.doc.customer || undefined,
},
setters: [{
fieldtype: 'Link',
label: __('Customer'),
options: 'Customer',
fieldname: 'party_name',
default: me.frm.doc.customer,
}],
get_query_filters: {
docstatus: 1,
status: ["!=", "Lost"],
@ -804,6 +812,13 @@ frappe.ui.form.on('Sales Invoice', {
frm.set_df_property("patient_name", "hidden", 1);
frm.set_df_property("ref_practitioner", "hidden", 1);
}
},
create_invoice_discounting: function(frm) {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_invoice_discounting",
frm: frm
});
}
})

View File

@ -155,6 +155,7 @@
"inter_company_invoice_reference",
"customer_group",
"campaign",
"is_discounted",
"col_break23",
"status",
"source",
@ -1324,6 +1325,13 @@
"options": "Campaign",
"print_hide": 1
},
{
"fieldname": "is_discounted",
"fieldtype": "Check",
"label": "Is Discounted",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "col_break23",
"fieldtype": "Column Break",
@ -1336,7 +1344,7 @@
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled",
"options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nOverdue\nCancelled",
"print_hide": 1,
"read_only": 1
},
@ -1558,7 +1566,7 @@
"icon": "fa fa-file-text",
"idx": 181,
"is_submittable": 1,
"modified": "2019-05-25 22:05:03.474745",
"modified": "2019-07-04 22:05:03.474745",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
@ -1612,4 +1620,4 @@
"title_field": "title",
"track_changes": 1,
"track_seen": 1
}
}

View File

@ -734,6 +734,7 @@ class SalesInvoice(SellingController):
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.against_income_account,
"debit": grand_total_in_company_currency,
"debit_in_account_currency": grand_total_in_company_currency \
@ -783,10 +784,13 @@ class SalesInvoice(SellingController):
asset.db_set("disposal_date", self.posting_date)
asset.set_status("Sold" if self.docstatus==1 else None)
else:
account_currency = get_account_currency(item.income_account)
income_account = (item.income_account
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
account_currency = get_account_currency(income_account)
gl_entries.append(
self.get_gl_dict({
"account": item.income_account if not item.enable_deferred_revenue else item.deferred_revenue_account,
"account": income_account,
"against": self.customer,
"credit": flt(item.base_net_amount, item.precision("base_net_amount")),
"credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount"))
@ -1173,6 +1177,56 @@ class SalesInvoice(SellingController):
self.set_missing_values(for_validate = True)
def get_discounting_status(self):
status = None
if self.is_discounted:
invoice_discounting_list = frappe.db.sql("""
select status
from `tabInvoice Discounting` id, `tabDiscounted Invoice` d
where
id.name = d.parent
and d.sales_invoice=%s
and id.docstatus=1
and status in ('Disbursed', 'Settled')
""", self.name)
for d in invoice_discounting_list:
status = d[0]
if status == "Disbursed":
break
return status
def set_status(self, update=False, status=None, update_modified=True):
if self.is_new():
if self.get('amended_from'):
self.status = 'Draft'
return
if not status:
if self.docstatus == 2:
status = "Cancelled"
elif self.docstatus == 1:
if flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed':
self.status = "Overdue and Discounted"
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()):
self.status = "Overdue"
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed':
self.status = "Unpaid and Discounted"
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()):
self.status = "Unpaid"
elif flt(self.outstanding_amount) < 0 and self.is_return==0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
self.status = "Credit Note Issued"
elif self.is_return == 1:
self.status = "Return"
elif flt(self.outstanding_amount)<=0:
self.status = "Paid"
else:
self.status = "Submitted"
else:
self.status = "Draft"
if update:
self.db_set('status', self.status, update_modified = update_modified)
def validate_inter_company_party(doctype, party, company, inter_company_reference):
if not party:
return
@ -1427,4 +1481,18 @@ def get_loyalty_programs(customer):
frappe.db.set(customer, 'loyalty_program', lp_details[0])
return []
else:
return lp_details
return lp_details
@frappe.whitelist()
def create_invoice_discounting(source_name, target_doc=None):
invoice = frappe.get_doc("Sales Invoice", source_name)
invoice_discounting = frappe.new_doc("Invoice Discounting")
invoice_discounting.company = invoice.company
invoice_discounting.append("invoices", {
"sales_invoice": source_name,
"customer": invoice.customer,
"posting_date": invoice.posting_date,
"outstanding_amount": invoice.outstanding_amount
})
return invoice_discounting

View File

@ -6,17 +6,18 @@ frappe.listview_settings['Sales Invoice'] = {
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
"currency", "is_return"],
get_indicator: function(doc) {
if(flt(doc.outstanding_amount) < 0) {
return [__("Credit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) {
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"]
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) {
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"]
} else if(cint(doc.is_return)) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if(flt(doc.outstanding_amount)==0) {
return [__("Paid"), "green", "outstanding_amount,=,0"]
}
var status_color = {
"Draft": "grey",
"Unpaid": "orange",
"Paid": "green",
"Return": "darkgrey",
"Credit Note Issued": "darkgrey",
"Unpaid and Discounted": "orange",
"Overdue and Discounted": "red",
"Overdue": "red"
};
return [__(doc.status), status_color[doc.status], "status,=,"+doc.status];
},
right_column: "grand_total"
};

View File

@ -108,3 +108,14 @@ frappe.query_reports["Accounts Payable"] = {
});
}
}
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});
});

View File

@ -92,3 +92,14 @@ frappe.query_reports["Accounts Payable Summary"] = {
});
}
}
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});
});

View File

@ -172,3 +172,14 @@ frappe.query_reports["Accounts Receivable"] = {
});
}
}
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});
});

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe, erpnext
from frappe import _, scrub
from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
class ReceivablePayableReport(object):
def __init__(self, filters=None):
@ -553,6 +554,14 @@ class ReceivablePayableReport(object):
conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts)))
values += accounts
accounting_dimensions = get_accounting_dimensions()
if accounting_dimensions:
for dimension in accounting_dimensions:
if self.filters.get(dimension):
conditions.append("{0} = %s".format(dimension))
values.append(self.filters.get(dimension))
return " and ".join(conditions), values
def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher):

View File

@ -116,3 +116,14 @@ frappe.query_reports["Accounts Receivable Summary"] = {
});
}
}
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});
});

View File

@ -63,9 +63,7 @@ frappe.query_reports["Budget Variance Report"] = {
]
}
let dimension_filters = erpnext.get_dimension_filters();
dimension_filters.then((dimensions) => {
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
});

View File

@ -8,17 +8,19 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
// The last item in the array is the definition for Presentation Currency
// filter. It won't be used in cash flow for now so we pop it. Please take
// of this if you are working here.
frappe.query_reports["Cash Flow"]["filters"].pop();
frappe.query_reports["Cash Flow"]["filters"].push({
"fieldname": "accumulated_values",
"label": __("Accumulated Values"),
"fieldtype": "Check"
});
frappe.query_reports["Cash Flow"]["filters"].splice(5, 1);
frappe.query_reports["Cash Flow"]["filters"].push({
"fieldname": "include_default_book_entries",
"label": __("Include Default Book Entries"),
"fieldtype": "Check"
});
frappe.query_reports["Cash Flow"]["filters"].push(
{
"fieldname": "accumulated_values",
"label": __("Accumulated Values"),
"fieldtype": "Check"
},
{
"fieldname": "include_default_book_entries",
"label": __("Include Default Book Entries"),
"fieldtype": "Check"
}
);
});

View File

@ -69,7 +69,9 @@ def execute(filters=None):
add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency)
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
return columns, data
chart = get_chart_data(columns, data)
return columns, data, None, chart
def get_cash_flow_accounts():
operation_accounts = {
@ -171,4 +173,21 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated
total_row["total"] += row["total"]
out.append(total_row)
out.append({})
out.append({})
def get_chart_data(columns, data):
labels = [d.get("label") for d in columns[2:]]
datasets = [{'name':account.get('account').replace("'", ""), 'values': [account.get('total')]} for account in data if account.get('parent_account') == None and account.get('currency')]
datasets = datasets[:-1]
chart = {
"data": {
'labels': labels,
'datasets': datasets
},
"type": "bar"
}
chart["fieldtype"] = "Currency"
return chart

View File

@ -159,9 +159,7 @@ frappe.query_reports["General Ledger"] = {
]
}
let dimension_filters = erpnext.get_dimension_filters();
dimension_filters.then((dimensions) => {
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{
"fieldname": dimension["fieldname"],

View File

@ -11,6 +11,7 @@ from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.report.financial_statements import get_cost_centers_with_children
from six import iteritems
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
from collections import OrderedDict
def execute(filters=None):
if not filters:
@ -274,7 +275,7 @@ def group_by_field(group_by):
return 'voucher_no'
def initialize_gle_map(gl_entries, filters):
gle_map = frappe._dict()
gle_map = OrderedDict()
group_by = group_by_field(filters.get('group_by'))
for gle in gl_entries:

View File

@ -93,4 +93,6 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
else:
chart["type"] = "line"
chart["fieldtype"] = "Currency"
return chart

View File

@ -16,7 +16,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"fieldname": "based_on",
"label": __("Based On"),
"fieldtype": "Select",
"options": "Cost Center\nProject",
"options": ["Cost Center", "Project"],
"default": "Cost Center",
"reqd": 1
},
@ -104,5 +104,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"parent_field": "parent_account",
"initial_depth": 3
}
});
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]);
});
});
});

View File

@ -24,8 +24,17 @@ def get_accounts_data(based_on, company):
if based_on == 'cost_center':
return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
from `tabCost Center` where company=%s order by name""", company, as_dict=True)
else:
elif based_on == 'project':
return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name')
else:
filters = {}
doctype = frappe.unscrub(based_on)
has_company = frappe.db.has_column(doctype, 'company')
if has_company:
filters.update({'company': company})
return frappe.get_all(doctype, fields = ["name"], filters = filters, order_by = 'name')
def get_data(accounts, filters, based_on):
if not accounts:
@ -42,7 +51,7 @@ def get_data(accounts, filters, based_on):
accumulate_values_into_parents(accounts, accounts_by_name)
data = prepare_data(accounts, filters, total_row, parent_children_map, based_on)
data = filter_out_zero_value_rows(data, parent_children_map,
data = filter_out_zero_value_rows(data, parent_children_map,
show_zero_values=filters.get("show_zero_values"))
return data
@ -112,14 +121,14 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on):
for key in value_fields:
row[key] = flt(d.get(key, 0.0), 3)
if abs(row[key]) >= 0.005:
# ignore zero values
has_value = True
row["has_value"] = has_value
data.append(row)
data.extend([{},total_row])
return data
@ -174,7 +183,7 @@ def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_
if from_date:
additional_conditions.append("and posting_date >= %(from_date)s")
gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit,
gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit,
is_opening, (select root_type from `tabAccount` where name = account) as type
from `tabGL Entry` where company=%(company)s
{additional_conditions}

View File

@ -67,3 +67,14 @@ frappe.query_reports["Sales Register"] = {
}
]
}
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
"options": dimension["document_type"]
});
});
});

View File

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import flt
from frappe import msgprint, _
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
def execute(filters=None):
return _execute(filters)
@ -163,6 +164,16 @@ def get_conditions(filters):
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)"""
accounting_dimensions = get_accounting_dimensions()
if accounting_dimensions:
for dimension in accounting_dimensions:
if filters.get(dimension):
conditions += """ and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension)
return conditions
def get_invoices(filters, additional_query_columns):

View File

@ -96,9 +96,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
}
});
let dimension_filters = erpnext.get_dimension_filters();
dimension_filters.then((dimensions) => {
erpnext.dimension_filters.then((dimensions) => {
dimensions.forEach((dimension) => {
frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
"fieldname": dimension["fieldname"],

View File

@ -628,7 +628,7 @@ def get_held_invoices(party_type, party):
return held_invoices
def get_outstanding_invoices(party_type, party, account, condition=None):
def get_outstanding_invoices(party_type, party, account, condition=None, filters=None):
outstanding_invoices = []
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
@ -644,7 +644,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
invoice_list = frappe.db.sql("""
select
voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount
voucher_no, voucher_type, posting_date, due_date,
ifnull(sum({dr_or_cr}), 0) as invoice_amount
from
`tabGL Entry`
where
@ -677,7 +678,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
""".format(payment_dr_or_cr=payment_dr_or_cr), {
"party_type": party_type,
"party": party,
"account": account,
"account": account
}, as_dict=True)
pe_map = frappe._dict()
@ -688,10 +689,12 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0)
outstanding_amount = flt(d.invoice_amount - payment_amount, precision)
if outstanding_amount > 0.5 / (10**precision):
if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
due_date = frappe.db.get_value(
d.voucher_type, d.voucher_no, "posting_date" if party_type == "Employee" else "due_date")
if (filters.get("outstanding_amt_greater_than") and
not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and
outstanding_amount <= filters.get("outstanding_amt_less_than"))):
continue
if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices:
outstanding_invoices.append(
frappe._dict({
'voucher_no': d.voucher_no,
@ -700,7 +703,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
'invoice_amount': flt(d.invoice_amount),
'payment_amount': payment_amount,
'outstanding_amount': outstanding_amount,
'due_date': due_date
'due_date': d.due_date
})
)
@ -731,6 +734,7 @@ def get_children(doctype, parent, company, is_root=False):
parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_')
fields = [
'name as value',
'root_type',
'is_group as expandable'
]
filters = [['docstatus', '<', 2]]
@ -738,7 +742,7 @@ def get_children(doctype, parent, company, is_root=False):
filters.append(['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent])
if is_root:
fields += ['root_type', 'report_type', 'account_currency'] if doctype == 'Account' else []
fields += ['report_type', 'account_currency'] if doctype == 'Account' else []
filters.append(['company', '=', company])
else:

View File

@ -66,10 +66,11 @@
"net_total",
"total_net_weight",
"taxes_section",
"taxes_and_charges",
"tax_category",
"column_break_50",
"shipping_rule",
"section_break_52",
"taxes_and_charges",
"taxes",
"sec_tax_breakup",
"other_charges_calculation",
@ -569,7 +570,7 @@
{
"fieldname": "taxes_and_charges",
"fieldtype": "Link",
"label": "Taxes and Charges",
"label": "Purchase Taxes and Charges Template",
"oldfieldname": "purchase_other_charges",
"oldfieldtype": "Link",
"options": "Purchase Taxes and Charges Template",
@ -1032,12 +1033,18 @@
"fieldname": "update_auto_repeat_reference",
"fieldtype": "Button",
"label": "Update Auto Repeat Reference"
},
{
"fieldname": "tax_category",
"fieldtype": "Link",
"label": "Tax Category",
"options": "Tax Category"
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"modified": "2019-06-24 21:22:05.483429",
"modified": "2019-07-11 18:25:49.509343",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals
import unittest
import frappe
import json
import frappe.defaults
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from frappe.utils import flt, add_days, nowdate, getdate
@ -15,7 +16,7 @@ from erpnext.stock.doctype.material_request.test_material_request import make_ma
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.controllers.accounts_controller import update_child_qty_rate
import json
from erpnext.controllers.status_updater import OverAllowanceError
class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_receipt(self):
@ -41,7 +42,7 @@ class TestPurchaseOrder(unittest.TestCase):
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 4)
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 50)
pr = create_pr_against_po(po.name, received_qty=8)
self.assertEqual(get_ordered_qty(), existing_ordered_qty)
@ -57,12 +58,12 @@ class TestPurchaseOrder(unittest.TestCase):
def test_ordered_qty_against_pi_with_update_stock(self):
existing_ordered_qty = get_ordered_qty()
po = create_purchase_order()
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 10)
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 50)
frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 20)
pi = make_pi_from_po(po.name)
pi.update_stock = 1
@ -81,6 +82,11 @@ class TestPurchaseOrder(unittest.TestCase):
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 0)
frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 0)
frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 0)
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
def test_update_child_qty_rate(self):
mr = make_material_request(qty=10)
po = make_purchase_order(mr.name)

View File

@ -6,11 +6,12 @@ import frappe
def get_data():
config = [
{
"label": _("Masters and Accounts"),
"label": _("Accounts Receivable"),
"items": [
{
"type": "doctype",
"name": "Item",
"name": "Sales Invoice",
"description": _("Bills raised to Customers."),
"onboard": 1,
},
{
@ -19,12 +20,115 @@ def get_data():
"description": _("Customer database."),
"onboard": 1,
},
{
"type": "doctype",
"name": "Payment Entry",
"description": _("Bank/Cash transactions against party or for internal transfer")
},
{
"type": "doctype",
"name": "Payment Request",
"description": _("Payment Request"),
},
{
"type": "report",
"name": "Accounts Receivable",
"doctype": "Sales Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Accounts Receivable Summary",
"doctype": "Sales Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Sales Register",
"doctype": "Sales Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Item-wise Sales Register",
"is_query_report": True,
"doctype": "Sales Invoice"
},
{
"type": "report",
"name": "Ordered Items To Be Billed",
"is_query_report": True,
"doctype": "Sales Invoice"
},
{
"type": "report",
"name": "Delivered Items To Be Billed",
"is_query_report": True,
"doctype": "Sales Invoice"
},
]
},
{
"label": _("Accounts Payable"),
"items": [
{
"type": "doctype",
"name": "Purchase Invoice",
"description": _("Bills raised by Suppliers."),
"onboard": 1
},
{
"type": "doctype",
"name": "Supplier",
"description": _("Supplier database."),
"onboard": 1,
},
{
"type": "doctype",
"name": "Payment Entry",
"description": _("Bank/Cash transactions against party or for internal transfer")
},
{
"type": "report",
"name": "Accounts Payable",
"doctype": "Purchase Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Accounts Payable Summary",
"doctype": "Purchase Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Purchase Register",
"doctype": "Purchase Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Item-wise Purchase Register",
"is_query_report": True,
"doctype": "Purchase Invoice"
},
{
"type": "report",
"name": "Purchase Order Items To Be Billed",
"is_query_report": True,
"doctype": "Purchase Invoice"
},
{
"type": "report",
"name": "Received Items To Be Billed",
"is_query_report": True,
"doctype": "Purchase Invoice"
},
]
},
{
"label": _("Accounting Masters"),
"items": [
{
"type": "doctype",
"name": "Company",
@ -40,201 +144,31 @@ def get_data():
"description": _("Tree of financial accounts."),
"onboard": 1,
},
]
},
{
"label": _("Billing"),
"items": [
{
"type": "doctype",
"name": "Sales Invoice",
"description": _("Bills raised to Customers."),
"onboard": 1,
},
{
"type": "doctype",
"name": "Purchase Invoice",
"description": _("Bills raised by Suppliers."),
"onboard": 1
},
{
"type": "doctype",
"name": "Payment Request",
"description": _("Payment Request"),
},
{
"type": "doctype",
"name": "Payment Term",
"description": _("Payment Terms based on conditions")
},
# Reports
{
"type": "report",
"name": "Ordered Items To Be Billed",
"is_query_report": True,
"reference_doctype": "Sales Invoice"
},
{
"type": "report",
"name": "Delivered Items To Be Billed",
"is_query_report": True,
"reference_doctype": "Sales Invoice"
},
{
"type": "report",
"name": "Purchase Order Items To Be Billed",
"is_query_report": True,
"reference_doctype": "Purchase Invoice"
},
{
"type": "report",
"name": "Received Items To Be Billed",
"is_query_report": True,
"reference_doctype": "Purchase Invoice"
},
]
},
{
"label": _("Settings"),
"icon": "fa fa-cog",
"items": [
{
"type": "doctype",
"name": "Accounts Settings",
"description": _("Default settings for accounting transactions.")
},
{
"type": "doctype",
"name": "Fiscal Year",
"description": _("Financial / accounting year.")
},
{
"type": "doctype",
"name": "Currency",
"description": _("Enable / disable currencies.")
},
{
"type": "doctype",
"name": "Currency Exchange",
"description": _("Currency exchange rate master.")
},
{
"type": "doctype",
"name": "Exchange Rate Revaluation",
"description": _("Exchange Rate Revaluation master.")
},
{
"type": "doctype",
"name": "Payment Gateway Account",
"description": _("Setup Gateway accounts.")
},
{
"type": "doctype",
"name": "Terms and Conditions",
"label": _("Terms and Conditions Template"),
"description": _("Template of terms or contract.")
},
{
"type": "doctype",
"name": "Mode of Payment",
"description": _("e.g. Bank, Cash, Credit Card")
},
{
"type": "doctype",
"name": "Auto Repeat",
"label": _("Auto Repeat"),
"description": _("To make recurring documents")
},
{
"type": "doctype",
"name": "C-Form",
"description": _("C-Form records"),
"country": "India"
},
{
"type": "doctype",
"name": "Cheque Print Template",
"description": _("Setup cheque dimensions for printing")
},
{
"type": "doctype",
"name": "Accounting Dimension",
"description": _("Setup custom dimensions for accounting")
},
{
"type": "doctype",
"name": "Opening Invoice Creation Tool",
"description": _("Create Opening Sales and Purchase Invoices")
}
]
},
{
"label": _("Accounting Entries"),
"items": [
{
"type": "doctype",
"name": "Payment Entry",
"description": _("Bank/Cash transactions against party or for internal transfer")
"name": "Finance Book",
},
{
"type": "doctype",
"name": "Journal Entry",
"description": _("Accounting journal entries.")
}
]
},
{
"label": _("Financial Statements"),
"items": [
{
"type": "report",
"name": "General Ledger",
"doctype": "GL Entry",
"is_query_report": True,
"name": "Accounting Period",
},
{
"type": "report",
"name": "Accounts Receivable",
"doctype": "Sales Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Accounts Payable",
"doctype": "Purchase Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Trial Balance",
"doctype": "GL Entry",
"is_query_report": True,
},
{
"type": "report",
"name": "Balance Sheet",
"doctype": "GL Entry",
"is_query_report": True
},
{
"type": "report",
"name": "Cash Flow",
"doctype": "GL Entry",
"is_query_report": True
},
{
"type": "report",
"name": "Profit and Loss Statement",
"doctype": "GL Entry",
"is_query_report": True
},
{
"type": "report",
"name": "Consolidated Financial Statement",
"doctype": "GL Entry",
"is_query_report": True
"type": "doctype",
"name": "Payment Term",
"description": _("Payment Terms based on conditions")
},
]
},
@ -243,43 +177,10 @@ def get_data():
"items": [
{
"type": "doctype",
"label": _("Bank"),
"name": "Bank",
"label": _("Match Payments with Invoices"),
"name": "Payment Reconciliation",
"description": _("Match non-linked Invoices and Payments.")
},
{
"type": "page",
"label": _("Reconcile payments and bank transactions"),
"name": "bank-reconciliation",
"description": _("Link bank transactions with payments.")
},
{
"type": "doctype",
"label": _("Bank Account"),
"name": "Bank Account",
},
{
"type": "doctype",
"label": _("Invoice Discounting"),
"name": "Invoice Discounting",
},
{
"type": "doctype",
"label": _("Bank Statement Transaction Entry List"),
"name": "Bank Statement Transaction Entry",
"route": "#List/Bank Statement Transaction Entry",
},
{
"type": "doctype",
"label": _("Bank Statement Transaction Entry Report"),
"name": "Bank Statement Transaction Entry",
"route": "#Report/Bank Statement Transaction Entry",
},
{
"type": "doctype",
"label": _("Bank Statement Settings"),
"name": "Bank Statement Settings",
},
{
"type": "doctype",
"label": _("Update Bank Transaction Dates"),
@ -288,9 +189,8 @@ def get_data():
},
{
"type": "doctype",
"label": _("Match Payments with Invoices"),
"name": "Payment Reconciliation",
"description": _("Match non-linked Invoices and Payments.")
"label": _("Invoice Discounting"),
"name": "Invoice Discounting",
},
{
"type": "report",
@ -306,8 +206,75 @@ def get_data():
},
{
"type": "doctype",
"name": "Bank Guarantee",
"doctype": "Bank Guarantee"
"name": "Bank Guarantee"
},
{
"type": "doctype",
"name": "Cheque Print Template",
"description": _("Setup cheque dimensions for printing")
},
]
},
{
"label": _("General Ledger"),
"items": [
{
"type": "doctype",
"name": "Journal Entry",
"description": _("Accounting journal entries.")
},
{
"type": "report",
"name": "General Ledger",
"doctype": "GL Entry",
"is_query_report": True,
},
{
"type": "report",
"name": "Customer Ledger Summary",
"doctype": "Sales Invoice",
"is_query_report": True,
},
{
"type": "report",
"name": "Supplier Ledger Summary",
"doctype": "Sales Invoice",
"is_query_report": True,
}
]
},
{
"label": _("Taxes"),
"items": [
{
"type": "doctype",
"name": "Sales Taxes and Charges Template",
"description": _("Tax template for selling transactions.")
},
{
"type": "doctype",
"name": "Purchase Taxes and Charges Template",
"description": _("Tax template for buying transactions.")
},
{
"type": "doctype",
"name": "Item Tax Template",
"description": _("Tax template for item tax rates.")
},
{
"type": "doctype",
"name": "Tax Category",
"description": _("Tax Category for overriding tax rates.")
},
{
"type": "doctype",
"name": "Tax Rule",
"description": _("Tax Rule for transactions.")
},
{
"type": "doctype",
"name": "Tax Withholding Category",
"description": _("Tax Withholding rates to be applied on transactions.")
},
]
},
@ -327,6 +294,10 @@ def get_data():
"name": "Budget",
"description": _("Define budget for a financial year.")
},
{
"type": "doctype",
"name": "Accounting Dimension",
},
{
"type": "report",
"name": "Budget Variance Report",
@ -338,51 +309,106 @@ def get_data():
"name": "Monthly Distribution",
"description": _("Seasonality for setting budgets, targets etc.")
},
]
},
{
"label": _("Financial Statements"),
"items": [
{
"type": "report",
"name": "Trial Balance",
"doctype": "GL Entry",
"is_query_report": True,
},
{
"type": "report",
"name": "Profit and Loss Statement",
"doctype": "GL Entry",
"is_query_report": True
},
{
"type": "report",
"name": "Balance Sheet",
"doctype": "GL Entry",
"is_query_report": True
},
{
"type": "report",
"name": "Cash Flow",
"doctype": "GL Entry",
"is_query_report": True
},
{
"type": "report",
"name": "Consolidated Financial Statement",
"doctype": "GL Entry",
"is_query_report": True
},
]
},
{
"label": _("Opening and Closing"),
"items": [
{
"type": "doctype",
"name": "Opening Invoice Creation Tool",
},
{
"type": "doctype",
"name": "Chart of Accounts Importer",
},
{
"type": "doctype",
"name": "Period Closing Voucher",
"description": _("Close Balance Sheet and book Profit or Loss.")
},
]
},
{
"label": _("Taxes"),
"label": _("Multi Currency"),
"items": [
{
"type": "doctype",
"name": "Tax Category",
"description": _("Tax Category for overriding tax rates.")
"name": "Currency",
"description": _("Enable / disable currencies.")
},
{
"type": "doctype",
"name": "Sales Taxes and Charges Template",
"description": _("Tax template for selling transactions.")
"name": "Currency Exchange",
"description": _("Currency exchange rate master.")
},
{
"type": "doctype",
"name": "Purchase Taxes and Charges Template",
"description": _("Tax template for buying transactions.")
"name": "Exchange Rate Revaluation",
"description": _("Exchange Rate Revaluation master.")
},
]
},
{
"label": _("Settings"),
"icon": "fa fa-cog",
"items": [
{
"type": "doctype",
"name": "Payment Gateway Account",
"description": _("Setup Gateway accounts.")
},
{
"type": "doctype",
"name": "Item Tax Template",
"description": _("Tax template for item tax rates.")
"name": "Terms and Conditions",
"label": _("Terms and Conditions Template"),
"description": _("Template of terms or contract.")
},
{
"type": "doctype",
"name": "Tax Rule",
"description": _("Tax Rule for transactions.")
},
{
"type": "doctype",
"name": "Tax Withholding Category",
"description": _("Tax Withholding rates to be applied on transactions.")
"name": "Mode of Payment",
"description": _("e.g. Bank, Cash, Credit Card")
},
]
},
{
"label": _("Subscription Management"),
"icon": "fa fa-microchip ",
"items": [
{
"type": "doctype",
@ -403,7 +429,31 @@ def get_data():
]
},
{
"label": _("Key Reports"),
"label": _("Bank Statement"),
"items": [
{
"type": "doctype",
"label": _("Bank"),
"name": "Bank",
},
{
"type": "doctype",
"label": _("Bank Account"),
"name": "Bank Account",
},
{
"type": "doctype",
"name": "Bank Statement Transaction Entry",
},
{
"type": "doctype",
"label": _("Bank Statement Settings"),
"name": "Bank Statement Settings",
},
]
},
{
"label": _("Profitability"),
"items": [
{
"type": "report",
@ -413,21 +463,9 @@ def get_data():
},
{
"type": "report",
"name": "Sales Register",
"doctype": "Sales Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Purchase Register",
"doctype": "Purchase Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Purchase Invoice Trends",
"name": "Profitability Analysis",
"doctype": "GL Entry",
"is_query_report": True,
"doctype": "Purchase Invoice"
},
{
"type": "report",
@ -437,38 +475,14 @@ def get_data():
},
{
"type": "report",
"name": "Item-wise Sales Register",
"is_query_report": True,
"doctype": "Sales Invoice"
},
{
"type": "report",
"name": "Item-wise Purchase Register",
"name": "Purchase Invoice Trends",
"is_query_report": True,
"doctype": "Purchase Invoice"
},
{
"type": "report",
"name": "Profitability Analysis",
"doctype": "GL Entry",
"is_query_report": True,
},
{
"type": "report",
"name": "Customer Ledger Summary",
"doctype": "Sales Invoice",
"is_query_report": True,
},
{
"type": "report",
"name": "Supplier Ledger Summary",
"doctype": "Sales Invoice",
"is_query_report": True,
}
]
},
{
"label": _("Other Reports"),
"label": _("Reports"),
"icon": "fa fa-table",
"items": [
{
@ -489,18 +503,6 @@ def get_data():
"is_query_report": True,
"doctype": "Sales Invoice"
},
{
"type": "report",
"name": "Accounts Receivable Summary",
"doctype": "Sales Invoice",
"is_query_report": True
},
{
"type": "report",
"name": "Accounts Payable Summary",
"doctype": "Purchase Invoice",
"is_query_report": True
},
{
"type": "report",
"is_query_report": True,
@ -549,27 +551,7 @@ def get_data():
}
]
},
{
"label": _("Help"),
"icon": "fa fa-facetime-video",
"items": [
{
"type": "help",
"label": _("Chart of Accounts"),
"youtube_id": "DyR-DST-PyA"
},
{
"type": "help",
"label": _("Opening Accounting Balance"),
"youtube_id": "kdgM20Q-q68"
},
{
"type": "help",
"label": _("Setting up Taxes"),
"youtube_id": "nQ1zZdPgdaQ"
}
]
}
]
gst = {
@ -617,6 +599,12 @@ def get_data():
"name": "GST Itemised Purchase Register",
"is_query_report": True
},
{
"type": "doctype",
"name": "C-Form",
"description": _("C-Form records"),
"country": "India"
},
]
}
@ -624,6 +612,6 @@ def get_data():
countries = frappe.get_all("Company", fields="country")
countries = [country["country"] for country in countries]
if "India" in countries:
config.insert(7, gst)
config.insert(9, gst)
domains = frappe.get_active_domains()
return config

View File

@ -228,29 +228,5 @@ def get_data():
}
]
},
{
"label": _("Help"),
"items": [
{
"type": "help",
"label": _("Customer and Supplier"),
"youtube_id": "anoGi_RpQ20"
},
{
"type": "help",
"label": _("Material Request to Purchase Order"),
"youtube_id": "4TN9kPyfIqM"
},
{
"type": "help",
"label": _("Purchase Order to Payment"),
"youtube_id": "EK65tLdVUDk"
},
{
"type": "help",
"label": _("Managing Subcontracting"),
"youtube_id": "ThiMCC2DtKo"
},
]
},
]

View File

@ -26,8 +26,8 @@ def get_data():
},
{
"type": "page",
"name": "medical_record",
"label": _("Patient Medical Record"),
"name": "patient_history",
"label": _("Patient History"),
},
{
"type": "page",

View File

@ -4,127 +4,13 @@ from frappe import _
def get_data():
return [
{
"label": _("Employee and Attendance"),
"label": _("Employee"),
"items": [
{
"type": "doctype",
"name": "Employee",
"onboard": 1,
},
{
"type": "doctype",
"name": "Employee Attendance Tool",
"hide_count": True,
"onboard": 1,
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Group",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Attendance",
"onboard": 1,
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Attendance Request",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Upload Attendance",
"hide_count": True,
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Checkin",
"hide_count": True,
"onboard": 1,
"dependencies": ["Employee"]
},
]
},
{
"label": _("Payroll"),
"items": [
{
"type": "doctype",
"name": "Salary Structure",
"onboard": 1,
},
{
"type": "doctype",
"name": "Salary Structure Assignment",
"onboard": 1,
"dependencies": ["Salary Structure", "Employee"],
},
{
"type": "doctype",
"name": "Salary Slip",
"onboard": 1,
},
{
"type": "doctype",
"name": "Payroll Entry",
"onboard": 1,
},
{
"type": "doctype",
"name": "Employee Benefit Application",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Benefit Claim",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Additional Salary",
},
{
"type": "doctype",
"name": "Employee Tax Exemption Declaration",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Tax Exemption Proof Submission",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Incentive",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Retention Bonus",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Payroll Period",
},
{
"type": "doctype",
"name": "Salary Component",
},
]
},
{
"label": _("Settings"),
"icon": "fa fa-cog",
"items": [
{
"type": "doctype",
"name": "HR Settings",
},
{
"type": "doctype",
"name": "Employment Type",
@ -147,19 +33,56 @@ def get_data():
},
{
"type": "doctype",
"name": "Daily Work Summary Group"
"name": "Employee Group",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Health Insurance"
},
{
"type": "doctype",
"name": "Staffing Plan",
}
]
},
{
"label": _("Attendance"),
"items": [
{
"type": "doctype",
"name": "Employee Attendance Tool",
"hide_count": True,
"onboard": 1,
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Attendance",
"onboard": 1,
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Attendance Request",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Upload Attendance",
"hide_count": True,
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Checkin",
"hide_count": True,
"dependencies": ["Employee"]
},
{
"type": "report",
"is_query_report": True,
"name": "Monthly Attendance Sheet",
"doctype": "Attendance"
},
]
},
{
"label": _("Leaves"),
"items": [
@ -175,13 +98,8 @@ def get_data():
},
{
"type": "doctype",
"name": "Compensatory Leave Request",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Leave Encashment",
"dependencies": ["Employee"]
"name": "Leave Policy",
"dependencies": ["Leave Type"]
},
{
"type": "doctype",
@ -194,37 +112,167 @@ def get_data():
},
{
"type": "doctype",
"name": "Leave Policy",
"dependencies": ["Leave Type"]
"name": "Holiday List",
},
{
"type": "doctype",
"name": "Holiday List",
"name": "Compensatory Leave Request",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Leave Encashment",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Leave Block List",
},
{
"type": "report",
"is_query_report": True,
"name": "Employee Leave Balance",
"doctype": "Leave Application"
},
]
},
{
"label": _("Recruitment and Training"),
"label": _("Payroll"),
"items": [
{
"type": "doctype",
"name": "Job Applicant",
"name": "Salary Structure",
"onboard": 1,
},
{
"type": "doctype",
"name": "Salary Structure Assignment",
"onboard": 1,
"dependencies": ["Salary Structure", "Employee"],
},
{
"type": "doctype",
"name": "Payroll Entry",
"onboard": 1,
},
{
"type": "doctype",
"name": "Salary Slip",
"onboard": 1,
},
{
"type": "doctype",
"name": "Salary Component",
},
{
"type": "doctype",
"name": "Additional Salary",
},
{
"type": "doctype",
"name": "Retention Bonus",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Incentive",
"dependencies": ["Employee"]
},
{
"type": "report",
"is_query_report": True,
"name": "Salary Register",
"doctype": "Salary Slip"
},
]
},
{
"label": _("Employee Tax and Benefits"),
"items": [
{
"type": "doctype",
"name": "Employee Tax Exemption Declaration",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Tax Exemption Proof Submission",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Benefit Application",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Benefit Claim",
"dependencies": ["Employee"]
},
]
},
{
"label": _("Employee Lifecycle"),
"items": [
{
"type": "doctype",
"name": "Employee Onboarding",
"dependencies": ["Job Applicant"],
},
{
"type": "doctype",
"name": "Employee Promotion",
"dependencies": ["Employee"],
},
{
"type": "doctype",
"name": "Employee Transfer",
"dependencies": ["Employee"],
},
{
"type": "doctype",
"name": "Employee Separation",
"dependencies": ["Employee"],
},
{
"type": "doctype",
"name": "Employee Onboarding Template",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Separation Template",
"dependencies": ["Employee"]
},
]
},
{
"label": _("Recruitment"),
"items": [
{
"type": "doctype",
"name": "Job Opening",
"onboard": 1,
},
{
"type": "doctype",
"name": "Job Applicant",
"onboard": 1,
},
{
"type": "doctype",
"name": "Job Offer",
"onboard": 1,
},
{
"type": "doctype",
"name": "Staffing Plan",
},
]
},
{
"label": _("Training"),
"items": [
{
"type": "doctype",
"name": "Training Program"
@ -244,42 +292,7 @@ def get_data():
]
},
{
"label": _("Employee Lifecycle"),
"items": [
{
"type": "doctype",
"name": "Employee Transfer",
"dependencies": ["Employee"],
},
{
"type": "doctype",
"name": "Employee Promotion",
"dependencies": ["Employee"],
},
{
"type": "doctype",
"name": "Employee Separation",
"dependencies": ["Employee"],
},
{
"type": "doctype",
"name": "Employee Onboarding",
"dependencies": ["Job Applicant"],
},
{
"type": "doctype",
"name": "Employee Separation Template",
"dependencies": ["Employee"]
},
{
"type": "doctype",
"name": "Employee Onboarding Template",
"dependencies": ["Employee"]
}
]
},
{
"label": _("Appraisals, Expense Claims and Loans"),
"label": _("Performance"),
"items": [
{
"type": "doctype",
@ -290,15 +303,24 @@ def get_data():
"name": "Appraisal Template",
},
{
"type": "page",
"name": "team-updates",
"label": _("Team Updates")
"type": "doctype",
"name": "Energy Point Rule",
},
{
"type": "doctype",
"name": "Employee Advance",
"dependencies": ["Employee"]
"name": "Energy Point Log",
},
{
"type": "link",
"doctype": "Energy Point Log",
"label": _("Energy Point Leaderboard"),
"route": "#social/users"
},
]
},
{
"label": _("Expense Claims"),
"items": [
{
"type": "doctype",
"name": "Expense Claim",
@ -306,8 +328,14 @@ def get_data():
},
{
"type": "doctype",
"name": "Loan Type",
"name": "Employee Advance",
"dependencies": ["Employee"]
},
]
},
{
"label": _("Loans"),
"items": [
{
"type": "doctype",
"name": "Loan Application",
@ -316,19 +344,72 @@ def get_data():
{
"type": "doctype",
"name": "Loan"
}
},
{
"type": "doctype",
"name": "Loan Type",
},
]
},
{
"label": _("Shift Management"),
"items": [
{
"type": "doctype",
"name": "Shift Type",
},
{
"type": "doctype",
"name": "Shift Request",
},
{
"type": "doctype",
"name": "Shift Assignment",
},
]
},
{
"label": _("Fleet Management"),
"items": [
{
"type": "doctype",
"name": "Vehicle"
},
{
"type": "doctype",
"name": "Vehicle Log"
},
{
"type": "report",
"is_query_report": True,
"name": "Vehicle Expenses",
"doctype": "Vehicle"
},
]
},
{
"label": _("Settings"),
"icon": "fa fa-cog",
"items": [
{
"type": "doctype",
"name": "HR Settings",
},
{
"type": "doctype",
"name": "Daily Work Summary Group"
},
{
"type": "page",
"name": "team-updates",
"label": _("Team Updates")
},
]
},
{
"label": _("Reports"),
"icon": "fa fa-list",
"items": [
{
"type": "report",
"is_query_report": True,
"name": "Employee Leave Balance",
"doctype": "Leave Application"
},
{
"type": "report",
"is_query_report": True,
@ -341,29 +422,6 @@ def get_data():
"name": "Employees working on a holiday",
"doctype": "Employee"
},
{
"type": "report",
"name": "Employee Information",
"doctype": "Employee"
},
{
"type": "report",
"is_query_report": True,
"name": "Salary Register",
"doctype": "Salary Slip"
},
{
"type": "report",
"is_query_report": True,
"name": "Monthly Attendance Sheet",
"doctype": "Attendance"
},
{
"type": "report",
"is_query_report": True,
"name": "Vehicle Expenses",
"doctype": "Vehicle"
},
{
"type": "report",
"is_query_report": True,
@ -372,50 +430,4 @@ def get_data():
},
]
},
{
"label": _("Shifts and Fleet Management"),
"items": [
{
"type": "doctype",
"name": "Shift Type",
},
{
"type": "doctype",
"name": "Shift Request",
},
{
"type": "doctype",
"name": "Shift Assignment",
},
{
"type": "doctype",
"name": "Vehicle"
},
{
"type": "doctype",
"name": "Vehicle Log"
},
]
},
# {
# "label": _("Help"),
# "icon": "fa fa-facetime-video",
# "items": [
# {
# "type": "help",
# "label": _("Setting up Employees"),
# "youtube_id": "USfIUdZlUhw"
# },
# {
# "type": "help",
# "label": _("Leave Management"),
# "youtube_id": "fc0p_AXebc8"
# },
# {
# "type": "help",
# "label": _("Expense Claims"),
# "youtube_id": "5SZHJF--ZFY"
# }
# ]
# },
]

View File

@ -88,17 +88,14 @@ def get_data():
"doctype": "Project",
"dependencies": ["Project"],
},
]
},
{
"label": _("Help"),
"icon": "fa fa-facetime-video",
"items": [
{
"type": "help",
"label": _("Managing Projects"),
"youtube_id": "egxIGwtoKI4"
"type": "report",
"is_query_report": True,
"name": "Project Billing Summary",
"doctype": "Project",
"dependencies": ["Project"],
},
]
},
]

View File

@ -318,41 +318,5 @@ def get_data():
}
]
},
{
"label": _("SMS"),
"icon": "fa fa-wrench",
"items": [
{
"type": "doctype",
"name": "SMS Center",
"description":_("Send mass SMS to your contacts"),
},
{
"type": "doctype",
"name": "SMS Log",
"description":_("Logs for maintaining sms delivery status"),
},
]
},
{
"label": _("Help"),
"items": [
{
"type": "help",
"label": _("Customer and Supplier"),
"youtube_id": "anoGi_RpQ20"
},
{
"type": "help",
"label": _("Sales Order to Payment"),
"youtube_id": "1eP90MWoDQM"
},
{
"type": "help",
"label": _("Point-of-Sale"),
"youtube_id": "4WkelWkbP_c"
},
]
},
]

View File

@ -281,9 +281,9 @@ def get_data():
},
{
"type": "report",
"is_query_report": True,
"name": "Item Shortage Report",
"route": "#Report/Bin/Item Shortage Report",
"doctype": "Purchase Receipt"
"doctype": "Bin"
},
{
"type": "report",
@ -329,45 +329,5 @@ def get_data():
}
]
},
{
"label": _("Help"),
"icon": "fa fa-facetime-video",
"items": [
{
"type": "help",
"label": _("Items and Pricing"),
"youtube_id": "qXaEwld4_Ps"
},
{
"type": "help",
"label": _("Item Variants"),
"youtube_id": "OGBETlCzU5o"
},
{
"type": "help",
"label": _("Opening Stock Balance"),
"youtube_id": "0yPgrtfeCTs"
},
{
"type": "help",
"label": _("Making Stock Entries"),
"youtube_id": "Njt107hlY3I"
},
{
"type": "help",
"label": _("Serialized Inventory"),
"youtube_id": "gvOVlEwFDAk"
},
{
"type": "help",
"label": _("Batch Inventory"),
"youtube_id": "J0QKl7ABPKM"
},
{
"type": "help",
"label": _("Managing Subcontracting"),
"youtube_id": "ThiMCC2DtKo"
},
]
}
]

View File

@ -21,13 +21,7 @@ def get_data():
"type": "doctype",
"name": "Issue Priority",
"description": _("Issue Priority."),
},
{
"type": "doctype",
"name": "Communication",
"description": _("Communication log."),
"onboard": 1,
},
}
]
},
{
@ -97,4 +91,15 @@ def get_data():
},
]
},
{
"label": _("Settings"),
"icon": "fa fa-list",
"items": [
{
"type": "doctype",
"name": "Support Settings",
"label": _("Support Settings"),
},
]
},
]

View File

@ -475,21 +475,20 @@ class AccountsController(TransactionBase):
order_doctype = "Purchase Order"
order_list = list(set([d.get(order_field)
for d in self.get("items") if d.get(order_field)]))
for d in self.get("items") if d.get(order_field)]))
journal_entries = get_advance_journal_entries(party_type, party, party_account,
amount_field, order_doctype, order_list, include_unallocated)
amount_field, order_doctype, order_list, include_unallocated)
payment_entries = get_advance_payment_entries(party_type, party, party_account,
order_doctype, order_list, include_unallocated)
order_doctype, order_list, include_unallocated)
res = journal_entries + payment_entries
return res
def is_inclusive_tax(self):
is_inclusive = cint(frappe.db.get_single_value("Accounts Settings",
"show_inclusive_tax_in_print"))
is_inclusive = cint(frappe.db.get_single_value("Accounts Settings", "show_inclusive_tax_in_print"))
if is_inclusive:
is_inclusive = 0
@ -501,7 +500,7 @@ class AccountsController(TransactionBase):
def validate_advance_entries(self):
order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order"
order_list = list(set([d.get(order_field)
for d in self.get("items") if d.get(order_field)]))
for d in self.get("items") if d.get(order_field)]))
if not order_list: return
@ -513,7 +512,7 @@ class AccountsController(TransactionBase):
if not advance_entries_against_si or d.reference_name not in advance_entries_against_si:
frappe.msgprint(_(
"Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.")
.format(d.reference_name, d.against_order))
.format(d.reference_name, d.against_order))
def update_against_document_in_jv(self):
"""
@ -551,9 +550,9 @@ class AccountsController(TransactionBase):
'unadjusted_amount': flt(d.advance_amount),
'allocated_amount': flt(d.allocated_amount),
'exchange_rate': (self.conversion_rate
if self.party_account_currency != self.company_currency else 1),
if self.party_account_currency != self.company_currency else 1),
'grand_total': (self.base_grand_total
if self.party_account_currency == self.company_currency else self.grand_total),
if self.party_account_currency == self.company_currency else self.grand_total),
'outstanding_amount': self.outstanding_amount
})
lst.append(args)
@ -576,36 +575,37 @@ class AccountsController(TransactionBase):
unlink_ref_doc_from_payment_entries(self)
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_tolerance_for
item_tolerance = {}
global_tolerance = None
from erpnext.controllers.status_updater import get_allowance_for
item_allowance = {}
global_qty_allowance, global_amount_allowance = None, None
for item in self.get("items"):
if item.get(item_ref_dn):
ref_amt = flt(frappe.db.get_value(ref_dt + " Item",
item.get(item_ref_dn), based_on), self.precision(based_on, item))
item.get(item_ref_dn), based_on), self.precision(based_on, item))
if not ref_amt:
frappe.msgprint(
_("Warning: System will not check overbilling since amount for Item {0} in {1} is zero").format(
item.item_code, ref_dt))
_("Warning: System will not check overbilling since amount for Item {0} in {1} is zero")
.format(item.item_code, ref_dt))
else:
already_billed = frappe.db.sql("""select sum(%s) from `tab%s`
where %s=%s and docstatus=1 and parent != %s""" %
(based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'),
(item.get(item_ref_dn), self.name))[0][0]
already_billed = frappe.db.sql("""
select sum(%s)
from `tab%s`
where %s=%s and docstatus=1 and parent != %s
""" % (based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'),
(item.get(item_ref_dn), self.name))[0][0]
total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)),
self.precision(based_on, item))
self.precision(based_on, item))
tolerance, item_tolerance, global_tolerance = get_tolerance_for(item.item_code,
item_tolerance, global_tolerance)
allowance, item_allowance, global_qty_allowance, global_amount_allowance = \
get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount")
max_allowed_amt = flt(ref_amt * (100 + tolerance) / 100)
max_allowed_amt = flt(ref_amt * (100 + allowance) / 100)
if total_billed_amt - max_allowed_amt > 0.01:
frappe.throw(_(
"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set in Stock Settings").format(
item.item_code, item.idx, max_allowed_amt))
frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set in Stock Settings")
.format(item.item_code, item.idx, max_allowed_amt))
def get_company_default(self, fieldname):
from erpnext.accounts.utils import get_company_default
@ -615,9 +615,10 @@ class AccountsController(TransactionBase):
stock_items = []
item_codes = list(set(item.item_code for item in self.get("items")))
if item_codes:
stock_items = [r[0] for r in frappe.db.sql("""select name
from `tabItem` where name in (%s) and is_stock_item=1""" % \
(", ".join((["%s"] * len(item_codes))),), item_codes)]
stock_items = [r[0] for r in frappe.db.sql("""
select name from `tabItem`
where name in (%s) and is_stock_item=1
""" % (", ".join((["%s"] * len(item_codes))),), item_codes)]
return stock_items

View File

@ -206,10 +206,11 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999),
idx desc, name
limit %(start)s, %(page_len)s """.format(
fcond=get_filters_cond(doctype, filters, conditions),
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
mcond=get_match_cond(doctype),
key=searchfield), {
'txt': '%' + txt + '%',
key=frappe.db.escape(searchfield)),
{
'txt': "%"+frappe.db.escape(txt)+"%",
'_txt': txt.replace("%", ""),
'start': start or 0,
'page_len': page_len or 20

View File

@ -75,7 +75,7 @@ def validate_returned_items(doc):
items_returned = False
for d in doc.get("items"):
if flt(d.qty) < 0 or d.get('received_qty') < 0:
if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0):
if d.item_code not in valid_items:
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
@ -107,6 +107,9 @@ def validate_returned_items(doc):
items_returned = True
elif d.item_name:
items_returned = True
if not items_returned:
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))

View File

@ -7,6 +7,8 @@ from frappe.utils import flt, comma_or, nowdate, getdate
from frappe import _
from frappe.model.document import Document
class OverAllowanceError(frappe.ValidationError): pass
def validate_status(status, options):
if status not in options:
frappe.throw(_("Status must be one of {0}").format(comma_or(options)))
@ -43,16 +45,6 @@ status_map = {
["Closed", "eval:self.status=='Closed'"],
["On Hold", "eval:self.status=='On Hold'"],
],
"Sales Invoice": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"],
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
],
"Purchase Invoice": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
@ -154,8 +146,9 @@ class StatusUpdater(Document):
def validate_qty(self):
"""Validates qty at row level"""
self.tolerance = {}
self.global_tolerance = None
self.item_allowance = {}
self.global_qty_allowance = None
self.global_amount_allowance = None
for args in self.status_updater:
if "target_ref_field" not in args:
@ -186,32 +179,41 @@ class StatusUpdater(Document):
# if not item[args['target_ref_field']]:
# msgprint(_("Note: System will not check over-delivery and over-booking for Item {0} as quantity or amount is 0").format(item.item_code))
if args.get('no_tolerance'):
if args.get('no_allowance'):
item['reduce_by'] = item[args['target_field']] - item[args['target_ref_field']]
if item['reduce_by'] > .01:
self.limits_crossed_error(args, item)
elif item[args['target_ref_field']]:
self.check_overflow_with_tolerance(item, args)
self.check_overflow_with_allowance(item, args)
def check_overflow_with_tolerance(self, item, args):
def check_overflow_with_allowance(self, item, args):
"""
Checks if there is overflow condering a relaxation tolerance
Checks if there is overflow condering a relaxation allowance
"""
# check if overflow is within tolerance
tolerance, self.tolerance, self.global_tolerance = get_tolerance_for(item['item_code'],
self.tolerance, self.global_tolerance)
qty_or_amount = "qty" if "qty" in args['target_ref_field'] else "amount"
# check if overflow is within allowance
allowance, self.item_allowance, self.global_qty_allowance, self.global_amount_allowance = \
get_allowance_for(item['item_code'], self.item_allowance,
self.global_qty_allowance, self.global_amount_allowance, qty_or_amount)
overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) /
item[args['target_ref_field']]) * 100
if overflow_percent - tolerance > 0.01:
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100)
if overflow_percent - allowance > 0.01:
item['max_allowed'] = flt(item[args['target_ref_field']] * (100+allowance)/100)
item['reduce_by'] = item[args['target_field']] - item['max_allowed']
self.limits_crossed_error(args, item)
self.limits_crossed_error(args, item, qty_or_amount)
def limits_crossed_error(self, args, item):
def limits_crossed_error(self, args, item, qty_or_amount):
'''Raise exception for limits crossed'''
if qty_or_amount == "qty":
action_msg = _('To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.')
else:
action_msg = _('To allow over billing, update "Over Billing Allowance" in Accounts Settings or the Item.')
frappe.throw(_('This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?')
.format(
frappe.bold(_(item["target_ref_field"].title())),
@ -219,9 +221,7 @@ class StatusUpdater(Document):
frappe.bold(_(args.get('target_dt'))),
frappe.bold(_(self.doctype)),
frappe.bold(item.get('item_code'))
) + '<br><br>' +
_('To allow over-billing or over-ordering, update "Allowance" in Stock Settings or the Item.'),
title = _('Limit Crossed'))
) + '<br><br>' + action_msg, OverAllowanceError, title = _('Limit Crossed'))
def update_qty(self, update_modified=True):
"""Updates qty or amount at row level
@ -294,7 +294,7 @@ class StatusUpdater(Document):
frappe.db.sql("""update `tab%(target_parent_dt)s`
set %(target_parent_field)s = round(
ifnull((select
ifnull(sum(if(%(target_ref_field)s > %(target_field)s, abs(%(target_field)s), abs(%(target_ref_field)s))), 0)
ifnull(sum(if(abs(%(target_ref_field)s) > abs(%(target_field)s), abs(%(target_field)s), abs(%(target_ref_field)s))), 0)
/ sum(abs(%(target_ref_field)s)) * 100
from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6)
%(update_modified)s
@ -358,19 +358,34 @@ class StatusUpdater(Document):
ref_doc.db_set("per_billed", per_billed)
ref_doc.set_status(update=True)
def get_tolerance_for(item_code, item_tolerance={}, global_tolerance=None):
def get_allowance_for(item_code, item_allowance={}, global_qty_allowance=None, global_amount_allowance=None, qty_or_amount="qty"):
"""
Returns the tolerance for the item, if not set, returns global tolerance
Returns the allowance for the item, if not set, returns global allowance
"""
if item_tolerance.get(item_code):
return item_tolerance[item_code], item_tolerance, global_tolerance
if qty_or_amount == "qty":
if item_allowance.get(item_code, frappe._dict()).get("qty"):
return item_allowance[item_code].qty, item_allowance, global_qty_allowance, global_amount_allowance
else:
if item_allowance.get(item_code, frappe._dict()).get("amount"):
return item_allowance[item_code].amount, item_allowance, global_qty_allowance, global_amount_allowance
tolerance = flt(frappe.db.get_value('Item',item_code,'tolerance') or 0)
qty_allowance, over_billing_allowance = \
frappe.db.get_value('Item', item_code, ['over_delivery_receipt_allowance', 'over_billing_allowance'])
if not tolerance:
if global_tolerance == None:
global_tolerance = flt(frappe.db.get_value('Stock Settings', None, 'tolerance'))
tolerance = global_tolerance
if qty_or_amount == "qty" and not qty_allowance:
if global_qty_allowance == None:
global_qty_allowance = flt(frappe.db.get_single_value('Stock Settings', 'over_delivery_receipt_allowance'))
qty_allowance = global_qty_allowance
elif qty_or_amount == "amount" and not over_billing_allowance:
if global_amount_allowance == None:
global_amount_allowance = flt(frappe.db.get_single_value('Accounts Settings', 'over_billing_allowance'))
over_billing_allowance = global_amount_allowance
item_tolerance[item_code] = tolerance
return tolerance, item_tolerance, global_tolerance
if qty_or_amount == "qty":
allowance = qty_allowance
item_allowance.setdefault(item_code, frappe._dict()).setdefault("qty", qty_allowance)
else:
allowance = over_billing_allowance
item_allowance.setdefault(item_code, frappe._dict()).setdefault("amount", over_billing_allowance)
return allowance, item_allowance, global_qty_allowance, global_amount_allowance

View File

@ -15,6 +15,9 @@ class calculate_taxes_and_totals(object):
self.calculate()
def calculate(self):
if not len(self.doc.get("items")):
return
self.discount_amount_applied = False
self._calculate()

View File

@ -7,7 +7,9 @@ import frappe
from frappe import _
from frappe.model.document import Document
from requests_oauthlib import OAuth2Session
import json, requests
import json
import requests
import traceback
from erpnext import encode_company_abbr
# QuickBooks requires a redirect URL, User will be redirect to this URL
@ -32,7 +34,6 @@ def callback(*args, **kwargs):
class QuickBooksMigrator(Document):
def __init__(self, *args, **kwargs):
super(QuickBooksMigrator, self).__init__(*args, **kwargs)
from pprint import pprint
self.oauth = OAuth2Session(
client_id=self.client_id,
redirect_uri=self.redirect_url,
@ -46,7 +47,9 @@ class QuickBooksMigrator(Document):
if self.company:
# We need a Cost Center corresponding to the selected erpnext Company
self.default_cost_center = frappe.db.get_value('Company', self.company, 'cost_center')
self.default_warehouse = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0})[0]["name"]
company_warehouses = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0})
if company_warehouses:
self.default_warehouse = company_warehouses[0].name
if self.authorization_endpoint:
self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0]
@ -218,7 +221,7 @@ class QuickBooksMigrator(Document):
def _fetch_general_ledger(self):
try:
query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint ,self.quickbooks_company_id)
query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint, self.quickbooks_company_id)
response = self._get(query_uri,
params={
"columns": ",".join(["tx_date", "txn_type", "credit_amt", "debt_amt"]),
@ -493,17 +496,17 @@ class QuickBooksMigrator(Document):
"account_currency": customer["CurrencyRef"]["value"],
"company": self.company,
})[0]["name"]
except Exception as e:
except Exception:
receivable_account = None
erpcustomer = frappe.get_doc({
"doctype": "Customer",
"quickbooks_id": customer["Id"],
"customer_name" : encode_company_abbr(customer["DisplayName"], self.company),
"customer_type" : "Individual",
"customer_group" : "Commercial",
"customer_name": encode_company_abbr(customer["DisplayName"], self.company),
"customer_type": "Individual",
"customer_group": "Commercial",
"default_currency": customer["CurrencyRef"]["value"],
"accounts": [{"company": self.company, "account": receivable_account}],
"territory" : "All Territories",
"territory": "All Territories",
"company": self.company,
}).insert()
if "BillAddr" in customer:
@ -521,7 +524,7 @@ class QuickBooksMigrator(Document):
item_dict = {
"doctype": "Item",
"quickbooks_id": item["Id"],
"item_code" : encode_company_abbr(item["Name"], self.company),
"item_code": encode_company_abbr(item["Name"], self.company),
"stock_uom": "Unit",
"is_stock_item": 0,
"item_group": "All Item Groups",
@ -549,14 +552,14 @@ class QuickBooksMigrator(Document):
erpsupplier = frappe.get_doc({
"doctype": "Supplier",
"quickbooks_id": vendor["Id"],
"supplier_name" : encode_company_abbr(vendor["DisplayName"], self.company),
"supplier_group" : "All Supplier Groups",
"supplier_name": encode_company_abbr(vendor["DisplayName"], self.company),
"supplier_group": "All Supplier Groups",
"company": self.company,
}).insert()
if "BillAddr" in vendor:
self._create_address(erpsupplier, "Supplier", vendor["BillAddr"], "Billing")
if "ShipAddr" in vendor:
self._create_address(erpsupplier, "Supplier",vendor["ShipAddr"], "Shipping")
self._create_address(erpsupplier, "Supplier", vendor["ShipAddr"], "Shipping")
except Exception as e:
self._log_error(e)
@ -829,7 +832,7 @@ class QuickBooksMigrator(Document):
"currency": invoice["CurrencyRef"]["value"],
"conversion_rate": invoice.get("ExchangeRate", 1),
"posting_date": invoice["TxnDate"],
"due_date": invoice.get("DueDate", invoice["TxnDate"]),
"due_date": invoice.get("DueDate", invoice["TxnDate"]),
"credit_to": credit_to_account,
"supplier": frappe.get_all("Supplier",
filters={
@ -1200,7 +1203,7 @@ class QuickBooksMigrator(Document):
def _create_address(self, entity, doctype, address, address_type):
try :
try:
if not frappe.db.exists({"doctype": "Address", "quickbooks_id": address["Id"]}):
frappe.get_doc({
"doctype": "Address",
@ -1252,8 +1255,6 @@ class QuickBooksMigrator(Document):
def _log_error(self, execption, data=""):
import json, traceback
traceback.print_exc()
frappe.log_error(title="QuickBooks Migration Error",
message="\n".join([
"Data",

View File

@ -21,9 +21,9 @@ frappe.ui.form.on('Patient', {
});
}
if (frm.doc.patient_name && frappe.user.has_role("Physician")) {
frm.add_custom_button(__('Medical Record'), function () {
frm.add_custom_button(__('Patient History'), function () {
frappe.route_options = { "patient": frm.doc.name };
frappe.set_route("medical_record");
frappe.set_route("patient_history");
},"View");
}
if (!frm.doc.__islocal && (frappe.user.has_role("Nursing User") || frappe.user.has_role("Physician"))) {

View File

@ -30,9 +30,9 @@ frappe.ui.form.on('Patient Appointment', {
};
});
if(frm.doc.patient){
frm.add_custom_button(__('Medical Record'), function() {
frm.add_custom_button(__('Patient History'), function() {
frappe.route_options = {"patient": frm.doc.patient};
frappe.set_route("medical_record");
frappe.set_route("patient_history");
},__("View"));
}
if(frm.doc.status == "Open"){

View File

@ -41,10 +41,10 @@ frappe.ui.form.on('Patient Encounter', {
}
});
}
frm.add_custom_button(__('Medical Record'), function() {
frm.add_custom_button(__('Patient History'), function() {
if (frm.doc.patient) {
frappe.route_options = {"patient": frm.doc.patient};
frappe.set_route("medical_record");
frappe.set_route("patient_history");
} else {
frappe.msgprint(__("Please select Patient"));
}

View File

@ -1 +0,0 @@
from __future__ import unicode_literals

View File

@ -1,182 +0,0 @@
frappe.provide("frappe.medical_record");
frappe.pages['medical_record'].on_page_load = function(wrapper) {
var me = this;
var page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Medical Record',
});
frappe.breadcrumbs.add("Medical");
page.main.html(frappe.render_template("patient_select", {}));
var patient = frappe.ui.form.make_control({
parent: page.main.find(".patient"),
df: {
fieldtype: "Link",
options: "Patient",
fieldname: "patient",
change: function(){
page.main.find(".frappe-list").html("");
draw_page(patient.get_value(), me);
}
},
only_input: true,
});
patient.refresh();
this.page.main.on("click", ".medical_record-message", function() {
var doctype = $(this).attr("data-doctype"),
docname = $(this).attr("data-docname");
if (doctype && docname) {
frappe.route_options = {
scroll_to: { "doctype": doctype, "name": docname }
};
frappe.set_route(["Form", doctype, docname]);
}
});
this.page.sidebar.on("click", ".edit-details", function() {
patient = patient.get_value();
if (patient) {
frappe.set_route(["Form", "Patient", patient]);
}
});
};
frappe.pages['medical_record'].refresh = function() {
var me = this;
if(frappe.route_options) {
if(frappe.route_options.patient){
me.page.main.find(".frappe-list").html("");
var patient = frappe.route_options.patient;
draw_page(patient,me);
me.page.main.find("[data-fieldname='patient']").val(patient);
frappe.route_options = null;
}
}
};
var show_patient_info = function(patient, me){
frappe.call({
"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
args: {
patient: patient
},
callback: function (r) {
var data = r.message;
var details = "";
if(data.email) details += "<br><b>Email :</b> " + data.email;
if(data.mobile) details += "<br><b>Mobile :</b> " + data.mobile;
if(data.occupation) details += "<br><b>Occupation :</b> " + data.occupation;
if(data.blood_group) details += "<br><b>Blood group : </b> " + data.blood_group;
if(data.allergies) details += "<br><br><b>Allergies : </b> "+ data.allergies;
if(data.medication) details += "<br><b>Medication : </b> "+ data.medication;
if(data.alcohol_current_use) details += "<br><br><b>Alcohol use : </b> "+ data.alcohol_current_use;
if(data.alcohol_past_use) details += "<br><b>Alcohol past use : </b> "+ data.alcohol_past_use;
if(data.tobacco_current_use) details += "<br><b>Tobacco use : </b> "+ data.tobacco_current_use;
if(data.tobacco_past_use) details += "<br><b>Tobacco past use : </b> "+ data.tobacco_past_use;
if(data.medical_history) details += "<br><br><b>Medical history : </b> "+ data.medical_history;
if(data.surgical_history) details += "<br><b>Surgical history : </b> "+ data.surgical_history;
if(data.surrounding_factors) details += "<br><br><b>Occupational hazards : </b> "+ data.surrounding_factors;
if(data.other_risk_factors) details += "<br><b>Other risk factors : </b> " + data.other_risk_factors;
if(data.patient_details) details += "<br><br><b>More info : </b> " + data.patient_details;
if(details){
details = "<div style='padding-left:10px; font-size:13px;' align='center'></br><b class='text-muted'>Patient Details</b>" + details + "</div>";
}
var vitals = "";
if(data.temperature) vitals += "<br><b>Temperature :</b> " + data.temperature;
if(data.pulse) vitals += "<br><b>Pulse :</b> " + data.pulse;
if(data.respiratory_rate) vitals += "<br><b>Respiratory Rate :</b> " + data.respiratory_rate;
if(data.bp) vitals += "<br><b>BP :</b> " + data.bp;
if(data.bmi) vitals += "<br><b>BMI :</b> " + data.bmi;
if(data.height) vitals += "<br><b>Height :</b> " + data.height;
if(data.weight) vitals += "<br><b>Weight :</b> " + data.weight;
if(data.signs_date) vitals += "<br><b>Date :</b> " + data.signs_date;
if(vitals){
vitals = "<div style='padding-left:10px; font-size:13px;' align='center'></br><b class='text-muted'>Vital Signs</b>" + vitals + "<br></div>";
details = vitals + details;
}
if(details) details += "<div align='center'><br><a class='btn btn-default btn-sm edit-details'>Edit Details</a></b> </div>";
me.page.sidebar.addClass("col-sm-3");
me.page.sidebar.html(details);
me.page.wrapper.find(".layout-main-section-wrapper").addClass("col-sm-9");
}
});
};
var draw_page = function(patient, me){
frappe.model.with_doctype("Patient Medical Record", function() {
me.page.list = new frappe.ui.BaseList({
hide_refresh: true,
page: me.page,
method: 'erpnext.healthcare.page.medical_record.medical_record.get_feed',
args: {name: patient},
parent: $("<div></div>").appendTo(me.page.main),
render_view: function(values) {
var me = this;
var wrapper = me.page.main.find(".result-list").get(0);
values.map(function (value) {
var row = $('<div class="list-row">').data("data", value).appendTo($(wrapper)).get(0);
new frappe.medical_record.Feed(row, value);
});
},
show_filters: true,
doctype: "Patient Medical Record",
});
show_patient_info(patient, me);
me.page.list.run();
});
};
frappe.medical_record.last_feed_date = false;
frappe.medical_record.Feed = Class.extend({
init: function(row, data) {
this.scrub_data(data);
this.add_date_separator(row, data);
if(!data.add_class)
data.add_class = "label-default";
data.link = "";
if (data.reference_doctype && data.reference_name) {
data.link = frappe.format(data.reference_name, {fieldtype: "Link", options: data.reference_doctype},
{label: __(data.reference_doctype)});
}
$(row)
.append(frappe.render_template("medical_record_row", data))
.find("a").addClass("grey");
},
scrub_data: function(data) {
data.by = frappe.user.full_name(data.owner);
data.imgsrc = frappe.utils.get_file_link(frappe.user_info(data.owner).image);
data.icon = "icon-flag";
},
add_date_separator: function(row, data) {
var date = frappe.datetime.str_to_obj(data.creation);
var last = frappe.medical_record.last_feed_date;
if((last && frappe.datetime.obj_to_str(last) != frappe.datetime.obj_to_str(date)) || (!last)) {
var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date));
if(diff < 1) {
var pdate = 'Today';
} else if(diff < 2) {
pdate = 'Yesterday';
} else {
pdate = frappe.datetime.global_date_format(date);
}
data.date_sep = pdate;
data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator";
} else {
data.date_sep = null;
data.date_class = "";
}
frappe.medical_record.last_feed_date = date;
}
});

View File

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, ESS LLP and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import cint
@frappe.whitelist()
def get_feed(start, page_length, name):
"""get feed"""
result = frappe.db.sql("""select name, owner, modified, creation,
reference_doctype, reference_name, subject
from `tabPatient Medical Record`
where patient=%(patient)s
order by creation desc
limit %(start)s, %(page_length)s""",
{
"start": cint(start),
"page_length": cint(page_length),
"patient": name
}, as_dict=True)
return result

View File

@ -1,21 +0,0 @@
<div class="row medical_record-row" data-creation="{%= creation.split(" ")[0] + " 00:00:00" %}">
<div class="col-xs-3 text-right medical_record-date"><span class="{%= date_class %}">
{%= date_sep || "" %}</span>
</div>
<div class="col-xs-9 medical_record-message"
data-doctype="{%= reference_doctype %}"
data-docname="{%= reference_name %}"
title="{%= by %} / {%= frappe.datetime.str_to_user(creation) %}">
<span class="avatar avatar-small">
<div class="avatar-frame" style="background-image: url({{ imgsrc }});"></div>
<!-- <img src="{%= imgsrc %}"> -->
</span>
<span class="small">
{% if (reference_doctype && reference_name) { %}
{%= __("{0}: {1}", [link, "<strong>" + subject + "</strong>"]) %}
{% } else { %}
{%= subject %}
{% } %}
</span>
</div>
</div>

View File

@ -1,5 +0,0 @@
<div class="text-center col-sm-9" style="padding: 40px;">
<p>{%= __("Select Patient") %}</p>
<p class="patient" style="margin: auto; max-width: 300px; margin-bottom: 20px;"></p>
</div>

View File

@ -14,6 +14,10 @@
margin-bottom: -4px;
}
.medical_record-row > * {
z-index: -999;
}
.date-indicator {
background:none;
font-size:12px;
@ -35,6 +39,61 @@
color: #5e64ff;
}
.div-bg-color {
background: #fafbfc;
}
.bg-color-white {
background: #FFFFFF;
}
.d-flex {
display: flex;
}
.width-full {
width: 100%;
}
.p-3 {
padding: 16px;
}
.mt-2 {
margin-top: 8px;
}
.mr-3 {
margin-right: 16px;
}
.Box {
background-color: #fff;
border: 1px solid #d1d5da;
border-radius: 3px;
}
.flex-column {
flex-direction: column;
}
.avatar {
display: inline-block;
overflow: hidden;
line-height: 1;
vertical-align: middle;
border-radius: 3px;
}
.py-3 {
padding-top: 16px;
padding-bottom: 16px;
}
.border-bottom {
border-bottom: 1px #e1e4e8 solid;
}
.date-indicator.blue::after {
background: #5e64ff;
}
@ -65,8 +124,3 @@
#page-medical_record .list-filters {
display: none ;
}
#page-medical_record .octicon-heart {
color: #ff5858;
margin: 0px 5px;
}

View File

@ -0,0 +1,20 @@
<div class="col-sm-12">
<div class="col-sm-3">
<p class="text-center">{%= __("Select Patient") %}</p>
<p class="patient" style="margin: auto; max-width: 300px; margin-bottom: 20px;"></p>
<div class="patient_details" style="z-index=0"></div>
</div>
<div class="col-sm-9 patient_documents">
<div class="col-sm-12">
<div class="col-sm-12 show_chart_btns" align="center">
</div>
<div id="chart" class="col-sm-12 patient_vital_charts">
</div>
</div>
<div class="col-sm-12 patient_documents_list">
</div>
<div class="col-sm-12 text-center py-3">
<a class="btn btn-sm btn-default btn-get-records" style="display:none">More..</a>
</div>
</div>
</div>

View File

@ -0,0 +1,300 @@
frappe.provide("frappe.patient_history");
frappe.pages['patient_history'].on_page_load = function(wrapper) {
var me = this;
var page = frappe.ui.make_app_page({
parent: wrapper,
title: 'Patient History',
single_column: true
});
frappe.breadcrumbs.add("Healthcare");
let pid = '';
page.main.html(frappe.render_template("patient_history", {}));
var patient = frappe.ui.form.make_control({
parent: page.main.find(".patient"),
df: {
fieldtype: "Link",
options: "Patient",
fieldname: "patient",
change: function(){
if(pid != patient.get_value() && patient.get_value()){
me.start = 0;
me.page.main.find(".patient_documents_list").html("");
get_documents(patient.get_value(), me);
show_patient_info(patient.get_value(), me);
show_patient_vital_charts(patient.get_value(), me, "bp", "mmHg", "Blood Pressure");
}
pid = patient.get_value();
}
},
only_input: true,
});
patient.refresh();
if (frappe.route_options){
patient.set_value(frappe.route_options.patient);
}
this.page.main.on("click", ".btn-show-chart", function() {
var btn_show_id = $(this).attr("data-show-chart-id"), pts = $(this).attr("data-pts");
var title = $(this).attr("data-title");
show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title);
});
this.page.main.on("click", ".btn-more", function() {
var doctype = $(this).attr("data-doctype"), docname = $(this).attr("data-docname");
if(me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched') == "1"){
me.page.main.find("."+docname).hide();
me.page.main.find("."+docname).parent().find('.document-html').show();
}else{
if(doctype && docname){
let exclude = ["patient", "patient_name", 'patient_sex', "encounter_date"];
frappe.call({
method: "erpnext.healthcare.utils.render_doc_as_html",
args:{
doctype: doctype,
docname: docname,
exclude_fields: exclude
},
callback: function(r) {
if (r.message){
me.page.main.find("."+docname).hide();
me.page.main.find("."+docname).parent().find('.document-html').html(r.message.html+"\
<div align='center'><a class='btn octicon octicon-chevron-up btn-default btn-xs\
btn-less' data-doctype='"+doctype+"' data-docname='"+docname+"'></a></div>");
me.page.main.find("."+docname).parent().find('.document-html').show();
me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched', "1");
}
},
freeze: true
});
}
}
});
this.page.main.on("click", ".btn-less", function() {
var docname = $(this).attr("data-docname");
me.page.main.find("."+docname).parent().find('.document-id').show();
me.page.main.find("."+docname).parent().find('.document-html').hide();
});
me.start = 0;
me.page.main.on("click", ".btn-get-records", function(){
get_documents(patient.get_value(), me);
});
};
var get_documents = function(patient, me){
frappe.call({
"method": "erpnext.healthcare.page.patient_history.patient_history.get_feed",
args: {
name: patient,
start: me.start,
page_length: 20
},
callback: function (r) {
var data = r.message;
if(data.length){
add_to_records(me, data);
}else{
me.page.main.find(".patient_documents_list").append("<div class='text-muted' align='center'><br><br>No more records..<br><br></div>");
me.page.main.find(".btn-get-records").hide();
}
}
});
};
var add_to_records = function(me, data){
var details = "<ul class='nav nav-pills nav-stacked'>";
var i;
for(i=0; i<data.length; i++){
if(data[i].reference_doctype){
let label = '';
if(data[i].subject){
label += "<br/>"+data[i].subject;
}
data[i] = add_date_separator(data[i]);
if(frappe.user_info(data[i].owner).image){
data[i].imgsrc = frappe.utils.get_file_link(frappe.user_info(data[i].owner).image);
}
else{
data[i].imgsrc = false;
}
var time_line_heading = data[i].practitioner ? `${data[i].practitioner} ` : ``;
time_line_heading += data[i].reference_doctype + " - "+ data[i].reference_name;
details += `<li data-toggle='pill' class='patient_doc_menu'
data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
<div class='col-sm-12 d-flex border-bottom py-3'>`;
if (data[i].imgsrc){
details += `<span class='mr-3'>
<img class='avtar' src='${data[i].imgsrc}' width='32' height='32'>
</img>
</span>`;
}else{
details += `<span class='mr-3 avatar avatar-small' style='width:32px; height:32px;'><div align='center' class='standard-image'
style='background-color: #fafbfc;'>${data[i].practitioner ? data[i].practitioner.charAt(0) : "U"}</div></span>`;
}
details += `<div class='d-flex flex-column width-full'>
<div>
`+time_line_heading+` on
<span>
${data[i].date_sep}
</span>
</div>
<div class='Box p-3 mt-2'>
<span class='${data[i].reference_name} document-id'>${label}
<div align='center'>
<a class='btn octicon octicon-chevron-down btn-default btn-xs btn-more'
data-doctype='${data[i].reference_doctype}' data-docname='${data[i].reference_name}'>
</a>
</div>
</span>
<span class='document-html' hidden data-fetched="0">
</span>
</div>
</div>
</div>
</li>`;
}
}
details += "</ul>";
me.page.main.find(".patient_documents_list").append(details);
me.start += data.length;
if(data.length===20){
me.page.main.find(".btn-get-records").show();
}else{
me.page.main.find(".btn-get-records").hide();
me.page.main.find(".patient_documents_list").append("<div class='text-muted' align='center'><br><br>No more records..<br><br></div>");
}
};
var add_date_separator = function(data) {
var date = frappe.datetime.str_to_obj(data.creation);
var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date));
if(diff < 1) {
var pdate = 'Today';
} else if(diff < 2) {
pdate = 'Yesterday';
} else {
pdate = frappe.datetime.global_date_format(date);
}
data.date_sep = pdate;
return data;
};
var show_patient_info = function(patient, me){
frappe.call({
"method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
args: {
patient: patient
},
callback: function (r) {
var data = r.message;
var details = "";
if(data.image){
details += "<div><img class='thumbnail' width=75% src='"+data.image+"'></div>";
}
details += "<b>" + data.patient_name +"</b><br>" + data.sex;
if(data.email) details += "<br>" + data.email;
if(data.mobile) details += "<br>" + data.mobile;
if(data.occupation) details += "<br><br><b>Occupation :</b> " + data.occupation;
if(data.blood_group) details += "<br><b>Blood group : </b> " + data.blood_group;
if(data.allergies) details += "<br><br><b>Allergies : </b> "+ data.allergies.replace("\n", "<br>");
if(data.medication) details += "<br><b>Medication : </b> "+ data.medication.replace("\n", "<br>");
if(data.alcohol_current_use) details += "<br><br><b>Alcohol use : </b> "+ data.alcohol_current_use;
if(data.alcohol_past_use) details += "<br><b>Alcohol past use : </b> "+ data.alcohol_past_use;
if(data.tobacco_current_use) details += "<br><b>Tobacco use : </b> "+ data.tobacco_current_use;
if(data.tobacco_past_use) details += "<br><b>Tobacco past use : </b> "+ data.tobacco_past_use;
if(data.medical_history) details += "<br><br><b>Medical history : </b> "+ data.medical_history.replace("\n", "<br>");
if(data.surgical_history) details += "<br><b>Surgical history : </b> "+ data.surgical_history.replace("\n", "<br>");
if(data.surrounding_factors) details += "<br><br><b>Occupational hazards : </b> "+ data.surrounding_factors.replace("\n", "<br>");
if(data.other_risk_factors) details += "<br><b>Other risk factors : </b> " + data.other_risk_factors.replace("\n", "<br>");
if(data.patient_details) details += "<br><br><b>More info : </b> " + data.patient_details.replace("\n", "<br>");
if(details){
details = "<div style='padding-left:10px; font-size:13px;' align='center'>" + details + "</div>";
}
me.page.main.find(".patient_details").html(details);
}
});
};
var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) {
frappe.call({
method: "erpnext.healthcare.utils.get_patient_vitals",
args:{
patient: patient
},
callback: function(r) {
if (r.message){
var show_chart_btns_html = "<div style='padding-top:5px;'><a class='btn btn-default btn-xs btn-show-chart' \
data-show-chart-id='bp' data-pts='mmHg' data-title='Blood Pressure'>Blood Pressure</a>\
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='pulse_rate' \
data-pts='per Minutes' data-title='Respiratory/Pulse Rate'>Respiratory/Pulse Rate</a>\
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='temperature' \
data-pts='°C or °F' data-title='Temperature'>Temperature</a>\
<a class='btn btn-default btn-xs btn-show-chart' data-show-chart-id='bmi' \
data-pts='bmi' data-title='BMI'>BMI</a></div>";
me.page.main.find(".show_chart_btns").html(show_chart_btns_html);
var data = r.message;
let labels = [], datasets = [];
let bp_systolic = [], bp_diastolic = [], temperature = [];
let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = [];
for(var i=0; i<data.length; i++){
labels.push(data[i].signs_date+"||"+data[i].signs_time);
if(btn_show_id=="bp"){
bp_systolic.push(data[i].bp_systolic);
bp_diastolic.push(data[i].bp_diastolic);
}
if(btn_show_id=="temperature"){
temperature.push(data[i].temperature);
}
if(btn_show_id=="pulse_rate"){
pulse.push(data[i].pulse);
respiratory_rate.push(data[i].respiratory_rate);
}
if(btn_show_id=="bmi"){
bmi.push(data[i].bmi);
height.push(data[i].height);
weight.push(data[i].weight);
}
}
if(btn_show_id=="temperature"){
datasets.push({name: "Temperature", values: temperature, chartType:'line'});
}
if(btn_show_id=="bmi"){
datasets.push({name: "BMI", values: bmi, chartType:'line'});
datasets.push({name: "Height", values: height, chartType:'line'});
datasets.push({name: "Weight", values: weight, chartType:'line'});
}
if(btn_show_id=="bp"){
datasets.push({name: "BP Systolic", values: bp_systolic, chartType:'line'});
datasets.push({name: "BP Diastolic", values: bp_diastolic, chartType:'line'});
}
if(btn_show_id=="pulse_rate"){
datasets.push({name: "Heart Rate / Pulse", values: pulse, chartType:'line'});
datasets.push({name: "Respiratory Rate", values: respiratory_rate, chartType:'line'});
}
new Chart( ".patient_vital_charts", {
data: {
labels: labels,
datasets: datasets
},
title: title,
type: 'axis-mixed', // 'axis-mixed', 'bar', 'line', 'pie', 'percentage'
height: 150,
colors: ['purple', '#ffa3ef', 'light-blue'],
tooltipOptions: {
formatTooltipX: d => (d + '').toUpperCase(),
formatTooltipY: d => d + ' ' + pts,
}
});
}else{
me.page.main.find(".patient_vital_charts").html("");
me.page.main.find(".show_chart_btns").html("");
}
}
});
};

View File

@ -1,18 +1,21 @@
{
"content": null,
"creation": "2016-06-09 11:33:14.025787",
"creation": "2018-08-08 17:09:13.816199",
"docstatus": 0,
"doctype": "Page",
"icon": "icon-play",
"icon": "",
"idx": 0,
"modified": "2018-08-06 11:40:39.705660",
"modified": "2018-08-08 17:09:55.969424",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "medical_record",
"name": "patient_history",
"owner": "Administrator",
"page_name": "medical_record",
"page_name": "patient_history",
"restrict_to_domain": "Healthcare",
"roles": [
{
"role": "Healthcare Administrator"
},
{
"role": "Physician"
}
@ -21,5 +24,5 @@
"standard": "Yes",
"style": null,
"system_page": 0,
"title": "Medical Record"
"title": "Patient History"
}

View File

@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, ESS LLP and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import cint
from erpnext.healthcare.utils import render_docs_as_html
@frappe.whitelist()
def get_feed(name, start=0, page_length=20):
"""get feed"""
result = frappe.db.sql("""select name, owner, creation,
reference_doctype, reference_name, subject
from `tabPatient Medical Record`
where patient=%(patient)s
order by creation desc
limit %(start)s, %(page_length)s""",
{
"patient": name,
"start": cint(start),
"page_length": cint(page_length)
}, as_dict=True)
return result
@frappe.whitelist()
def get_feed_for_dt(doctype, docname):
"""get feed"""
result = frappe.db.sql("""select name, owner, modified, creation,
reference_doctype, reference_name, subject
from `tabPatient Medical Record`
where reference_name=%(docname)s and reference_doctype=%(doctype)s
order by creation desc""",
{
"docname": docname,
"doctype": doctype
}, as_dict=True)
return result

View File

@ -429,3 +429,116 @@ def get_children(doctype, parent, company, is_root=False):
occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total)
each["occupied_out_of_vacant"] = occupancy_msg
return hc_service_units
@frappe.whitelist()
def get_patient_vitals(patient, from_date=None, to_date=None):
if not patient: return
vitals = frappe.db.sql("""select * from `tabVital Signs` where \
docstatus=1 and patient=%s order by signs_date, signs_time""", \
(patient), as_dict=1)
if vitals and vitals[0]:
return vitals
else:
return False
@frappe.whitelist()
def render_docs_as_html(docs):
# docs key value pair {doctype: docname}
docs_html = "<div class='col-md-12 col-sm-12 text-muted'>"
for doc in docs:
docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + "<br/>"
return {'html': docs_html}
@frappe.whitelist()
def render_doc_as_html(doctype, docname, exclude_fields = []):
#render document as html, three column layout will break
doc = frappe.get_doc(doctype, docname)
meta = frappe.get_meta(doctype)
doc_html = "<div class='col-md-12 col-sm-12'>"
section_html = ""
section_label = ""
html = ""
sec_on = False
col_on = 0
has_data = False
for df in meta.fields:
#on section break append append previous section and html to doc html
if df.fieldtype == "Section Break":
if has_data and col_on and sec_on:
doc_html += section_html + html + "</div>"
elif has_data and not col_on and sec_on:
doc_html += "<div class='col-md-12 col-sm-12'\
><div class='col-md-12 col-sm-12'>" \
+ section_html + html +"</div></div>"
while col_on:
doc_html += "</div>"
col_on -= 1
sec_on = True
has_data= False
col_on = 0
section_html = ""
html = ""
if df.label:
section_label = df.label
continue
#on column break append html to section html or doc html
if df.fieldtype == "Column Break":
if sec_on and has_data:
section_html += "<div class='col-md-12 col-sm-12'\
><div class='col-md-6 col\
-sm-6'><b>" + section_label + "</b>" + html + "</div><div \
class='col-md-6 col-sm-6'>"
elif has_data:
doc_html += "<div class='col-md-12 col-sm-12'><div class='col-m\
d-6 col-sm-6'>" + html + "</div><div class='col-md-6 col-sm-6'>"
elif sec_on and not col_on:
section_html += "<div class='col-md-6 col-sm-6'>"
html = ""
col_on += 1
if df.label:
html += '<br>' + df.label
continue
#on table iterate in items and create table based on in_list_view, append to section html or doc html
if df.fieldtype == "Table":
items = doc.get(df.fieldname)
if not items: continue
child_meta = frappe.get_meta(df.options)
if not has_data : has_data = True
table_head = ""
table_row = ""
create_head = True
for item in items:
table_row += '<tr>'
for cdf in child_meta.fields:
if cdf.in_list_view:
if create_head:
table_head += '<th>' + cdf.label + '</th>'
if item.get(cdf.fieldname):
table_row += '<td>' + str(item.get(cdf.fieldname)) \
+ '</td>'
else:
table_row += '<td></td>'
create_head = False
table_row += '</tr>'
if sec_on:
section_html += '<table class="table table-condensed \
bordered">' + table_head + table_row + '</table>'
else:
html += '<table class="table table-condensed table-bordered">' \
+ table_head + table_row + '</table>'
continue
#on other field types add label and value to html
if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields:
html += "<br>{0} : {1}".format(df.label or df.fieldname, \
doc.get(df.fieldname))
if not has_data : has_data = True
if sec_on and col_on and has_data:
doc_html += section_html + html + "</div></div>"
elif sec_on and not col_on and has_data:
doc_html += "<div class='col-md-12 col-sm-12'\
><div class='col-md-12 col-sm-12'>" \
+ section_html + html +"</div></div>"
if doc_html:
doc_html = "<div class='small'><div class='col-md-12 text-right'><a class='btn btn-default btn-xs' href='#Form/%s/%s'></a></div>" %(doctype, docname) + doc_html + "</div>"
return {'html': doc_html}

View File

@ -1,69 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe, math
from frappe import _
from frappe.utils import flt, rounded
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
from erpnext.hr.doctype.employee_loan.employee_loan import get_monthly_repayment_amount, check_repayment_method
class EmployeeLoanApplication(Document):
def validate(self):
check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
self.validate_loan_amount()
self.get_repayment_details()
def validate_loan_amount(self):
maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount')
if maximum_loan_limit and self.loan_amount > maximum_loan_limit:
frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit))
def get_repayment_details(self):
if self.repayment_method == "Repay Over Number of Periods":
self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
if self.repayment_method == "Repay Fixed Amount per Period":
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
if monthly_interest_rate:
monthly_interest_amount = self.loan_amount * monthly_interest_rate
if monthly_interest_amount >= self.repayment_amount:
frappe.throw(_("Repayment amount {} should be greater than monthly interest amount {}").
format(self.repayment_amount, monthly_interest_amount))
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
math.log(self.repayment_amount - (monthly_interest_amount))) /
(math.log(1 + monthly_interest_rate)))
else:
self.repayment_periods = self.loan_amount / self.repayment_amount
self.calculate_payable_amount()
def calculate_payable_amount(self):
balance_amount = self.loan_amount
self.total_payable_amount = 0
self.total_payable_interest = 0
while(balance_amount > 0):
interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100))
balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
self.total_payable_interest += interest_amount
self.total_payable_amount = self.loan_amount + self.total_payable_interest
@frappe.whitelist()
def make_employee_loan(source_name, target_doc = None):
doclist = get_mapped_doc("Employee Loan Application", source_name, {
"Employee Loan Application": {
"doctype": "Employee Loan",
"validation": {
"docstatus": ["=", 1]
}
}
}, target_doc)
return doclist

View File

@ -12,7 +12,7 @@ from erpnext.hr.doctype.employee_onboarding.employee_onboarding import Incomplet
class TestEmployeeOnboarding(unittest.TestCase):
def test_employee_onboarding_incomplete_task(self):
if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}):
return frappe.get_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'})
_set_up()
applicant = get_job_applicant()
onboarding = frappe.new_doc('Employee Onboarding')
@ -39,9 +39,10 @@ class TestEmployeeOnboarding(unittest.TestCase):
# complete the task
project = frappe.get_doc('Project', onboarding.project)
project.load_tasks()
project.tasks[0].status = 'Completed'
project.save()
for task in frappe.get_all('Task', dict(project=project.name)):
task = frappe.get_doc('Task', task.name)
task.status = 'Completed'
task.save()
# make employee
onboarding.reload()
@ -71,4 +72,3 @@ def _set_up():
project = "Employee Onboarding : Test Researcher - test@researcher.com"
frappe.db.sql("delete from tabProject where name=%s", project)
frappe.db.sql("delete from tabTask where project=%s", project)
frappe.db.sql("delete from `tabProject Task` where parent=%s", project)

View File

@ -10,33 +10,36 @@ from erpnext.accounts.doctype.account.test_account import create_account
test_records = frappe.get_test_records('Expense Claim')
test_dependencies = ['Employee']
company_name = '_Test Company 4'
class TestExpenseClaim(unittest.TestCase):
def test_total_expense_claim_for_project(self):
frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """)
frappe.db.sql("""delete from `tabProject Task` where parent = "_Test Project 1" """)
frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """)
frappe.db.sql("delete from `tabExpense Claim` where project='_Test Project 1'")
frappe.db.sql("update `tabExpense Claim` set project = '', task = ''")
frappe.get_doc({
"project_name": "_Test Project 1",
"doctype": "Project",
"doctype": "Project"
}).save()
task = frappe.get_doc({
"doctype": "Task",
"subject": "_Test Project Task 1",
"project": "_Test Project 1"
}).save()
task = frappe.get_doc(dict(
doctype = 'Task',
subject = '_Test Project Task 1',
status = 'Open',
project = '_Test Project 1'
)).insert()
task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"})
payable_account = get_payable_account("Wind Power LLC")
make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name)
task_name = task.name
payable_account = get_payable_account(company_name)
make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200)
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
expense_claim2 = make_expense_claim(payable_account, 600, 500, "Wind Power LLC", "Travel Expenses - WP","_Test Project 1", task_name)
expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name)
self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700)
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700)
@ -48,8 +51,8 @@ class TestExpenseClaim(unittest.TestCase):
self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200)
def test_expense_claim_status(self):
payable_account = get_payable_account("Wind Power LLC")
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP")
payable_account = get_payable_account(company_name)
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4")
je_dict = make_bank_entry("Expense Claim", expense_claim.name)
je = frappe.get_doc(je_dict)
@ -66,9 +69,9 @@ class TestExpenseClaim(unittest.TestCase):
self.assertEqual(expense_claim.status, "Unpaid")
def test_expense_claim_gl_entry(self):
payable_account = get_payable_account("Wind Power LLC")
payable_account = get_payable_account(company_name)
taxes = generate_taxes()
expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", do_not_submit=True, taxes=taxes)
expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes)
expense_claim.submit()
gl_entries = frappe.db.sql("""select account, debit, credit
@ -78,9 +81,9 @@ class TestExpenseClaim(unittest.TestCase):
self.assertTrue(gl_entries)
expected_values = dict((d[0], d) for d in [
['CGST - WP',10.0, 0.0],
[payable_account, 0.0, 210.0],
["Travel Expenses - WP", 200.0, 0.0]
['CGST - _TC4',18.0, 0.0],
[payable_account, 0.0, 218.0],
["Travel Expenses - _TC4", 200.0, 0.0]
])
for gle in gl_entries:
@ -89,14 +92,14 @@ class TestExpenseClaim(unittest.TestCase):
self.assertEquals(expected_values[gle.account][2], gle.credit)
def test_rejected_expense_claim(self):
payable_account = get_payable_account("Wind Power LLC")
payable_account = get_payable_account(company_name)
expense_claim = frappe.get_doc({
"doctype": "Expense Claim",
"employee": "_T-Employee-00001",
"payable_account": payable_account,
"approval_status": "Rejected",
"expenses":
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "amount": 300, "sanctioned_amount": 200 }]
[{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }]
})
expense_claim.submit()
@ -111,9 +114,9 @@ def get_payable_account(company):
def generate_taxes():
parent_account = frappe.db.get_value('Account',
{'company': "Wind Power LLC", 'is_group':1, 'account_type': 'Tax'},
{'company': company_name, 'is_group':1, 'account_type': 'Tax'},
'name')
account = create_account(company="Wind Power LLC", account_name="CGST", account_type="Tax", parent_account=parent_account)
account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account)
return {'taxes':[{
"account_head": account,
"rate": 0,
@ -124,15 +127,18 @@ def generate_taxes():
def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None):
employee = frappe.db.get_value("Employee", {"status": "Active"})
currency = frappe.db.get_value('Company', company, 'default_currency')
expense_claim = {
"doctype": "Expense Claim",
"employee": employee,
"payable_account": payable_account,
"approval_status": "Approved",
"company": company,
'currency': currency,
"expenses":
[{"expense_type": "Travel",
"default_account": account,
'currency': currency,
"amount": amount,
"sanctioned_amount": sanctioned_amount}]}
if taxes:

View File

@ -15,7 +15,7 @@ frappe.ui.form.on('HR Settings', {
let policy = frm.doc.password_policy;
if (policy) {
if (policy.includes(' ') || policy.includes('--')) {
frappe.msgprint(_("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically"));
frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically"));
}
frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-'));
}

View File

@ -4,6 +4,12 @@
frappe.provide("erpnext.job_offer");
frappe.ui.form.on("Job Offer", {
onload: function (frm) {
frm.set_query("select_terms", function() {
return { filters: { hr: 1 } };
});
},
select_terms: function (frm) {
erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) {
if (!r.exc) {

View File

@ -39,31 +39,19 @@ frappe.ui.form.on('Loan', {
},
refresh: function (frm) {
if (frm.doc.docstatus == 1 && frm.doc.status == "Sanctioned") {
frm.add_custom_button(__('Create Disbursement Entry'), function() {
frm.trigger("make_jv");
})
}
if (frm.doc.repayment_schedule) {
let total_amount_paid = 0;
$.each(frm.doc.repayment_schedule || [], function(i, row) {
if (row.paid) {
total_amount_paid += row.total_payment;
}
});
frm.set_value("total_amount_paid", total_amount_paid);
; }
if (frm.doc.docstatus == 1 && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) {
frm.add_custom_button(__('Create Repayment Entry'), function() {
frm.trigger("make_repayment_entry");
})
if (frm.doc.docstatus == 1) {
if (frm.doc.status == "Sanctioned") {
frm.add_custom_button(__('Create Disbursement Entry'), function() {
frm.trigger("make_jv");
}).addClass("btn-primary");
} else if (frm.doc.status == "Disbursed" && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) {
frm.add_custom_button(__('Create Repayment Entry'), function() {
frm.trigger("make_repayment_entry");
}).addClass("btn-primary");
}
}
frm.trigger("toggle_fields");
},
status: function (frm) {
frm.toggle_reqd("disbursement_date", frm.doc.status == 'Disbursed')
frm.toggle_reqd("repayment_start_date", frm.doc.status == 'Disbursed')
},
make_jv: function (frm) {
frappe.call({

View File

@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@ -20,6 +21,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "applicant_type",
"fieldtype": "Select",
"hidden": 0,
@ -53,6 +55,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "applicant",
"fieldtype": "Dynamic Link",
"hidden": 0,
@ -86,6 +89,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "applicant_name",
"fieldtype": "Data",
"hidden": 0,
@ -118,6 +122,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "loan_application",
"fieldtype": "Link",
"hidden": 0,
@ -151,6 +156,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "loan_type",
"fieldtype": "Link",
"hidden": 0,
@ -184,6 +190,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@ -215,7 +222,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"default": "Today",
"fetch_if_empty": 0,
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
@ -248,6 +256,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@ -282,6 +291,7 @@
"collapsible": 0,
"columns": 0,
"default": "Sanctioned",
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
@ -299,7 +309,7 @@
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@ -316,6 +326,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.applicant_type==\"Employee\"",
"fetch_if_empty": 0,
"fieldname": "repay_from_salary",
"fieldtype": "Check",
"hidden": 0,
@ -348,6 +359,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
@ -380,6 +392,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "loan_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -415,6 +428,7 @@
"columns": 0,
"default": "",
"fetch_from": "loan_type.rate_of_interest",
"fetch_if_empty": 0,
"fieldname": "rate_of_interest",
"fieldtype": "Percent",
"hidden": 0,
@ -448,6 +462,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.status==\"Disbursed\"",
"fetch_if_empty": 0,
"fieldname": "disbursement_date",
"fieldtype": "Date",
"hidden": 0,
@ -480,6 +496,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "repayment_start_date",
"fieldtype": "Date",
"hidden": 0,
@ -499,7 +516,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
@ -512,6 +529,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
@ -544,6 +562,7 @@
"collapsible": 0,
"columns": 0,
"default": "Repay Over Number of Periods",
"fetch_if_empty": 0,
"fieldname": "repayment_method",
"fieldtype": "Select",
"hidden": 0,
@ -579,6 +598,7 @@
"columns": 0,
"default": "",
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "repayment_periods",
"fieldtype": "Int",
"hidden": 0,
@ -613,6 +633,7 @@
"columns": 0,
"default": "",
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "monthly_repayment_amount",
"fieldtype": "Currency",
"hidden": 0,
@ -646,6 +667,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "account_info",
"fieldtype": "Section Break",
"hidden": 0,
@ -678,6 +700,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "mode_of_payment",
"fieldtype": "Link",
"hidden": 0,
@ -711,6 +734,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "payment_account",
"fieldtype": "Link",
"hidden": 0,
@ -744,6 +768,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_9",
"fieldtype": "Column Break",
"hidden": 0,
@ -775,6 +800,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "loan_account",
"fieldtype": "Link",
"hidden": 0,
@ -808,6 +834,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "interest_income_account",
"fieldtype": "Link",
"hidden": 0,
@ -841,6 +868,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_15",
"fieldtype": "Section Break",
"hidden": 0,
@ -873,6 +901,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "repayment_schedule",
"fieldtype": "Table",
"hidden": 0,
@ -906,6 +935,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_17",
"fieldtype": "Section Break",
"hidden": 0,
@ -939,6 +969,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
"fetch_if_empty": 0,
"fieldname": "total_payment",
"fieldtype": "Currency",
"hidden": 0,
@ -972,6 +1003,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_19",
"fieldtype": "Column Break",
"hidden": 0,
@ -1004,6 +1036,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
"fetch_if_empty": 0,
"fieldname": "total_interest_payable",
"fieldtype": "Currency",
"hidden": 0,
@ -1037,6 +1070,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "total_amount_paid",
"fieldtype": "Currency",
"hidden": 0,
@ -1070,6 +1104,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
@ -1106,7 +1141,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 16:15:53.267145",
"modified": "2019-07-10 13:04:20.953694",
"modified_by": "Administrator",
"module": "HR",
"name": "Loan",
@ -1149,7 +1184,6 @@
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"user_permission_doctypes": "[\"Employee\"]",
"write": 0
}
],

View File

@ -6,29 +6,33 @@ from __future__ import unicode_literals
import frappe, math, json
import erpnext
from frappe import _
from frappe.utils import flt, rounded, add_months, nowdate
from frappe.utils import flt, rounded, add_months, nowdate, getdate
from erpnext.controllers.accounts_controller import AccountsController
class Loan(AccountsController):
def validate(self):
check_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
self.set_missing_fields()
self.make_repayment_schedule()
self.set_repayment_period()
self.calculate_totals()
def set_missing_fields(self):
if not self.company:
self.company = erpnext.get_default_company()
if not self.posting_date:
self.posting_date = nowdate()
if self.loan_type and not self.rate_of_interest:
self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest")
if self.repayment_method == "Repay Over Number of Periods":
self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods)
if self.status == "Repaid/Closed":
self.total_amount_paid = self.total_payment
if self.status == 'Disbursed' and self.repayment_start_date < self.disbursement_date:
frappe.throw(_("Repayment Start Date cannot be before Disbursement Date."))
if self.status == "Disbursed":
self.make_repayment_schedule()
self.set_repayment_period()
self.calculate_totals()
def make_jv_entry(self):
self.check_permission('write')
@ -105,20 +109,31 @@ def update_total_amount_paid(doc):
frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid)
def update_disbursement_status(doc):
disbursement = frappe.db.sql("""select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
from `tabGL Entry` where account = %s and against_voucher_type = 'Loan' and against_voucher = %s""",
(doc.payment_account, doc.name), as_dict=1)[0]
if disbursement.disbursed_amount == doc.loan_amount:
frappe.db.set_value("Loan", doc.name , "status", "Disbursed")
if disbursement.disbursed_amount == 0:
frappe.db.set_value("Loan", doc.name , "status", "Sanctioned")
if disbursement.disbursed_amount > doc.loan_amount:
frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount))
if disbursement.disbursed_amount > 0:
frappe.db.set_value("Loan", doc.name , "disbursement_date", disbursement.posting_date)
frappe.db.set_value("Loan", doc.name , "repayment_start_date", disbursement.posting_date)
disbursement = frappe.db.sql("""
select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount
from `tabGL Entry`
where account = %s and against_voucher_type = 'Loan' and against_voucher = %s
""", (doc.payment_account, doc.name), as_dict=1)[0]
def check_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
disbursement_date = None
if not disbursement or disbursement.disbursed_amount == 0:
status = "Sanctioned"
elif disbursement.disbursed_amount == doc.loan_amount:
disbursement_date = disbursement.posting_date
status = "Disbursed"
elif disbursement.disbursed_amount > doc.loan_amount:
frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount))
if status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", doc.name, "repayment_start_date")):
frappe.throw(_("Disbursement Date cannot be after Loan Repayment Start Date"))
frappe.db.sql("""
update `tabLoan`
set status = %s, disbursement_date = %s
where name = %s
""", (status, disbursement_date, doc.name))
def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods):
if repayment_method == "Repay Over Number of Periods" and not repayment_periods:
frappe.throw(_("Please enter Repayment Periods"))
@ -222,4 +237,4 @@ def make_jv_entry(loan, company, loan_account, applicant_type, applicant, loan_a
"reference_name": loan,
})
journal_entry.set("accounts", account_amt_list)
return journal_entry.as_dict()
return journal_entry.as_dict()

View File

@ -23,9 +23,8 @@ frappe.ui.form.on('Loan Application', {
},
add_toolbar_buttons: function(frm) {
if (frm.doc.status == "Approved") {
frm.add_custom_button(__('Loan'), function() {
frm.add_custom_button(__('Create Loan'), function() {
frappe.call({
type: "GET",
method: "erpnext.hr.doctype.loan_application.loan_application.make_loan",
args: {
"source_name": frm.doc.name
@ -37,7 +36,7 @@ frappe.ui.form.on('Loan Application', {
}
}
});
})
}).addClass("btn-primary");
}
}
});

View File

@ -9,11 +9,11 @@ from frappe.utils import flt, rounded
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, check_repayment_method
from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, validate_repayment_method
class LoanApplication(Document):
def validate(self):
check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods)
self.validate_loan_amount()
self.get_repayment_details()
@ -29,14 +29,17 @@ class LoanApplication(Document):
if self.repayment_method == "Repay Fixed Amount per Period":
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
if monthly_interest_rate:
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
math.log(self.repayment_amount - (self.loan_amount*monthly_interest_rate))) /
(math.log(1 + monthly_interest_rate)))
min_repayment_amount = self.loan_amount*monthly_interest_rate
if self.repayment_amount - min_repayment_amount < 0:
frappe.throw(_("Repayment Amount must be greater than " \
+ str(flt(min_repayment_amount, 2))))
self.repayment_periods = math.ceil(math.log(self.repayment_amount) -
math.log(self.repayment_amount - min_repayment_amount) /(math.log(1 + monthly_interest_rate)))
else:
self.repayment_periods = self.loan_amount / self.repayment_amount
self.calculate_payable_amount()
def calculate_payable_amount(self):
balance_amount = self.loan_amount
self.total_payable_amount = 0
@ -47,9 +50,9 @@ class LoanApplication(Document):
balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
self.total_payable_interest += interest_amount
self.total_payable_amount = self.loan_amount + self.total_payable_interest
@frappe.whitelist()
def make_loan(source_name, target_doc = None):
doclist = get_mapped_doc("Loan Application", source_name, {

View File

@ -31,21 +31,22 @@ class TestLoanApplication(unittest.TestCase):
"rate_of_interest": 9.2,
"loan_amount": 250000,
"repayment_method": "Repay Over Number of Periods",
"repayment_periods": 24
"repayment_periods": 18
})
loan_application.insert()
def test_loan_totals(self):
loan_application = frappe.get_doc("Loan Application", {"applicant":self.applicant})
self.assertEquals(loan_application.repayment_amount, 11445)
self.assertEquals(loan_application.total_payable_interest, 24657)
self.assertEquals(loan_application.total_payable_amount, 274657)
loan_application.repayment_method = "Repay Fixed Amount per Period"
loan_application.repayment_amount = 15000
self.assertEqual(loan_application.total_payable_interest, 18599)
self.assertEqual(loan_application.total_payable_amount, 268599)
self.assertEqual(loan_application.repayment_amount, 14923)
loan_application.repayment_periods = 24
loan_application.save()
loan_application.reload()
self.assertEqual(loan_application.repayment_periods, 18)
self.assertEqual(loan_application.total_payable_interest, 18506)
self.assertEqual(loan_application.total_payable_amount, 268506)
self.assertEqual(loan_application.total_payable_interest, 24657)
self.assertEqual(loan_application.total_payable_amount, 274657)
self.assertEqual(loan_application.repayment_amount, 11445)

View File

@ -22,7 +22,7 @@ class ShiftType(Document):
'skip_auto_attendance':'0',
'attendance':('is', 'not set'),
'time':('>=', self.process_attendance_after),
'shift_actual_start': ('<', self.last_sync_of_checkin),
'shift_actual_end': ('<', self.last_sync_of_checkin),
'shift': self.name
}
logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time")

View File

@ -2,6 +2,10 @@
// For license information, please see license.txt
frappe.ui.form.on('Blanket Order', {
onload: function(frm) {
frm.trigger('set_tc_name_filter');
},
setup: function(frm) {
frm.add_fetch("customer", "customer_name", "customer_name");
frm.add_fetch("supplier", "supplier_name", "supplier_name");
@ -44,4 +48,23 @@ frappe.ui.form.on('Blanket Order', {
}
});
},
set_tc_name_filter: function(frm) {
if (frm.doc.blanket_order_type === 'Selling') {
frm.set_query("tc_name", function() {
return { filters: { selling: 1 } };
});
}
if (frm.doc.blanket_order_type === 'Purchasing') {
frm.set_query("tc_name", function() {
return { filters: { buying: 1 } };
});
}
},
blanket_order_type: function (frm) {
frm.trigger('set_tc_name_filter');
}
});

View File

@ -0,0 +1,12 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'blanket_order',
'transactions': [
{
'items': ['Purchase Order', 'Sales Order']
}
]
}

View File

@ -596,6 +596,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite
sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * %(qty)s as qty,
item.description,
item.image,
bom.project,
item.stock_uom,
item.allow_alternative_item,
item_default.default_warehouse,

View File

@ -0,0 +1,27 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'bom_no',
'non_standard_fieldnames': {
'Item': 'default_bom',
'Purchase Order': 'bom',
'Purchase Receipt': 'bom',
'Purchase Invoice': 'bom'
},
'transactions': [
{
'label': _('Stock'),
'items': ['Item', 'Stock Entry', 'Quality Inspection']
},
{
'label': _('Manufacture'),
'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan']
},
{
'label': _('Purchase'),
'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
}
]
}

View File

@ -10,4 +10,4 @@ def get_data():
'items': ['Material Request', 'Stock Entry']
}
]
}
}

View File

@ -0,0 +1,13 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'operation',
'transactions': [
{
'label': _('Manufacture'),
'items': ['BOM', 'Work Order', 'Job Card', 'Timesheet']
}
]
}

View File

@ -0,0 +1,12 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'routing',
'transactions': [
{
'items': ['BOM']
}
]
}

View File

@ -0,0 +1,13 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'workstation',
'transactions': [
{
'label': _('Manufacture'),
'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet']
}
]
}

View File

@ -505,6 +505,7 @@ erpnext.patches.v10_0.update_hub_connector_domain
erpnext.patches.v10_0.set_student_party_type
erpnext.patches.v10_0.update_project_in_sle
erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract
erpnext.patches.v10_0.repost_requested_qty_for_non_stock_uom_items
erpnext.patches.v11_0.merge_land_unit_with_location
erpnext.patches.v11_0.add_index_on_nestedset_doctypes
erpnext.patches.v11_0.remove_modules_setup_page
@ -602,9 +603,11 @@ erpnext.patches.v11_1.set_salary_details_submittable
erpnext.patches.v11_1.rename_depends_on_lwp
execute:frappe.delete_doc("Report", "Inactive Items")
erpnext.patches.v11_1.delete_scheduling_tool
erpnext.patches.v12_0.rename_tolerance_fields
erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019
execute:frappe.delete_doc_if_exists("Page", "support-analytics")
erpnext.patches.v12_0.make_item_manufacturer
erpnext.patches.v12_0.remove_patient_medical_record_page
erpnext.patches.v11_1.move_customer_lead_to_dynamic_column
erpnext.patches.v11_1.set_default_action_for_quality_inspection
erpnext.patches.v11_1.delete_bom_browser
@ -616,3 +619,7 @@ erpnext.patches.v12_0.set_quotation_status
erpnext.patches.v12_0.set_priority_for_support
erpnext.patches.v12_0.delete_priority_property_setter
erpnext.patches.v12_0.set_default_batch_size
execute:frappe.delete_doc("DocType", "Project Task")
erpnext.patches.v11_1.update_default_supplier_in_item_defaults
erpnext.patches.v12_0.update_due_date_in_gle
erpnext.patches.v12_0.add_default_buying_selling_terms_in_company

View File

@ -0,0 +1,21 @@
# Copyright (c) 2019, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty
count=0
for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse
from `tabMaterial Request Item` where docstatus = 1 and stock_uom<>uom"""):
try:
count += 1
update_bin_qty(item_code, warehouse, {
"indented_qty": get_indented_qty(item_code, warehouse),
})
if count % 200 == 0:
frappe.db.commit()
except:
frappe.db.rollback()

View File

@ -0,0 +1,25 @@
# Copyright (c) 2018, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
'''
default supplier was not set in the item defaults for multi company instance,
this patch will set the default supplier
'''
if not frappe.db.has_column('Item', 'default_supplier'):
return
frappe.reload_doc('stock', 'doctype', 'item_default')
frappe.reload_doc('stock', 'doctype', 'item')
companies = frappe.get_all("Company")
if len(companies) > 1:
frappe.db.sql(""" UPDATE `tabItem Default`, `tabItem`
SET `tabItem Default`.default_supplier = `tabItem`.default_supplier
WHERE
`tabItem Default`.parent = `tabItem`.name and `tabItem Default`.default_supplier is null
and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """)

View File

@ -0,0 +1,19 @@
# Copyright (c) 2019, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
frappe.reload_doc("setup", "doctype", "company")
if frappe.db.has_column('Company', 'default_terms'):
rename_field('Company', "default_terms", "default_selling_terms")
for company in frappe.get_all("Company", ["name", "default_selling_terms", "default_buying_terms"]):
if company.default_selling_terms and not company.default_buying_terms:
frappe.db.set_value("Company", company.name, "default_buying_terms", company.default_selling_terms)
frappe.reload_doc("setup", "doctype", "terms_and_conditions")
frappe.db.sql("update `tabTerms and Conditions` set selling=1, buying=1, hr=1")

View File

@ -0,0 +1,7 @@
# Copyright (c) 2019
from __future__ import unicode_literals
import frappe
def execute():
frappe.delete_doc("Page", "medical_record")

View File

@ -0,0 +1,15 @@
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
frappe.reload_doc("stock", "doctype", "item")
frappe.reload_doc("stock", "doctype", "stock_settings")
frappe.reload_doc("accounts", "doctype", "accounts_settings")
rename_field('Stock Settings', "tolerance", "over_delivery_receipt_allowance")
rename_field('Item', "tolerance", "over_delivery_receipt_allowance")
qty_allowance = frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance")
frappe.db.set_value("Accounts Settings", None, "over_delivery_receipt_allowance", qty_allowance)
frappe.db.sql("update tabItem set over_billing_allowance=over_delivery_receipt_allowance")

View File

@ -33,19 +33,23 @@ def set_priorities_service_level():
service_level_priorities = frappe.get_list("Service Level", fields=["name", "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period"])
frappe.reload_doc("support", "doctype", "service_level")
frappe.reload_doc("support", "doctype", "support_settings")
frappe.db.set_value('Support Settings', None, 'track_service_level_agreement', 1)
for service_level in service_level_priorities:
if service_level:
doc = frappe.get_doc("Service Level", service_level.name)
doc.append("priorities", {
"priority": service_level.priority,
"default_priority": 1,
"response_time": service_level.response_time,
"response_time_period": service_level.response_time_period,
"resolution_time": service_level.resolution_time,
"resolution_time_period": service_level.resolution_time_period
})
doc.save(ignore_permissions=True)
if not doc.priorities:
doc.append("priorities", {
"priority": service_level.priority,
"default_priority": 1,
"response_time": service_level.response_time,
"response_time_period": service_level.response_time_period,
"resolution_time": service_level.resolution_time,
"resolution_time_period": service_level.resolution_time_period
})
doc.flags.ignore_validate = True
doc.save(ignore_permissions=True)
except frappe.db.TableMissingError:
frappe.reload_doc("support", "doctype", "service_level")
@ -73,6 +77,7 @@ def set_priorities_service_level_agreement():
"resolution_time": service_level_agreement.resolution_time,
"resolution_time_period": service_level_agreement.resolution_time_period
})
doc.flags.ignore_validate = True
doc.save(ignore_permissions=True)
except frappe.db.TableMissingError:
frappe.reload_doc("support", "doctype", "service_level_agreement")

View File

@ -2,10 +2,9 @@ import frappe
def execute():
frappe.reload_doctype('Task')
frappe.reload_doctype('Project Task')
# add "Completed" if customized
for doctype in ('Task', 'Project Task'):
for doctype in ('Task'):
property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options'))
if property_setter_name:
property_setter = frappe.get_doc('Property Setter', property_setter_name)

View File

@ -0,0 +1,17 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doc("accounts", "doctype", "gl_entry")
for doctype in ["Sales Invoice", "Purchase Invoice", "Journal Entry"]:
frappe.reload_doc("accounts", "doctype", frappe.scrub(doctype))
frappe.db.sql(""" UPDATE `tabGL Entry`, `tab{doctype}`
SET
`tabGL Entry`.due_date = `tab{doctype}`.due_date
WHERE
`tabGL Entry`.voucher_no = `tab{doctype}`.name and `tabGL Entry`.party is not null
and `tabGL Entry`.voucher_type in ('Sales Invoice', 'Purchase Invoice', 'Journal Entry')
and `tabGL Entry`.account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable'))""" #nosec
.format(doctype=doctype))

Some files were not shown because too many files have changed in this diff Show More