diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index 9172792411..85c9209205 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -23,7 +23,7 @@
{
"hidden": 0,
"label": "Reports",
- "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n }\n]"
+ "links": "[\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Trial Balance for Party\",\n \"name\": \"Trial Balance for Party\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Journal Entry\"\n ],\n \"doctype\": \"Journal Entry\",\n \"is_query_report\": true,\n \"label\": \"Payment Period Based On Invoice Date\",\n \"name\": \"Payment Period Based On Invoice Date\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Partners Commission\",\n \"name\": \"Sales Partners Commission\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Customer\"\n ],\n \"doctype\": \"Customer\",\n \"is_query_report\": true,\n \"label\": \"Customer Credit Balance\",\n \"name\": \"Customer Credit Balance\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Sales Invoice\"\n ],\n \"doctype\": \"Sales Invoice\",\n \"is_query_report\": true,\n \"label\": \"Sales Payment Summary\",\n \"name\": \"Sales Payment Summary\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"Address\"\n ],\n \"doctype\": \"Address\",\n \"is_query_report\": true,\n \"label\": \"Address And Contacts\",\n \"name\": \"Address And Contacts\",\n \"type\": \"report\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"DATEV Export\",\n \"name\": \"DATEV\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
@@ -98,7 +98,7 @@
"idx": 0,
"is_standard": 1,
"label": "Accounting",
- "modified": "2020-11-06 13:05:58.650150",
+ "modified": "2020-11-11 18:35:11.542909",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
index 152e17dbc8..bc77dac1cd 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js
@@ -9,11 +9,7 @@ frappe.ui.form.on('Fiscal Year', {
}
},
refresh: function (frm) {
- let doc = frm.doc;
- frm.toggle_enable('year_start_date', doc.__islocal);
- frm.toggle_enable('year_end_date', doc.__islocal);
-
- if (!doc.__islocal && (doc.name != frappe.sys_defaults.fiscal_year)) {
+ if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) {
frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm));
frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'"));
} else {
@@ -24,8 +20,10 @@ frappe.ui.form.on('Fiscal Year', {
return frm.call('set_as_default');
},
year_start_date: function(frm) {
- let year_end_date =
- frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1);
- frm.set_value("year_end_date", year_end_date);
+ if (!frm.doc.is_short_year) {
+ let year_end_date =
+ frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1);
+ frm.set_value("year_end_date", year_end_date);
+ }
},
});
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
index 4ca9f6b96f..5ab91f2506 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
@@ -1,347 +1,126 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 0,
- "autoname": "field:year",
- "beta": 0,
- "creation": "2013-01-22 16:50:25",
- "custom": 0,
- "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 0,
+ "actions": [],
+ "allow_import": 1,
+ "autoname": "field:year",
+ "creation": "2013-01-22 16:50:25",
+ "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "engine": "InnoDB",
+ "field_order": [
+ "year",
+ "disabled",
+ "is_short_year",
+ "year_start_date",
+ "year_end_date",
+ "companies",
+ "auto_created"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "For e.g. 2012, 2012-13",
- "fieldname": "year",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Year Name",
- "length": 0,
- "no_copy": 0,
- "oldfieldname": "year",
- "oldfieldtype": "Data",
- "permlevel": 0,
- "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
- },
+ "description": "For e.g. 2012, 2012-13",
+ "fieldname": "year",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Year Name",
+ "oldfieldname": "year",
+ "oldfieldtype": "Data",
+ "reqd": 1,
+ "unique": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "disabled",
- "fieldtype": "Check",
- "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": "Disabled",
- "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
- },
+ "default": "0",
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "label": "Disabled"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "year_start_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": "Year Start Date",
- "length": 0,
- "no_copy": 1,
- "oldfieldname": "year_start_date",
- "oldfieldtype": "Date",
- "permlevel": 0,
- "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
- },
+ "fieldname": "year_start_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Year Start Date",
+ "no_copy": 1,
+ "oldfieldname": "year_start_date",
+ "oldfieldtype": "Date",
+ "reqd": 1,
+ "set_only_once": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "year_end_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": "Year End Date",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "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
- },
+ "fieldname": "year_end_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Year End Date",
+ "no_copy": 1,
+ "reqd": 1,
+ "set_only_once": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "companies",
- "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": "Companies",
- "length": 0,
- "no_copy": 0,
- "options": "Fiscal Year 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": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "companies",
+ "fieldtype": "Table",
+ "label": "Companies",
+ "options": "Fiscal Year Company"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "fieldname": "auto_created",
- "fieldtype": "Check",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Auto Created",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "default": "0",
+ "fieldname": "auto_created",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Auto Created",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "description": "Less than 12 months.",
+ "fieldname": "is_short_year",
+ "fieldtype": "Check",
+ "label": "Is Short Year",
+ "set_only_once": 1
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "fa fa-calendar",
- "idx": 1,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-04-25 14:21:41.273354",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Fiscal Year",
- "owner": "Administrator",
+ ],
+ "icon": "fa fa-calendar",
+ "idx": 1,
+ "links": [],
+ "modified": "2020-11-05 12:16:53.081573",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Fiscal Year",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Sales User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Sales User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Purchase User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Purchase User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Accounts User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Accounts User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Stock User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Stock User"
+ },
{
- "amend": 0,
- "cancel": 0,
- "create": 0,
- "delete": 0,
- "email": 0,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 0,
- "role": "Employee",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "read": 1,
+ "role": "Employee"
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 1,
- "sort_field": "name",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0
+ ],
+ "show_name_in_global_search": 1,
+ "sort_field": "name",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
index d80bc7fad1..da6a3fd2ef 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py
@@ -36,6 +36,11 @@ class FiscalYear(Document):
frappe.throw(_("Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."))
def validate_dates(self):
+ if self.is_short_year:
+ # Fiscal Year can be shorter than one year, in some jurisdictions
+ # under certain circumstances. For example, in the USA and Germany.
+ return
+
if getdate(self.year_start_date) > getdate(self.year_end_date):
frappe.throw(_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
FiscalYearIncorrectDate)
@@ -116,12 +121,8 @@ def auto_create_fiscal_year():
pass
def get_from_and_to_date(fiscal_year):
- from_and_to_date_tuple = frappe.db.sql("""select year_start_date, year_end_date
- from `tabFiscal Year` where name=%s""", (fiscal_year))[0]
-
- from_and_to_date = {
- "from_date": from_and_to_date_tuple[0],
- "to_date": from_and_to_date_tuple[1]
- }
-
- return from_and_to_date
+ fields = [
+ "year_start_date as from_date",
+ "year_end_date as to_date"
+ ]
+ return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1)
diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
index f7b7782766..cec4f4492d 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
@@ -11,6 +11,7 @@ test_records = frappe.get_test_records('Fiscal Year')
test_ignore = ["Company"]
class TestFiscalYear(unittest.TestCase):
+
def test_extra_year(self):
if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"):
frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000")
diff --git a/erpnext/accounts/doctype/fiscal_year/test_records.json b/erpnext/accounts/doctype/fiscal_year/test_records.json
index d5723ca62b..44052535cb 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_records.json
+++ b/erpnext/accounts/doctype/fiscal_year/test_records.json
@@ -1,4 +1,11 @@
[
+ {
+ "doctype": "Fiscal Year",
+ "year": "_Test Short Fiscal Year 2011",
+ "is_short_year": 1,
+ "year_end_date": "2011-04-01",
+ "year_start_date": "2011-12-31"
+ },
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2012",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 149c47673c..55a5b0e513 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -352,8 +352,14 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
pricing_rule_rate = 0.0
if pricing_rule.currency == args.currency:
pricing_rule_rate = pricing_rule.rate
+
+ if pricing_rule_rate:
+ # Override already set price list rate (from item price)
+ # if pricing_rule_rate > 0
+ item_details.update({
+ "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
+ })
item_details.update({
- "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1),
"discount_percentage": 0.0
})
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 22a031c162..ec0a485bfc 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -484,6 +484,43 @@ class TestPricingRule(unittest.TestCase):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
+ def test_item_price_with_pricing_rule(self):
+ item = make_item("Water Flask")
+ make_item_price("Water Flask", "_Test Price List", 100)
+
+ pricing_rule_record = {
+ "doctype": "Pricing Rule",
+ "title": "_Test Water Flask Rule",
+ "apply_on": "Item Code",
+ "items": [{
+ "item_code": "Water Flask",
+ }],
+ "selling": 1,
+ "currency": "INR",
+ "rate_or_discount": "Rate",
+ "rate": 0,
+ "margin_type": "Percentage",
+ "margin_rate_or_amount": 2,
+ "company": "_Test Company"
+ }
+ rule = frappe.get_doc(pricing_rule_record)
+ rule.insert()
+
+ si = create_sales_invoice(do_not_save=True, item_code="Water Flask")
+ si.selling_price_list = "_Test Price List"
+ si.save()
+
+ # If rate in Rule is 0, give preference to Item Price if it exists
+ self.assertEqual(si.items[0].price_list_rate, 100)
+ self.assertEqual(si.items[0].margin_rate_or_amount, 2)
+ self.assertEqual(si.items[0].rate_with_margin, 102)
+ self.assertEqual(si.items[0].rate, 102)
+
+ si.delete()
+ rule.delete()
+ frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
+ item.delete()
+
def make_pricing_rule(**args):
args = frappe._dict(args)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 2f52a9e035..47483c9d1c 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -90,6 +90,11 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
this.frm.set_df_property("drop_ship", "hidden", !is_drop_ship);
if(doc.docstatus == 1) {
+ this.frm.fields_dict.items_section.wrapper.addClass("hide-border");
+ if(!this.frm.doc.set_warehouse) {
+ this.frm.fields_dict.items_section.wrapper.removeClass("hide-border");
+ }
+
if(!in_list(["Closed", "Delivered"], doc.status)) {
if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) {
this.frm.add_custom_button(__('Update Items'), () => {
@@ -126,16 +131,25 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
if(doc.status != "Closed") {
if (doc.status != "On Hold") {
if(flt(doc.per_received) < 100 && allow_receipt) {
- cur_frm.add_custom_button(__('Receipt'), this.make_purchase_receipt, __('Create'));
+ cur_frm.add_custom_button(__('Purchase Receipt'), this.make_purchase_receipt, __('Create'));
if(doc.is_subcontracted==="Yes" && me.has_unsupplied_items()) {
cur_frm.add_custom_button(__('Material to Supplier'),
function() { me.make_stock_entry(); }, __("Transfer"));
}
}
if(flt(doc.per_billed) < 100)
- cur_frm.add_custom_button(__('Invoice'),
+ cur_frm.add_custom_button(__('Purchase Invoice'),
this.make_purchase_invoice, __('Create'));
+ if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
+ cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create'));
+ }
+
+ if(flt(doc.per_billed)==0) {
+ this.frm.add_custom_button(__('Payment Request'),
+ function() { me.make_payment_request() }, __('Create'));
+ }
+
if(!doc.auto_repeat) {
cur_frm.add_custom_button(__('Subscription'), function() {
erpnext.utils.make_subscription(doc.doctype, doc.name)
@@ -156,13 +170,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
});
}
}
- if(flt(doc.per_billed)==0) {
- this.frm.add_custom_button(__('Payment Request'),
- function() { me.make_payment_request() }, __('Create'));
- }
- if(flt(doc.per_billed)==0 && doc.status != "Delivered") {
- cur_frm.add_custom_button(__('Payment'), cur_frm.cscript.make_payment_entry, __('Create'));
- }
+
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
}
} else if(doc.docstatus===0) {
@@ -358,12 +366,16 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
source_doctype: "Material Request",
target: me.frm,
- setters: {},
+ setters: {
+ schedule_date: undefined,
+ status: undefined
+ },
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
+ company: me.frm.doc.company
}
})
}, __("Get Items From"));
@@ -375,16 +387,17 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
source_doctype: "Supplier Quotation",
target: me.frm,
setters: {
- supplier: me.frm.doc.supplier
+ supplier: me.frm.doc.supplier,
+ valid_till: undefined
},
get_query_filters: {
docstatus: 1,
- status: ["!=", "Stopped"],
+ status: ["not in", ["Stopped", "Expired"]],
}
})
}, __("Get Items From"));
- this.frm.add_custom_button(__('Update rate as per last purchase'),
+ this.frm.add_custom_button(__('Update Rate as per Last Purchase'),
function() {
frappe.call({
"method": "get_last_purchase_rate",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 2747c7c54d..4b865a98e9 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -30,8 +30,8 @@
"customer_contact_email",
"section_addresses",
"supplier_address",
- "contact_person",
"address_display",
+ "contact_person",
"contact_display",
"contact_mobile",
"contact_email",
@@ -49,12 +49,14 @@
"plc_conversion_rate",
"ignore_pricing_rule",
"sec_warehouse",
- "set_warehouse",
- "col_break_warehouse",
"is_subcontracted",
+ "col_break_warehouse",
"supplier_warehouse",
- "items_section",
+ "before_items_section",
"scan_barcode",
+ "items_col_break",
+ "set_warehouse",
+ "items_section",
"items",
"sb_last_purchase",
"total_qty",
@@ -108,18 +110,13 @@
"payment_terms_template",
"payment_schedule",
"tracking_section",
- "per_billed",
+ "status",
"column_break_75",
+ "per_billed",
"per_received",
"terms_section_break",
"tc_name",
"terms",
- "more_info",
- "status",
- "ref_sq",
- "column_break_74",
- "party_account_currency",
- "inter_company_order_reference",
"column_break5",
"letter_head",
"select_print_heading",
@@ -131,7 +128,12 @@
"to_date",
"column_break_97",
"auto_repeat",
- "update_auto_repeat_reference"
+ "update_auto_repeat_reference",
+ "more_info",
+ "ref_sq",
+ "column_break_74",
+ "party_account_currency",
+ "inter_company_order_reference"
],
"fields": [
{
@@ -313,34 +315,34 @@
{
"fieldname": "supplier_address",
"fieldtype": "Link",
- "label": "Select Supplier Address",
+ "label": "Supplier Address",
"options": "Address",
"print_hide": 1
},
{
"fieldname": "contact_person",
"fieldtype": "Link",
- "label": "Contact Person",
+ "label": "Supplier Contact",
"options": "Contact",
"print_hide": 1
},
{
"fieldname": "address_display",
"fieldtype": "Small Text",
- "label": "Address",
+ "label": "Supplier Address Details",
"read_only": 1
},
{
"fieldname": "contact_display",
"fieldtype": "Small Text",
"in_global_search": 1,
- "label": "Contact",
+ "label": "Contact Name",
"read_only": 1
},
{
"fieldname": "contact_mobile",
"fieldtype": "Small Text",
- "label": "Mobile No",
+ "label": "Contact Mobile No",
"read_only": 1
},
{
@@ -358,14 +360,14 @@
{
"fieldname": "shipping_address",
"fieldtype": "Link",
- "label": "Select Shipping Address",
+ "label": "Company Shipping Address",
"options": "Address",
"print_hide": 1
},
{
"fieldname": "shipping_address_display",
"fieldtype": "Small Text",
- "label": "Shipping Address",
+ "label": "Shipping Address Details",
"print_hide": 1,
"read_only": 1
},
@@ -433,7 +435,8 @@
},
{
"fieldname": "sec_warehouse",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Subcontracting"
},
{
"description": "Sets 'Warehouse' in each row of the Items table.",
@@ -466,6 +469,7 @@
{
"fieldname": "items_section",
"fieldtype": "Section Break",
+ "hide_border": 1,
"oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart"
},
@@ -598,7 +602,8 @@
},
{
"fieldname": "section_break_52",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "hide_border": 1
},
{
"fieldname": "taxes",
@@ -626,10 +631,12 @@
{
"fieldname": "totals",
"fieldtype": "Section Break",
+ "label": "Taxes and Charges",
"oldfieldtype": "Section Break",
"options": "fa fa-money"
},
{
+ "depends_on": "base_taxes_and_charges_added",
"fieldname": "base_taxes_and_charges_added",
"fieldtype": "Currency",
"label": "Taxes and Charges Added (Company Currency)",
@@ -640,6 +647,7 @@
"read_only": 1
},
{
+ "depends_on": "base_taxes_and_charges_deducted",
"fieldname": "base_taxes_and_charges_deducted",
"fieldtype": "Currency",
"label": "Taxes and Charges Deducted (Company Currency)",
@@ -650,6 +658,7 @@
"read_only": 1
},
{
+ "depends_on": "base_total_taxes_and_charges",
"fieldname": "base_total_taxes_and_charges",
"fieldtype": "Currency",
"label": "Total Taxes and Charges (Company Currency)",
@@ -665,6 +674,7 @@
"fieldtype": "Column Break"
},
{
+ "depends_on": "taxes_and_charges_added",
"fieldname": "taxes_and_charges_added",
"fieldtype": "Currency",
"label": "Taxes and Charges Added",
@@ -675,6 +685,7 @@
"read_only": 1
},
{
+ "depends_on": "taxes_and_charges_deducted",
"fieldname": "taxes_and_charges_deducted",
"fieldtype": "Currency",
"label": "Taxes and Charges Deducted",
@@ -685,6 +696,7 @@
"read_only": 1
},
{
+ "depends_on": "total_taxes_and_charges",
"fieldname": "total_taxes_and_charges",
"fieldtype": "Currency",
"label": "Total Taxes and Charges",
@@ -694,7 +706,7 @@
},
{
"collapsible": 1,
- "collapsible_depends_on": "discount_amount",
+ "collapsible_depends_on": "apply_discount_on",
"fieldname": "discount_section",
"fieldtype": "Section Break",
"label": "Additional Discount"
@@ -734,7 +746,8 @@
},
{
"fieldname": "totals_section",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Totals"
},
{
"fieldname": "base_grand_total",
@@ -902,12 +915,12 @@
},
{
"fieldname": "ref_sq",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Ref SQ",
+ "fieldtype": "Link",
+ "label": "Supplier Quotation",
"no_copy": 1,
"oldfieldname": "ref_sq",
"oldfieldtype": "Data",
+ "options": "Supplier Quotation",
"print_hide": 1,
"read_only": 1
},
@@ -1061,7 +1074,7 @@
"collapsible": 1,
"fieldname": "tracking_section",
"fieldtype": "Section Break",
- "label": "Tracking"
+ "label": "Order Status"
},
{
"fieldname": "column_break_75",
@@ -1070,21 +1083,29 @@
{
"fieldname": "billing_address",
"fieldtype": "Link",
- "label": "Select Billing Address",
+ "label": "Company Billing Address",
"options": "Address"
},
{
"fieldname": "billing_address_display",
"fieldtype": "Small Text",
- "label": "Billing Address",
+ "label": "Billing Address Details",
"read_only": 1
+ },
+ {
+ "fieldname": "before_items_section",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "items_col_break",
+ "fieldtype": "Column Break"
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-07 14:31:57.661221",
+ "modified": "2020-10-30 11:39:37.388249",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 7a52c28a0e..10db240a44 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -24,6 +24,7 @@
"col_break2",
"uom",
"conversion_factor",
+ "stock_qty",
"sec_break1",
"price_list_rate",
"discount_percentage",
@@ -46,11 +47,8 @@
"column_break_32",
"base_net_rate",
"base_net_amount",
- "billed_amt",
"warehouse_and_reference",
"warehouse",
- "delivered_by_supplier",
- "project",
"material_request",
"material_request_item",
"sales_order",
@@ -58,36 +56,37 @@
"supplier_quotation",
"supplier_quotation_item",
"col_break5",
+ "delivered_by_supplier",
"against_blanket_order",
"blanket_order",
"blanket_order_rate",
"item_group",
"brand",
- "bom",
- "include_exploded_items",
"section_break_56",
- "stock_qty",
- "column_break_60",
"received_qty",
"returned_qty",
- "manufacture_details",
- "manufacturer",
- "column_break_14",
- "manufacturer_part_no",
- "more_info_section_break",
- "is_fixed_asset",
- "item_tax_rate",
+ "column_break_60",
+ "billed_amt",
"accounting_details",
"expense_account",
- "column_break_68",
+ "manufacture_details",
+ "manufacturer",
+ "manufacturer_part_no",
+ "column_break_14",
+ "bom",
+ "include_exploded_items",
"item_weight_details",
"weight_per_unit",
"total_weight",
"column_break_40",
"weight_uom",
"accounting_dimensions_section",
- "cost_center",
+ "project",
"dimension_col_break",
+ "cost_center",
+ "more_info_section_break",
+ "is_fixed_asset",
+ "item_tax_rate",
"section_break_72",
"page_break"
],
@@ -346,6 +345,7 @@
},
{
"default": "0",
+ "depends_on": "is_free_item",
"fieldname": "is_free_item",
"fieldtype": "Check",
"label": "Is Free Item",
@@ -508,9 +508,10 @@
},
{
"default": "0",
+ "depends_on": "delivered_by_supplier",
"fieldname": "delivered_by_supplier",
"fieldtype": "Check",
- "label": "To be delivered to customer",
+ "label": "To be Delivered to Customer",
"print_hide": 1,
"read_only": 1
},
@@ -558,6 +559,7 @@
"read_only": 1
},
{
+ "depends_on": "eval:parent.is_subcontracted == 'Yes'",
"fieldname": "bom",
"fieldtype": "Link",
"label": "BOM",
@@ -574,21 +576,21 @@
},
{
"fieldname": "section_break_56",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Billed, Received & Returned"
},
{
"fieldname": "stock_qty",
"fieldtype": "Float",
- "label": "Qty as per Stock UOM",
+ "label": "Qty in Stock UOM",
"no_copy": 1,
- "oldfieldname": "stock_qty",
- "oldfieldtype": "Currency",
"print_hide": 1,
"print_width": "100px",
"read_only": 1,
"width": "100px"
},
{
+ "depends_on": "received_qty",
"fieldname": "received_qty",
"fieldtype": "Float",
"label": "Received Qty",
@@ -612,9 +614,10 @@
"fieldtype": "Column Break"
},
{
+ "depends_on": "billed_amt",
"fieldname": "billed_amt",
"fieldtype": "Currency",
- "label": "Billed Amt",
+ "label": "Billed Amount",
"no_copy": 1,
"options": "currency",
"print_hide": 1,
@@ -633,6 +636,7 @@
"report_hide": 1
},
{
+ "collapsible": 1,
"fieldname": "accounting_details",
"fieldtype": "Section Break",
"label": "Accounting Details"
@@ -644,10 +648,6 @@
"options": "Account",
"print_hide": 1
},
- {
- "fieldname": "column_break_68",
- "fieldtype": "Column Break"
- },
{
"fieldname": "cost_center",
"fieldtype": "Link",
@@ -715,6 +715,7 @@
},
{
"default": "0",
+ "depends_on": "is_fixed_asset",
"fetch_from": "item_code.is_fixed_asset",
"fieldname": "is_fixed_asset",
"fieldtype": "Check",
@@ -728,9 +729,10 @@
}
],
"idx": 1,
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-04-21 11:55:58.643393",
+ "modified": "2020-10-30 11:59:47.670951",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
diff --git a/erpnext/crm/doctype/contract_template/contract_template.json b/erpnext/crm/doctype/contract_template/contract_template.json
index ef9974f863..5e4582f8d3 100644
--- a/erpnext/crm/doctype/contract_template/contract_template.json
+++ b/erpnext/crm/doctype/contract_template/contract_template.json
@@ -23,8 +23,7 @@
{
"fieldname": "contract_terms",
"fieldtype": "Text Editor",
- "label": "Contract Terms and Conditions",
- "read_only": 1
+ "label": "Contract Terms and Conditions"
},
{
"fieldname": "sb_fulfilment",
@@ -45,7 +44,7 @@
}
],
"links": [],
- "modified": "2020-06-03 00:24:58.179816",
+ "modified": "2020-11-11 17:49:44.879363",
"modified_by": "Administrator",
"module": "CRM",
"name": "Contract Template",
diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py
index d59f909298..8aa7453bd6 100644
--- a/erpnext/erpnext_integrations/connectors/shopify_connection.py
+++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py
@@ -2,12 +2,13 @@ from __future__ import unicode_literals
import frappe
from frappe import _
import json
-from frappe.utils import cstr, cint, nowdate, flt
+from frappe.utils import cstr, cint, nowdate, getdate, flt, get_request_session, get_datetime
from erpnext.erpnext_integrations.utils import validate_webhooks_request
from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import sync_item_from_shopify
from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer
from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log, dump_request_data
+from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header
@frappe.whitelist(allow_guest=True)
@validate_webhooks_request("Shopify Settings", 'X-Shopify-Hmac-Sha256', secret_key='shared_secret')
@@ -18,7 +19,7 @@ def store_request_data(order=None, event=None):
dump_request_data(order, event)
-def sync_sales_order(order, request_id=None):
+def sync_sales_order(order, request_id=None, old_order_sync=False):
frappe.set_user('Administrator')
shopify_settings = frappe.get_doc("Shopify Settings")
frappe.flags.request_id = request_id
@@ -27,7 +28,7 @@ def sync_sales_order(order, request_id=None):
try:
validate_customer(order, shopify_settings)
validate_item(order, shopify_settings)
- create_order(order, shopify_settings)
+ create_order(order, shopify_settings, old_order_sync=old_order_sync)
except Exception as e:
make_shopify_log(status="Error", exception=e)
@@ -77,13 +78,13 @@ def validate_item(order, shopify_settings):
if item.get("product_id") and not frappe.db.get_value("Item", {"shopify_product_id": item.get("product_id")}, "name"):
sync_item_from_shopify(shopify_settings, item)
-def create_order(order, shopify_settings, company=None):
+def create_order(order, shopify_settings, old_order_sync=False, company=None):
so = create_sales_order(order, shopify_settings, company)
if so:
if order.get("financial_status") == "paid":
- create_sales_invoice(order, shopify_settings, so)
+ create_sales_invoice(order, shopify_settings, so, old_order_sync=old_order_sync)
- if order.get("fulfillments"):
+ if order.get("fulfillments") and not old_order_sync:
create_delivery_note(order, shopify_settings, so)
def create_sales_order(shopify_order, shopify_settings, company=None):
@@ -92,7 +93,7 @@ def create_sales_order(shopify_order, shopify_settings, company=None):
so = frappe.db.get_value("Sales Order", {"shopify_order_id": shopify_order.get("id")}, "name")
if not so:
- items = get_order_items(shopify_order.get("line_items"), shopify_settings)
+ items = get_order_items(shopify_order.get("line_items"), shopify_settings, getdate(shopify_order.get('created_at')))
if not items:
message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master'
@@ -106,8 +107,10 @@ def create_sales_order(shopify_order, shopify_settings, company=None):
"doctype": "Sales Order",
"naming_series": shopify_settings.sales_order_series or "SO-Shopify-",
"shopify_order_id": shopify_order.get("id"),
+ "shopify_order_number": shopify_order.get("name"),
"customer": customer or shopify_settings.default_customer,
- "delivery_date": nowdate(),
+ "transaction_date": getdate(shopify_order.get("created_at")) or nowdate(),
+ "delivery_date": getdate(shopify_order.get("created_at")) or nowdate(),
"company": shopify_settings.company,
"selling_price_list": shopify_settings.price_list,
"ignore_pricing_rule": 1,
@@ -132,12 +135,20 @@ def create_sales_order(shopify_order, shopify_settings, company=None):
frappe.db.commit()
return so
-def create_sales_invoice(shopify_order, shopify_settings, so):
+def create_sales_invoice(shopify_order, shopify_settings, so, old_order_sync=False):
if not frappe.db.get_value("Sales Invoice", {"shopify_order_id": shopify_order.get("id")}, "name")\
and so.docstatus==1 and not so.per_billed and cint(shopify_settings.sync_sales_invoice):
+ if old_order_sync:
+ posting_date = getdate(shopify_order.get('created_at'))
+ else:
+ posting_date = nowdate()
+
si = make_sales_invoice(so.name, ignore_permissions=True)
si.shopify_order_id = shopify_order.get("id")
+ si.shopify_order_number = shopify_order.get("name")
+ si.set_posting_time = 1
+ si.posting_date = posting_date
si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-"
si.flags.ignore_mandatory = True
set_cost_center(si.items, shopify_settings.cost_center)
@@ -169,6 +180,9 @@ def create_delivery_note(shopify_order, shopify_settings, so):
dn = make_delivery_note(so.name)
dn.shopify_order_id = fulfillment.get("order_id")
+ dn.shopify_order_number = shopify_order.get("name")
+ dn.set_posting_time = 1
+ dn.posting_date = getdate(fulfillment.get("created_at"))
dn.shopify_fulfillment_id = fulfillment.get("id")
dn.naming_series = shopify_settings.delivery_note_series or "DN-Shopify-"
dn.items = get_fulfillment_items(dn.items, fulfillment.get("line_items"), shopify_settings)
@@ -187,7 +201,7 @@ def get_discounted_amount(order):
discounted_amount += flt(discount.get("amount"))
return discounted_amount
-def get_order_items(order_items, shopify_settings):
+def get_order_items(order_items, shopify_settings, delivery_date):
items = []
all_product_exists = True
product_not_exists = []
@@ -205,7 +219,7 @@ def get_order_items(order_items, shopify_settings):
"item_code": item_code,
"item_name": shopify_item.get("name"),
"rate": shopify_item.get("price"),
- "delivery_date": nowdate(),
+ "delivery_date": delivery_date,
"qty": shopify_item.get("quantity"),
"stock_uom": shopify_item.get("uom") or _("Nos"),
"warehouse": shopify_settings.warehouse
@@ -265,3 +279,64 @@ def get_tax_account_head(tax):
frappe.throw(_("Tax Account not specified for Shopify Tax {0}").format(tax.get("title")))
return tax_account
+
+@frappe.whitelist(allow_guest=True)
+def sync_old_orders():
+ frappe.set_user('Administrator')
+ shopify_settings = frappe.get_doc('Shopify Settings')
+
+ if not shopify_settings.sync_missing_orders:
+ return
+
+ url = get_url(shopify_settings)
+ session = get_request_session()
+
+ try:
+ res = session.get(url, headers=get_header(shopify_settings))
+ res.raise_for_status()
+ orders = res.json()["orders"]
+
+ for order in orders:
+ if is_sync_complete(shopify_settings, order):
+ stop_sync(shopify_settings)
+ return
+
+ sync_sales_order(order=order, old_order_sync=True)
+ last_order_id = order.get('id')
+
+ if last_order_id:
+ shopify_settings.load_from_db()
+ shopify_settings.last_order_id = last_order_id
+ shopify_settings.save()
+ frappe.db.commit()
+
+ except Exception as e:
+ raise e
+
+def stop_sync(shopify_settings):
+ shopify_settings.sync_missing_orders = 0
+ shopify_settings.last_order_id = ''
+ shopify_settings.save()
+ frappe.db.commit()
+
+def get_url(shopify_settings):
+ last_order_id = shopify_settings.last_order_id
+
+ if not last_order_id:
+ if shopify_settings.sync_based_on == 'Date':
+ url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&created_at_min={0}&since_id=0".format(
+ get_datetime(shopify_settings.from_date)), shopify_settings)
+ else:
+ url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(
+ shopify_settings.from_order_id), shopify_settings)
+ else:
+ url = get_shopify_url("admin/api/2020-10/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings)
+
+ return url
+
+def is_sync_complete(shopify_settings, order):
+ if shopify_settings.sync_based_on == 'Date':
+ return getdate(shopify_settings.to_date) < getdate(order.get('created_at'))
+ else:
+ return cstr(order.get('id')) == cstr(shopify_settings.to_order_id)
+
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
index 2e10751f96..20ec06373e 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json
@@ -1,7 +1,9 @@
{
+ "actions": [],
"creation": "2015-05-18 05:21:07.270859",
"doctype": "DocType",
"document_type": "System",
+ "engine": "InnoDB",
"field_order": [
"status_html",
"enable_shopify",
@@ -40,7 +42,16 @@
"sales_invoice_series",
"section_break_22",
"html_16",
- "taxes"
+ "taxes",
+ "syncing_details_section",
+ "sync_missing_orders",
+ "sync_based_on",
+ "column_break_41",
+ "from_date",
+ "to_date",
+ "from_order_id",
+ "to_order_id",
+ "last_order_id"
],
"fields": [
{
@@ -255,10 +266,71 @@
"fieldtype": "Table",
"label": "Shopify Tax Account",
"options": "Shopify Tax Account"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "syncing_details_section",
+ "fieldtype": "Section Break",
+ "label": "Syncing Missing Orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_missing_orders",
+ "fieldname": "last_order_id",
+ "fieldtype": "Data",
+ "label": "Last Order Id",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_41",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "description": "On checking this Order from the ",
+ "fieldname": "sync_missing_orders",
+ "fieldtype": "Check",
+ "label": "Sync Missing Old Shopify Orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_missing_orders",
+ "fieldname": "sync_based_on",
+ "fieldtype": "Select",
+ "label": "Sync Based On",
+ "mandatory_depends_on": "eval:doc.sync_missing_orders",
+ "options": "\nDate\nShopify Order Id"
+ },
+ {
+ "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders",
+ "fieldname": "from_date",
+ "fieldtype": "Date",
+ "label": "From Date",
+ "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders",
+ "fieldname": "to_date",
+ "fieldtype": "Date",
+ "label": "To Date",
+ "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders",
+ "fieldname": "from_order_id",
+ "fieldtype": "Data",
+ "label": "From Order Id",
+ "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders"
+ },
+ {
+ "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders",
+ "fieldname": "to_order_id",
+ "fieldtype": "Data",
+ "label": "To Order Id",
+ "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders"
}
],
"issingle": 1,
- "modified": "2020-09-18 17:26:09.703215",
+ "links": [],
+ "modified": "2020-11-05 20:44:03.664891",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Shopify Settings",
@@ -277,4 +349,4 @@
],
"sort_field": "modified",
"sort_order": "DESC"
-}
+}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
index 25ffd28109..cbdf90681d 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
@@ -87,7 +87,7 @@ def get_shopify_url(path, settings):
def get_header(settings):
header = {'Content-Type': 'application/json'}
- return header;
+ return header
@frappe.whitelist()
def get_series():
@@ -121,17 +121,23 @@ def setup_custom_fields():
],
"Sales Order": [
dict(fieldname='shopify_order_id', label='Shopify Order Id',
- fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
+ fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
+ dict(fieldname='shopify_order_number', label='Shopify Order Number',
+ fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1)
],
"Delivery Note":[
dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
+ dict(fieldname='shopify_order_number', label='Shopify Order Number',
+ fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1),
dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
],
"Sales Invoice": [
dict(fieldname='shopify_order_id', label='Shopify Order Id',
- fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
+ fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
+ dict(fieldname='shopify_order_number', label='Shopify Order Number',
+ fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1)
]
}
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
index 64ef3dc085..30fa23cfb4 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py
@@ -58,7 +58,7 @@ class ShopifySettings(unittest.TestCase):
}).save(ignore_permissions=True)
self.shopify_settings = shopify_settings
-
+
def test_order(self):
### Create Customer ###
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer:
@@ -75,7 +75,7 @@ class ShopifySettings(unittest.TestCase):
with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order:
shopify_order = json.load(shopify_order)
- create_order(shopify_order.get("order"), self.shopify_settings, "_Test Company")
+ create_order(shopify_order.get("order"), self.shopify_settings, False, company="_Test Company")
sales_order = frappe.get_doc("Sales Order", {"shopify_order_id": cstr(shopify_order.get("order").get("id"))})
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 90ae6442e4..78ef66585f 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -307,6 +307,7 @@ scheduler_events = {
"erpnext.projects.doctype.project.project.collect_project_status",
"erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts",
"erpnext.support.doctype.issue.issue.set_service_level_agreement_variance",
+ "erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders",
],
"daily": [
"erpnext.stock.reorder_item.reorder_item",
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.js b/erpnext/hr/doctype/upload_attendance/upload_attendance.js
index 9df2948a15..29aa85484a 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.js
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.js
@@ -24,10 +24,10 @@ erpnext.hr.AttendanceControlPanel = frappe.ui.form.Controller.extend({
}
window.location.href = repl(frappe.request.url +
'?cmd=%(cmd)s&from_date=%(from_date)s&to_date=%(to_date)s', {
- cmd: "erpnext.hr.doctype.upload_attendance.upload_attendance.get_template",
- from_date: this.frm.doc.att_fr_date,
- to_date: this.frm.doc.att_to_date,
- });
+ cmd: "erpnext.hr.doctype.upload_attendance.upload_attendance.get_template",
+ from_date: this.frm.doc.att_fr_date,
+ to_date: this.frm.doc.att_to_date,
+ });
},
show_upload() {
diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.py b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
index edf05e827b..674c8e3eb4 100644
--- a/erpnext/hr/doctype/upload_attendance/upload_attendance.py
+++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.py
@@ -28,7 +28,12 @@ def get_template():
w = UnicodeWriter()
w = add_header(w)
- w = add_data(w, args)
+ try:
+ w = add_data(w, args)
+ except Exception as e:
+ frappe.clear_messages()
+ frappe.respond_as_web_page("Holiday List Missing", html=e)
+ return
# write out response as a type csv
frappe.response['result'] = cstr(w.getvalue())
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 2ab1b98707..8888a96768 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -76,6 +76,7 @@ class BOM(WebsiteGenerator):
self.set_routing_operations()
self.validate_operations()
self.calculate_cost()
+ self.update_stock_qty()
self.update_cost(update_parent=False, from_child_bom=True, save=False)
def get_context(self, context):
@@ -84,8 +85,6 @@ class BOM(WebsiteGenerator):
def on_update(self):
frappe.cache().hdel('bom_children', self.name)
self.check_recursion()
- self.update_stock_qty()
- self.update_exploded_items()
def on_submit(self):
self.manage_default_bom()
@@ -237,7 +236,8 @@ class BOM(WebsiteGenerator):
self.calculate_cost()
if save:
self.db_update()
- self.update_exploded_items()
+
+ self.update_exploded_items(save=save)
# update parent BOMs
if self.total_cost != existing_bom_cost and update_parent:
@@ -318,8 +318,6 @@ class BOM(WebsiteGenerator):
m.uom = m.stock_uom
m.qty = m.stock_qty
- m.db_update()
-
def validate_uom_is_interger(self):
from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "uom", "qty", "BOM Item")
@@ -372,15 +370,6 @@ class BOM(WebsiteGenerator):
if raise_exception:
frappe.throw(_("BOM recursion: {0} cannot be parent or child of {1}").format(self.name, self.name))
- def update_cost_and_exploded_items(self, bom_list=[]):
- bom_list = self.traverse_tree(bom_list)
- for bom in bom_list:
- bom_obj = frappe.get_doc("BOM", bom)
- bom_obj.check_recursion(bom_list=bom_list)
- bom_obj.update_exploded_items()
-
- return bom_list
-
def traverse_tree(self, bom_list=None):
def _get_children(bom_no):
children = frappe.cache().hget('bom_children', bom_no)
@@ -472,10 +461,10 @@ class BOM(WebsiteGenerator):
d.rate = rate
d.amount = (d.stock_qty or d.qty) * rate
- def update_exploded_items(self):
+ def update_exploded_items(self, save=True):
""" Update Flat BOM, following will be correct data"""
self.get_exploded_items()
- self.add_exploded_items()
+ self.add_exploded_items(save=save)
def get_exploded_items(self):
""" Get all raw materials including items from child bom"""
@@ -544,11 +533,13 @@ class BOM(WebsiteGenerator):
'sourced_by_supplier': d.get('sourced_by_supplier', 0)
}))
- def add_exploded_items(self):
+ def add_exploded_items(self, save=True):
"Add items to Flat BOM table"
- frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name)
self.set('exploded_items', [])
+ if save:
+ frappe.db.sql("""delete from `tabBOM Explosion Item` where parent=%s""", self.name)
+
for d in sorted(self.cur_exploded_items, key=itemgetter(0)):
ch = self.append('exploded_items', {})
for i in self.cur_exploded_items[d].keys():
@@ -556,7 +547,9 @@ class BOM(WebsiteGenerator):
ch.amount = flt(ch.stock_qty) * flt(ch.rate)
ch.qty_consumed_per_unit = flt(ch.stock_qty) / flt(self.quantity)
ch.docstatus = self.docstatus
- ch.db_insert()
+
+ if save:
+ ch.db_insert()
def validate_bom_links(self):
if not self.is_active:
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index 5f8a13428c..e53927918e 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -443,6 +443,11 @@ class TestWorkOrder(unittest.TestCase):
ste1 = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1))
self.assertEqual(len(ste1.items), 3)
+ def test_cost_center_for_manufacture(self):
+ wo_order = make_wo_order_test_record()
+ ste = make_stock_entry(wo_order.name, "Material Transfer for Manufacture", wo_order.qty)
+ self.assertEquals(ste.get("items")[0].get("cost_center"), "_Test Cost Center - _TC")
+
def test_operation_time_with_batch_size(self):
fg_item = "Test Batch Size Item For BOM"
rm1 = "Test Batch Size Item RM 1 For BOM"
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 34dbdd0bd5..25be884117 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -733,4 +733,5 @@ erpnext.patches.v13_0.print_uom_after_quantity_patch
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
-execute:frappe.delete_doc("Report", "Quoted Item Comparison")
\ No newline at end of file
+erpnext.patches.v13_0.update_custom_fields_for_shopify
+execute:frappe.delete_doc("Report", "Quoted Item Comparison")
diff --git a/erpnext/patches/v13_0/update_custom_fields_for_shopify.py b/erpnext/patches/v13_0/update_custom_fields_for_shopify.py
new file mode 100644
index 0000000000..f1d2ea2d74
--- /dev/null
+++ b/erpnext/patches/v13_0/update_custom_fields_for_shopify.py
@@ -0,0 +1,10 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import setup_custom_fields
+
+def execute():
+ if frappe.db.get_single_value('Shopify Settings', 'enable_shopify'):
+ setup_custom_fields()
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index 30ea432678..a3d12c35c0 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -344,9 +344,13 @@ class PayrollEntry(Document):
employees_to_mark_attendance = []
days_in_payroll, days_holiday, days_attendance_marked = 0, 0, 0
for employee_detail in self.employees:
- days_holiday = self.get_count_holidays_of_employee(employee_detail.employee)
- days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee)
- days_in_payroll = date_diff(self.end_date, self.start_date) + 1
+ employee_joining_date = frappe.db.get_value("Employee", employee_detail.employee, 'date_of_joining')
+ start_date = self.start_date
+ if employee_joining_date > getdate(self.start_date):
+ start_date = employee_joining_date
+ days_holiday = self.get_count_holidays_of_employee(employee_detail.employee, start_date)
+ days_attendance_marked = self.get_count_employee_attendance(employee_detail.employee, start_date)
+ days_in_payroll = date_diff(self.end_date, start_date) + 1
if days_in_payroll > days_holiday + days_attendance_marked:
employees_to_mark_attendance.append({
"employee": employee_detail.employee,
@@ -354,22 +358,25 @@ class PayrollEntry(Document):
})
return employees_to_mark_attendance
- def get_count_holidays_of_employee(self, employee):
+ def get_count_holidays_of_employee(self, employee, start_date):
holiday_list = get_holiday_list_for_employee(employee)
holidays = 0
if holiday_list:
days = frappe.db.sql("""select count(*) from tabHoliday where
parent=%s and holiday_date between %s and %s""", (holiday_list,
- self.start_date, self.end_date))
+ start_date, self.end_date))
if days and days[0][0]:
holidays = days[0][0]
return holidays
- def get_count_employee_attendance(self, employee):
+ def get_count_employee_attendance(self, employee, start_date):
marked_days = 0
- attendances = frappe.db.sql("""select count(*) from tabAttendance where
- employee=%s and docstatus=1 and attendance_date between %s and %s""",
- (employee, self.start_date, self.end_date))
+ attendances = frappe.get_all("Attendance",
+ fields = ["count(*)"],
+ filters = {
+ "employee": employee,
+ "attendance_date": ('between', [start_date, self.end_date])
+ }, as_list=1)
if attendances and attendances[0][0]:
marked_days = attendances[0][0]
return marked_days
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.js b/erpnext/regional/doctype/datev_settings/datev_settings.js
index 69747b0b89..f04705929f 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.js
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.js
@@ -2,7 +2,7 @@
// For license information, please see license.txt
frappe.ui.form.on('DATEV Settings', {
- // refresh: function(frm) {
-
- // }
+ refresh: function(frm) {
+ frm.add_custom_button('Show Report', () => frappe.set_route('query-report', 'DATEV'), "fa fa-table");
+ }
});
diff --git a/erpnext/regional/doctype/datev_settings/datev_settings.json b/erpnext/regional/doctype/datev_settings/datev_settings.json
index 39486dfc12..713e8e34ef 100644
--- a/erpnext/regional/doctype/datev_settings/datev_settings.json
+++ b/erpnext/regional/doctype/datev_settings/datev_settings.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "field:client",
"creation": "2019-08-13 23:56:34.259906",
"doctype": "DocType",
@@ -6,6 +7,7 @@
"engine": "InnoDB",
"field_order": [
"client",
+ "account_number_length",
"column_break_2",
"client_number",
"section_break_4",
@@ -28,8 +30,8 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Client ID",
- "reqd": 1,
- "length": 5
+ "length": 5,
+ "reqd": 1
},
{
"fieldname": "consultant",
@@ -43,8 +45,8 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Consultant ID",
- "reqd": 1,
- "length": 7
+ "length": 7,
+ "reqd": 1
},
{
"fieldname": "column_break_2",
@@ -57,9 +59,17 @@
{
"fieldname": "column_break_6",
"fieldtype": "Column Break"
+ },
+ {
+ "default": "4",
+ "fieldname": "account_number_length",
+ "fieldtype": "Int",
+ "label": "Account Number Length",
+ "reqd": 1
}
],
- "modified": "2019-08-14 00:03:26.616460",
+ "links": [],
+ "modified": "2020-11-05 17:52:11.674329",
"modified_by": "Administrator",
"module": "Regional",
"name": "DATEV Settings",
@@ -104,4 +114,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
index cf07a1c824..f138a807bc 100644
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ b/erpnext/regional/germany/utils/datev/datev_csv.py
@@ -106,7 +106,7 @@ def get_header(filters, csv_class):
# M = Start of the fiscal year (Wirtschaftsjahresbeginn)
frappe.utils.formatdate(filters.get('fiscal_year_start'), 'yyyyMMdd'),
# N = Length of account numbers (Sachkontenlänge)
- datev_settings.get('account_number_length', '4'),
+ str(filters.get('account_number_length', 4)),
# O = Transaction batch start date (YYYYMMDD)
frappe.utils.formatdate(filters.get('from_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '',
# P = Transaction batch end date (YYYYMMDD)
diff --git a/erpnext/regional/report/datev/datev.js b/erpnext/regional/report/datev/datev.js
index 55f12cf373..4124e3df19 100644
--- a/erpnext/regional/report/datev/datev.js
+++ b/erpnext/regional/report/datev/datev.js
@@ -11,14 +11,14 @@ frappe.query_reports["DATEV"] = {
{
"fieldname": "from_date",
"label": __("From Date"),
- "default": frappe.datetime.month_start(),
+ "default": moment().subtract(1, 'month').startOf('month').format(),
"fieldtype": "Date",
"reqd": 1
},
{
"fieldname": "to_date",
"label": __("To Date"),
- "default": frappe.datetime.now_date(),
+ "default": moment().subtract(1, 'month').endOf('month').format(),
"fieldtype": "Date",
"reqd": 1
},
@@ -30,9 +30,23 @@ frappe.query_reports["DATEV"] = {
}
],
onload: function(query_report) {
+ let company = frappe.query_report.get_filter_value('company');
+ frappe.db.exists('DATEV Settings', company).then((settings_exist) => {
+ if (!settings_exist) {
+ frappe.confirm(__('DATEV Settings for your Company are missing. Would you like to create them now?'),
+ () => frappe.new_doc('DATEV Settings', {'company': company})
+ );
+ }
+ });
+
query_report.page.add_menu_item(__("Download DATEV File"), () => {
const filters = JSON.stringify(query_report.get_values());
window.open(`/api/method/erpnext.regional.report.datev.datev.download_datev_csv?filters=${filters}`);
});
+
+ query_report.page.add_menu_item(__("Change DATEV Settings"), () => {
+ let company = frappe.query_report.get_filter_value('company'); // read company from filters again – it might have changed by now.
+ frappe.set_route('Form', 'DATEV Settings', company);
+ });
}
};
diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py
index dbae230f1e..1e39c57786 100644
--- a/erpnext/regional/report/datev/datev.py
+++ b/erpnext/regional/report/datev/datev.py
@@ -94,8 +94,11 @@ COLUMNS = [
def execute(filters=None):
"""Entry point for frappe."""
- validate(filters)
- return COLUMNS, get_transactions(filters, as_dict=0)
+ data = []
+ if filters and validate(filters):
+ data = get_transactions(filters, as_dict=0)
+
+ return COLUMNS, data
def validate(filters):
@@ -114,10 +117,14 @@ def validate(filters):
validate_fiscal_year(from_date, to_date, company)
- try:
- frappe.get_doc('DATEV Settings', filters.get('company'))
- except frappe.DoesNotExistError:
- frappe.throw(_('Please create DATEV Settings for Company {}.').format(filters.get('company')))
+ if not frappe.db.exists('DATEV Settings', filters.get('company')):
+ frappe.log_error(_('Please create {} for Company {}.').format(
+ '{}'.format(_('DATEV Settings')),
+ frappe.bold(filters.get('company'))
+ ))
+ return False
+
+ return True
def validate_fiscal_year(from_date, to_date, company):
@@ -340,6 +347,8 @@ def download_datev_csv(filters):
coa = frappe.get_value('Company', company, 'chart_of_accounts')
filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '')
+ filters['account_number_length'] = frappe.get_value('DATEV Settings', company, 'account_number_length')
+
transactions = get_transactions(filters)
account_names = get_account_names(filters)
customers = get_customers(filters)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 768526705c..e3159b95c3 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1227,8 +1227,6 @@ class StockEntry(StockController):
return item_dict
def add_to_stock_entry_detail(self, item_dict, bom_no=None):
- cost_center = frappe.db.get_value("Company", self.company, 'cost_center')
-
for d in item_dict:
stock_uom = item_dict[d].get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
@@ -1239,9 +1237,10 @@ class StockEntry(StockController):
se_child.uom = item_dict[d]["uom"] if item_dict[d].get("uom") else stock_uom
se_child.stock_uom = stock_uom
se_child.qty = flt(item_dict[d]["qty"], se_child.precision("qty"))
- se_child.cost_center = item_dict[d].get("cost_center") or cost_center
se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0)
se_child.subcontracted_item = item_dict[d].get("main_item_code")
+ se_child.cost_center = (item_dict[d].get("cost_center") or
+ get_default_cost_center(item_dict[d], company = self.company))
for field in ["idx", "po_detail", "original_item",
"expense_account", "description", "item_name"]:
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 8d8dcb74c3..08f7a83b89 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -559,23 +559,40 @@ def get_default_deferred_account(args, item, fieldname=None):
else:
return None
-def get_default_cost_center(args, item, item_group, brand, company=None):
+def get_default_cost_center(args, item=None, item_group=None, brand=None, company=None):
cost_center = None
+
+ if not company and args.get("company"):
+ company = args.get("company")
+
if args.get('project'):
cost_center = frappe.db.get_value("Project", args.get("project"), "cost_center", cache=True)
- if not cost_center:
+ if not cost_center and (item and item_group and brand):
if args.get('customer'):
cost_center = item.get('selling_cost_center') or item_group.get('selling_cost_center') or brand.get('selling_cost_center')
else:
cost_center = item.get('buying_cost_center') or item_group.get('buying_cost_center') or brand.get('buying_cost_center')
- cost_center = cost_center or args.get("cost_center")
+ elif not cost_center and args.get("item_code") and company:
+ for method in ["get_item_defaults", "get_item_group_defaults", "get_brand_defaults"]:
+ path = "erpnext.stock.get_item_details.{0}".format(method)
+ data = frappe.get_attr(path)(args.get("item_code"), company)
+
+ if data and (data.selling_cost_center or data.buying_cost_center):
+ return data.selling_cost_center or data.buying_cost_center
+
+ if not cost_center and args.get("cost_center"):
+ cost_center = args.get("cost_center")
if (company and cost_center
and frappe.get_cached_value("Cost Center", cost_center, "company") != company):
return None
+ if not cost_center and company:
+ cost_center = frappe.get_cached_value("Company",
+ company, "cost_center")
+
return cost_center
def get_default_supplier(args, item, item_group, brand):