From 010f619ed365acdb357dd89171904980a2cb41e9 Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Mon, 23 Oct 2017 17:25:52 +0530 Subject: [PATCH 01/38] [Docs] added field for storing map related PR https://github.com/frappe/frappe/pull/4327 --- .../en/customize-erpnext/articles/field-types.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/docs/user/manual/en/customize-erpnext/articles/field-types.md b/erpnext/docs/user/manual/en/customize-erpnext/articles/field-types.md index 24cb4ab727..59888800d6 100644 --- a/erpnext/docs/user/manual/en/customize-erpnext/articles/field-types.md +++ b/erpnext/docs/user/manual/en/customize-erpnext/articles/field-types.md @@ -56,6 +56,17 @@ Integer field holds numeric value, without decimal place. Link field is connected to another master from where it fetches data. For example, in the Quotation master, Customer is a Link field. +- Map + +Use Map field to store GeoJSON featurecollection. Stores polygons, lines and points. Internally it uses following custom properties for identifying a circle. + +``` +{ + "point_type": "circle", + "radius": 10.00 +} +``` + - Password Password field will have decode value in it. From f8006b140fe14ba36dba331113cfcd004ca90370 Mon Sep 17 00:00:00 2001 From: Revant Nandgaonkar Date: Wed, 25 Oct 2017 11:18:29 +0530 Subject: [PATCH 02/38] [Docs] Map field renamed to Geolocation --- .../manual/en/customize-erpnext/articles/field-types.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/docs/user/manual/en/customize-erpnext/articles/field-types.md b/erpnext/docs/user/manual/en/customize-erpnext/articles/field-types.md index 59888800d6..c73ebc76af 100644 --- a/erpnext/docs/user/manual/en/customize-erpnext/articles/field-types.md +++ b/erpnext/docs/user/manual/en/customize-erpnext/articles/field-types.md @@ -56,9 +56,9 @@ Integer field holds numeric value, without decimal place. Link field is connected to another master from where it fetches data. For example, in the Quotation master, Customer is a Link field. -- Map +- Geolocation -Use Map field to store GeoJSON featurecollection. Stores polygons, lines and points. Internally it uses following custom properties for identifying a circle. +Use Geolocation field to store GeoJSON featurecollection. Stores polygons, lines and points. Internally it uses following custom properties for identifying a circle. ``` { @@ -95,4 +95,4 @@ Table will be (sort of) Link field which renders another docytpe within the curr Text Editor is text field. It has text-formatting options. In ERPNext, this field is generally used for defining Terms and Conditions. - \ No newline at end of file + From 6c06e700d60f5f78cdbd618eb0dc00d6cbffcf19 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 11:42:05 +0530 Subject: [PATCH 03/38] [Fix] Unallocated amount considering deductions (#11314) --- .../accounts/doctype/payment_entry/payment_entry.js | 12 ++++++++---- .../accounts/doctype/payment_entry/payment_entry.py | 7 ++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 04db9e28ae..fc9fc97332 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -660,8 +660,15 @@ frappe.ui.form.on('Payment Entry', { var party_amount = frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount; + var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [], + function(d) { return flt(d.amount) })); + if(frm.doc.total_allocated_amount < party_amount) { - unallocated_amount = party_amount - frm.doc.total_allocated_amount; + if(frm.doc.payment_type == "Receive") { + unallocated_amount = party_amount - (frm.doc.total_allocated_amount - total_deductions); + } else { + unallocated_amount = party_amount - (frm.doc.total_allocated_amount + total_deductions); + } } } frm.set_value("unallocated_amount", unallocated_amount); @@ -680,9 +687,6 @@ frappe.ui.form.on('Payment Entry', { difference_amount = flt(frm.doc.base_paid_amount) - flt(frm.doc.base_received_amount); } - var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [], - function(d) { return flt(d.amount) })); - frm.set_value("difference_amount", difference_amount - total_deductions); frm.events.hide_unhide_fields(frm); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9d24261038..56bdfbaf4b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -285,8 +285,13 @@ class PaymentEntry(AccountsController): if self.party: party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount + total_deductions = sum([flt(d.amount) for d in self.get("deductions")]) + if self.total_allocated_amount < party_amount: - self.unallocated_amount = party_amount - self.total_allocated_amount + if self.payment_type == "Receive": + self.unallocated_amount = party_amount - (self.total_allocated_amount - total_deductions) + else: + self.unallocated_amount = party_amount - (self.total_allocated_amount + total_deductions) def set_difference_amount(self): base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate) From a5eee4629f0d70c059260bf61ba3d416b01a3dce Mon Sep 17 00:00:00 2001 From: Manas Solanki Date: Wed, 25 Oct 2017 11:42:22 +0530 Subject: [PATCH 04/38] Fix UI tests (#11309) * decrease the timeout * delete the duplicate test --- .../doctype/offer_letter/test_offer_letter.js | 8 ++-- .../tests/test_training_event.js | 8 +++- .../tests/test_training_event_attendance.js | 40 ------------------- erpnext/tests/ui/tests.txt | 1 - 4 files changed, 10 insertions(+), 47 deletions(-) delete mode 100644 erpnext/hr/doctype/training_event/tests/test_training_event_attendance.js diff --git a/erpnext/hr/doctype/offer_letter/test_offer_letter.js b/erpnext/hr/doctype/offer_letter/test_offer_letter.js index 5b61d64eb5..c9b08a6c50 100644 --- a/erpnext/hr/doctype/offer_letter/test_offer_letter.js +++ b/erpnext/hr/doctype/offer_letter/test_offer_letter.js @@ -27,13 +27,13 @@ QUnit.test("Test: Offer Letter [HR]", function (assert) { ]}, ]); }, - () => frappe.timeout(12), + () => frappe.timeout(10), () => frappe.click_button('Submit'), () => frappe.timeout(2), () => frappe.click_button('Yes'), - () => frappe.timeout(8), + () => frappe.timeout(5), + // To check if the fields are correctly set () => { - // To check if the fields are correctly set assert.ok(cur_frm.get_field('status').value=='Accepted', 'Status of job offer is correct'); assert.ok(cur_frm.get_field('designation').value=='Software Developer', @@ -45,7 +45,7 @@ QUnit.test("Test: Offer Letter [HR]", function (assert) { () => { assert.ok(cur_list.data[0].docstatus==1,'Offer Letter Submitted successfully'); }, - () => frappe.timeout(4), + () => frappe.timeout(2), () => done() ]); }); \ No newline at end of file diff --git a/erpnext/hr/doctype/training_event/tests/test_training_event.js b/erpnext/hr/doctype/training_event/tests/test_training_event.js index a359af3329..8ff4fecd6e 100644 --- a/erpnext/hr/doctype/training_event/tests/test_training_event.js +++ b/erpnext/hr/doctype/training_event/tests/test_training_event.js @@ -1,7 +1,7 @@ QUnit.module('hr'); QUnit.test("Test: Training Event [HR]", function (assert) { - assert.expect(4); + assert.expect(5); let done = assert.async(); let employee_name; @@ -21,7 +21,8 @@ QUnit.test("Test: Training Event [HR]", function (assert) { { employees: [ [ {employee: employee_name}, - {employee_name: 'Test Employee 1'} + {employee_name: 'Test Employee 1'}, + {attendance: 'Optional'} ] ]}, ]); @@ -41,6 +42,9 @@ QUnit.test("Test: Training Event [HR]", function (assert) { assert.ok(cur_frm.doc.employees[0].employee_name=='Test Employee 1', 'Attendee Employee is correctly set'); + + assert.ok(cur_frm.doc.employees[0].attendance=='Optional', + 'Attendance is correctly set'); }, () => frappe.set_route('List','Training Event','List'), diff --git a/erpnext/hr/doctype/training_event/tests/test_training_event_attendance.js b/erpnext/hr/doctype/training_event/tests/test_training_event_attendance.js deleted file mode 100644 index 6364308f73..0000000000 --- a/erpnext/hr/doctype/training_event/tests/test_training_event_attendance.js +++ /dev/null @@ -1,40 +0,0 @@ -QUnit.module('hr'); - -QUnit.test("test: Training Event", function (assert) { - // number of asserts - assert.expect(1); - let done = assert.async(); - - frappe.run_serially([ - // insert a new Training Event - () => frappe.set_route("List", "Training Event", "List"), - () => frappe.new_doc("Training Event"), - () => frappe.timeout(1), - () => frappe.click_link('Edit in full page'), - () => cur_frm.set_value("event_name", "Test Event " + frappe.utils.get_random(10)), - () => cur_frm.set_value("start_time", "2017-07-26, 2:00 pm PDT"), - () => cur_frm.set_value("end_time", "2017-07-26, 2:30 pm PDT"), - () => cur_frm.set_value("introduction", "This is a test report"), - () => cur_frm.set_value("location", "Fake office"), - () => frappe.click_button('Add Row'), - () => frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name'), - (r) => { - console.log(r); - return cur_frm.fields_dict.employees.grid.grid_rows[0].doc.employee = r.message.name; - }, - () => { - return cur_frm.fields_dict.employees.grid.grid_rows[0].doc.attendance = "Optional"; - }, - () => frappe.click_button('Save'), - () => frappe.timeout(2), - () => frappe.click_button('Submit'), - () => frappe.timeout(2), - () => frappe.click_button('Yes'), - () => frappe.timeout(1), - () => { - assert.equal(cur_frm.doc.docstatus, 1); - }, - () => done() - ]); - -}); \ No newline at end of file diff --git a/erpnext/tests/ui/tests.txt b/erpnext/tests/ui/tests.txt index 38c138db0a..e7de60439a 100644 --- a/erpnext/tests/ui/tests.txt +++ b/erpnext/tests/ui/tests.txt @@ -72,7 +72,6 @@ erpnext/hr/doctype/appraisal/test_appraisal.js erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js erpnext/hr/doctype/expense_claim/test_expense_claim.js erpnext/hr/doctype/training_event/tests/test_training_event.js -erpnext/hr/doctype/training_event/tests/test_training_event_attendance.js erpnext/hr/doctype/training_result_employee/test_training_result.js erpnext/hr/doctype/training_feedback/test_training_feedback.js erpnext/hr/doctype/loan_type/test_loan_type.js From 1f99bea6ce80ace11d133f07fb61eaa67dad5c96 Mon Sep 17 00:00:00 2001 From: Neil Trini Lasrado Date: Wed, 25 Oct 2017 11:44:12 +0530 Subject: [PATCH 05/38] Pass sender as contact email in Opportunity (#11308) Always pass sender as the contact email while creating an opportunity for a website enquiry. --- erpnext/templates/utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/utils.py b/erpnext/templates/utils.py index 6ebe41185f..7ee39602f5 100644 --- a/erpnext/templates/utils.py +++ b/erpnext/templates/utils.py @@ -28,11 +28,12 @@ def send_message(subject="Website Query", message="", sender="", status="Open"): )).insert(ignore_permissions=True) opportunity = frappe.get_doc(dict( - doctype='Opportunity', + doctype ='Opportunity', enquiry_from = 'Customer' if customer else 'Lead', status = 'Open', title = subject, - to_discuss=message + contact_email = sender, + to_discuss = message )) if customer: From 7862eb444f716621cf50e3f0015744d2ad76f8aa Mon Sep 17 00:00:00 2001 From: Charles-Henri Decultot Date: Wed, 25 Oct 2017 08:14:46 +0200 Subject: [PATCH 06/38] [Hotfix] Addition of an empty tax group in the french CoA (#11306) --- .../chart_of_accounts/verified/fr_plan_comptable_general.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_general.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_general.json index f6015f3744..018d368de4 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_general.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/fr_plan_comptable_general.json @@ -851,7 +851,7 @@ "4457-Taxes sur le chiffre d'affaires collect\u00e9es par l'entreprise": { "44571-TVA collect\u00e9e": { "account_type": "Tax", - "tax_rate": 20.0 + "is_group": 1 }, "44578-Taxes assimil\u00e9es \u00e0 la TVA": {} }, From 311823aca1df4545fb4b8b1c4e07fb760a90abeb Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 25 Oct 2017 11:45:51 +0530 Subject: [PATCH 07/38] Validate stock exists against template item (#11305) --- erpnext/stock/doctype/item/item.js | 5 +++++ erpnext/stock/doctype/item/item.py | 12 ++++++++++++ erpnext/stock/doctype/item/test_item.py | 10 ++++++++++ 3 files changed, 27 insertions(+) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 03b93c0cb2..d8f1f30913 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -103,6 +103,11 @@ frappe.ui.form.on("Item", { frappe.set_route("Form", "Item Variant Settings"); }, __("View")); } + + if(frm.doc.__onload && frm.doc.__onload.stock_exists) { + // Hide variants section if stock exists + frm.toggle_display("variants_section", 0); + } }, validate: function(frm){ diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index a810665997..41a23a4d0f 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -17,6 +17,7 @@ from erpnext.controllers.item_variant import (get_variant, copy_attributes_to_va make_variant_item_code, validate_item_variant_attributes, ItemVariantExistsError) class DuplicateReorderRows(frappe.ValidationError): pass +class StockExistsForTemplate(frappe.ValidationError): pass class Item(WebsiteGenerator): website = frappe._dict( @@ -28,11 +29,15 @@ class Item(WebsiteGenerator): def onload(self): super(Item, self).onload() + self.set_onload('sle_exists', self.check_if_sle_exists()) if self.is_fixed_asset: asset = frappe.db.get_all("Asset", filters={"item_code": self.name, "docstatus": 1}, limit=1) self.set_onload("asset_exists", True if asset else False) + if frappe.db.get_value('Stock Ledger Entry', {'item_code': self.name}): + self.set_onload('stock_exists', True) + def autoname(self): if frappe.db.get_default("item_naming_by")=="Naming Series": if self.variant_of: @@ -85,6 +90,7 @@ class Item(WebsiteGenerator): self.synced_with_hub = 0 self.validate_has_variants() + self.validate_stock_exists_for_template_item() self.validate_attributes() self.validate_variant_attributes() self.validate_website_image() @@ -631,6 +637,12 @@ class Item(WebsiteGenerator): if frappe.db.exists("Item", {"variant_of": self.name}): frappe.throw(_("Item has variants.")) + def validate_stock_exists_for_template_item(self): + if self.has_variants and \ + frappe.db.get_value('Stock Ledger Entry', {'item_code': self.name}): + frappe.throw(_("As stock exists against an item {0}, you can not enable has variants property") + .format(self.name), StockExistsForTemplate) + def validate_uom(self): if not self.get("__islocal"): check_stock_uom_with_bin(self.name, self.stock_uom) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index c3f399a536..673d0f9f4a 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -8,6 +8,7 @@ import frappe from frappe.test_runner import make_test_records from erpnext.controllers.item_variant import (create_variant, ItemVariantExistsError, InvalidItemAttributeValueError, get_variant) +from erpnext.stock.doctype.item.item import StockExistsForTemplate from frappe.model.rename_doc import rename_doc from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry @@ -263,6 +264,15 @@ class TestItem(unittest.TestCase): self.assertEquals(variant.manufacturer, 'MSG1') self.assertEquals(variant.manufacturer_part_no, '007') + def test_stock_exists_against_template_item(self): + stock_item = frappe.get_all('Stock Ledger Entry', fields = ["item_code"], limit=1) + if stock_item: + item_code = stock_item[0].item_code + + item_doc = frappe.get_doc('Item', item_code) + item_doc.has_variants = 1 + self.assertRaises(StockExistsForTemplate, item_doc.save) + def set_item_variant_settings(fields): doc = frappe.get_doc('Item Variant Settings') doc.set('fields', fields) From 82c9352d5313714ef6b95faeb0eae5c89c85fb2d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 11:46:20 +0530 Subject: [PATCH 08/38] Update variant description based on attribute if not already there (#11302) --- erpnext/controllers/item_variant.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 8ca4f7096c..f40d519b92 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -201,14 +201,16 @@ def copy_attributes_to_variant(item, variant): variant.variant_of = item.name variant.has_variants = 0 if not variant.description: - variant.description = '' + variant.description = "" if item.variant_based_on=='Item Attribute': if variant.attributes: - if not variant.description: - variant.description += "\n" - for d in variant.attributes: - variant.description += "
" + d.attribute + ": " + cstr(d.attribute_value) + "
" + attributes_description = "" + for d in variant.attributes: + attributes_description += "
" + d.attribute + ": " + cstr(d.attribute_value) + "
" + + if attributes_description not in variant.description: + variant.description += attributes_description def make_variant_item_code(template_item_code, template_item_name, variant): """Uses template's item code and abbreviations to make variant's item code""" From 8a0943ead7c42b3aaac76d3d43d92b7648861276 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 11:46:35 +0530 Subject: [PATCH 09/38] Unlink reference doc on Journal Cancellation only if advance (#11285) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index face5ede25..790003c301 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -54,7 +54,7 @@ class JournalEntry(AccountsController): def update_advance_paid(self): advance_paid = frappe._dict() for d in self.get("accounts"): - if d.is_advance: + if d.is_advance == "Yes": if d.reference_type in ("Sales Order", "Purchase Order"): advance_paid.setdefault(d.reference_type, []).append(d.reference_name) @@ -76,7 +76,7 @@ class JournalEntry(AccountsController): def unlink_advance_entry_reference(self): for d in self.get("accounts"): - if d.is_advance and d.reference_type in ("Sales Invoice", "Purchase Invoice"): + if d.is_advance == "Yes" and d.reference_type in ("Sales Invoice", "Purchase Invoice"): doc = frappe.get_doc(d.reference_type, d.reference_name) doc.delink_advance_entries(self.name) d.reference_type = '' From f4283a3ebffc34eda5ad9fa3e74097634a259bb5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 11:46:47 +0530 Subject: [PATCH 10/38] Moved company field to the top in BOM (#11283) --- erpnext/manufacturing/doctype/bom/bom.json | 68 +++++++++++----------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index 2558df35ea..85a3f7e9b1 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -322,6 +322,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "currency_detail", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -359,8 +389,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "currency_detail", - "fieldtype": "Section Break", + "fieldname": "column_break_12", + "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -368,7 +398,6 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "", "length": 0, "no_copy": 0, "permlevel": 0, @@ -414,35 +443,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -1322,7 +1322,7 @@ "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 1, + "collapsible": 0, "columns": 0, "depends_on": "eval:!doc.__islocal", "fieldname": "section_break0", @@ -1671,7 +1671,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-08-23 14:09:30.492628", + "modified": "2017-10-23 14:56:21.991160", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", From 2672c331af0293a8c9cb098692bf8cb4cbba8f9a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 25 Oct 2017 11:47:18 +0530 Subject: [PATCH 11/38] Validation for duplicate offline pos (#11281) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 6ab614863b..1c4fe3d084 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -70,6 +70,7 @@ class SalesInvoice(SellingController): self.clear_unallocated_advances("Sales Invoice Advance", "advances") self.add_remarks() self.validate_write_off_account() + self.validate_duplicate_offline_pos_entry() self.validate_account_for_change_amount() self.validate_fixed_asset() self.set_income_account_for_fixed_assets() @@ -462,6 +463,12 @@ class SalesInvoice(SellingController): if flt(self.write_off_amount) and not self.write_off_account: msgprint(_("Please enter Write Off Account"), raise_exception=1) + def validate_duplicate_offline_pos_entry(self): + if self.is_pos and self.offline_pos_name \ + and frappe.db.get_value('Sales Invoice', + {'offline_pos_name': self.offline_pos_name, 'docstatus': 1}): + frappe.throw(_("Duplicate offline pos sales invoice {0}").format(self.offline_pos_name)) + def validate_account_for_change_amount(self): if flt(self.change_amount) and not self.account_for_change_amount: msgprint(_("Please enter Account for Change Amount"), raise_exception=1) From 47caf51efe05afc39aac059ba83bd28e69c3cd79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Narciso=20E=2E=20N=C3=BA=C3=B1ez=20Arias?= Date: Tue, 24 Oct 2017 22:23:10 -0800 Subject: [PATCH 12/38] [docs] Translate the Projects section of the Manual to Spanish (#11319) * [docs] Translate projects module. Add index page * [docs] Translate activity cost page from projects module. * [docs] Translate activity type page from projects module. * [docs] Translate the Project Costing article in project module * [docs] Translate the Project page in projects module * [docs] Translate the Task page in projects module * [docs] Translate the Time Log Batch page in projects module --- erpnext/docs/user/manual/es/index.txt | 1 + .../docs/user/manual/es/projects/__init__.py | 0 .../user/manual/es/projects/activity-cost.md | 6 + .../user/manual/es/projects/activity-type.md | 15 +++ .../manual/es/projects/articles/__init__.py | 0 .../user/manual/es/projects/articles/index.md | 3 + .../manual/es/projects/articles/index.txt | 1 + .../es/projects/articles/project-costing.md | 40 +++++++ erpnext/docs/user/manual/es/projects/index.md | 15 +++ .../docs/user/manual/es/projects/index.txt | 7 ++ .../docs/user/manual/es/projects/project.md | 110 ++++++++++++++++++ erpnext/docs/user/manual/es/projects/tasks.md | 61 ++++++++++ .../user/manual/es/projects/time-log-batch.md | 25 ++++ 13 files changed, 284 insertions(+) create mode 100644 erpnext/docs/user/manual/es/projects/__init__.py create mode 100644 erpnext/docs/user/manual/es/projects/activity-cost.md create mode 100644 erpnext/docs/user/manual/es/projects/activity-type.md create mode 100644 erpnext/docs/user/manual/es/projects/articles/__init__.py create mode 100644 erpnext/docs/user/manual/es/projects/articles/index.md create mode 100644 erpnext/docs/user/manual/es/projects/articles/index.txt create mode 100644 erpnext/docs/user/manual/es/projects/articles/project-costing.md create mode 100644 erpnext/docs/user/manual/es/projects/index.md create mode 100644 erpnext/docs/user/manual/es/projects/index.txt create mode 100644 erpnext/docs/user/manual/es/projects/project.md create mode 100644 erpnext/docs/user/manual/es/projects/tasks.md create mode 100644 erpnext/docs/user/manual/es/projects/time-log-batch.md diff --git a/erpnext/docs/user/manual/es/index.txt b/erpnext/docs/user/manual/es/index.txt index feb68a2fe0..00cf97b420 100644 --- a/erpnext/docs/user/manual/es/index.txt +++ b/erpnext/docs/user/manual/es/index.txt @@ -1,3 +1,4 @@ introduction accounts +projects schools diff --git a/erpnext/docs/user/manual/es/projects/__init__.py b/erpnext/docs/user/manual/es/projects/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/docs/user/manual/es/projects/activity-cost.md b/erpnext/docs/user/manual/es/projects/activity-cost.md new file mode 100644 index 0000000000..71eb15e432 --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/activity-cost.md @@ -0,0 +1,6 @@ +# Costo de Actividad + +El costo de la actividad registra la tasa de facturación por hora y la tasa de costos de un empleado en comparación con un tipo de actividad. +El sistema hace uso de esta tasa mientras hace registros de tiempo. Se usa para Costeo de proyectos. + +Activity Cost diff --git a/erpnext/docs/user/manual/es/projects/activity-type.md b/erpnext/docs/user/manual/es/projects/activity-type.md new file mode 100644 index 0000000000..20a03e2932 --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/activity-type.md @@ -0,0 +1,15 @@ +# Tipo de Actividad + +Los tipos de actividad son la lista de los diferentes tipos de actividades sobre las que se hacen registro de tiempo. + +Activity Type + +Por defecto, los siguientes tipos de actividades son creados. + +* Planning +* Research +* Proposal Writing +* Execution +* Communication + +{next} \ No newline at end of file diff --git a/erpnext/docs/user/manual/es/projects/articles/__init__.py b/erpnext/docs/user/manual/es/projects/articles/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/docs/user/manual/es/projects/articles/index.md b/erpnext/docs/user/manual/es/projects/articles/index.md new file mode 100644 index 0000000000..2d959ecb6e --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/articles/index.md @@ -0,0 +1,3 @@ +# Artículos + +{index} \ No newline at end of file diff --git a/erpnext/docs/user/manual/es/projects/articles/index.txt b/erpnext/docs/user/manual/es/projects/articles/index.txt new file mode 100644 index 0000000000..56c193c277 --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/articles/index.txt @@ -0,0 +1 @@ +project-costing \ No newline at end of file diff --git a/erpnext/docs/user/manual/es/projects/articles/project-costing.md b/erpnext/docs/user/manual/es/projects/articles/project-costing.md new file mode 100644 index 0000000000..a8820c7e29 --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/articles/project-costing.md @@ -0,0 +1,40 @@ +# Costeo de proyectos + +Cada proyecto tiene multiples tareas asociadas a el. Para hacer el seguimiento del costo actual de un proyecto, primeramente en términos de servicios, el usuario +tiene que crear un registro de tiempo basado en el tiempo que invirtió en una tarea del proyecto. Siguiendo los pasos de como puedes hacer el seguimiento del costo actual de un servicio usando el proyecto. + +#### Tipo de actividad + +Tipo de actividad es un maestro de los servicios ofrecidos por su personal. Puedes agregar un nuevo Tipo de Actividad desde: + +`Project > Activity Type > New` + +#### Costo de actividad + +Costo de actividad es un maestro donde puedes hacer el seguimiento de los montos de facturación y costo de cada empleado, y por cada tipo de Tipo de Actividad. + +Activity Cost + +#### Registro de Tiempo + +Basados en el tiempo actual invertido en una Tarea del Proyecto, El empleado va a crear un registro de tiempo. + +Time Log + +Al momento de seleccionar el Tipo de Actividad en el Registro de tiempo, el monto de Facturación y Costo del empleado va a ser traído de su respectivo registro en el master de Costo de Actividad. + +Time Log Costing + +Multiplicando esos montos con el total de número de horas en el registro de tiempo, nos da el monto de costos y Facturación para el registro de tiempo específico. + +#### Costeo en Proyectos y Tareas + +Basados en el total de registros de tiempos creados por una tarea en específico, su costo va a ser actualizado en el registro maestro de la tarea, o sea, en el detalle de la tarea. + +Costing in Task + +De la misma manera, el detalle del Proyecto va a actualizar su costo basado en el total de registros de tiempo a ese proyecto, y las tareas asociadas a ese proyecto. + +Costing in Project + + \ No newline at end of file diff --git a/erpnext/docs/user/manual/es/projects/index.md b/erpnext/docs/user/manual/es/projects/index.md new file mode 100644 index 0000000000..0882752a40 --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/index.md @@ -0,0 +1,15 @@ +# Proyectos + +ERPNext le ayuda en la administración de su proyecto a traves de la creacion de tareas y +poder asignarlas a diferentes personas. + +Las compras y las ventas también se pueden rastrear en relación con los proyectos y +esto puede ayudar a la empresa a controlar su presupuesto, entrega y rentabilidad para un proyecto. + +Los proyectos pueden ser usados para manejar los proyectos internos, trabajos de manufacturación y +planificación de servicios. Para los trabajos de servicios, los Time Sheets (hojas de tiempo) pueden ser creadas +para facturar a los clientes, en caso que el proceso de facturación se haga basado en tiempo y dinero de tareas. + +### Temas + +{index} \ No newline at end of file diff --git a/erpnext/docs/user/manual/es/projects/index.txt b/erpnext/docs/user/manual/es/projects/index.txt new file mode 100644 index 0000000000..716ec1fe3a --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/index.txt @@ -0,0 +1,7 @@ +tasks +project +time-log-batch +activity-type +activity-cost +articles +timesheet \ No newline at end of file diff --git a/erpnext/docs/user/manual/es/projects/project.md b/erpnext/docs/user/manual/es/projects/project.md new file mode 100644 index 0000000000..942433b47b --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/project.md @@ -0,0 +1,110 @@ +# Proyecto + +El manejo de proyectos en ERPNext se hace a traves de tareas. Puedes crear un proyecto y asignar varias tareas al mismo. + +Project + +También puedes hacer el seguimiento del % completado del proyecto usando diferentes métodos. + + 1. Tareas Completadas + 2. Progreso de tareas + 3. Peso de tarea + +Project + +Algunos ejemplos de como el % completado es cálculado basado en tareas. + +Project + +Project + +### Manejando tareas + +Los proyecto pueden ser divididos en multiples tareas. +Las tareas pueden ser creadas a traves del documento de Proyecto o pueden ser creadas via [Tarea](/docs/user/manual/en/projects/tasks.html) + +Project + +* Para ver las tareas creadas a un proyecto click en 'Tasks' + +Project - View Task + +Project - Task List + +* También puedes ver las tareas desde la misma vista del proyecto. + +Project - Task Grid + +* Para agregar peso a las tareas puedes seguir los pasos siguientes + +Project - Task Grid +Project - Task Grid + + +### Manejando tiempo + +ERPNext usa [Time Log](/docs/user/manual/en/projects/time-log.html) para hacer el seguimiento del progreso de un Proyecto. +Puedes crear registros de tiempo sobre cada Tarea. +El tiempo actual de inicio y finalización junto con el costo deben ser actualizados basados en los Registros de Tiempo. + +* Para ver los Registros de Tiempo realizados a un proyecto, dar click en 'Time Logs' + +Project - View Time Log + +Project - Time Log List + +* Puedes agregar un registro de tiempo directamente y luego asociarlo con el proyecto. + +Project - Link Time Log + +### Gestión de gastos + +Puede reservar la [Reclamación de gastos](/docs/user/manual/en/human-resources/expense-claim.html) contra una tarea de proyecto. +El sistema actualizará el monto total de las reclamaciones de gastos en la sección de costos del proyecto. + +* Para ver las reclamaciones de gastos realizadas en un proyecto, haga clic en 'Reclamaciones de gastos' + +Project - View Expense Claim + +* También puede crear un Reclamo de gastos directamente y vincularlo al Proyecto. + +Project - Link Expense Claim + +* El monto total de los Reclamos de gastos reservados contra un proyecto se muestra en 'Reclamo de gastos totales' en la Sección de Costos del proyecto + +Project - Total Expense Claim + +### Centro de Costo + +Puedes crear un [Cost Center](/docs/user/manual/en/accounts/setup/cost-center.html) sobre un proyecto o usar un centro de costo existente para hacer el seguimiento de todos los gastos realizados al proyecto. + +Project - Cost Center + +###Costeo del proyecto + +La sección Costeo del proyecto le ayuda a rastrear el tiempo y los gastos incurridos en relación con el proyecto. + +Project - Costing + +* La sección de cálculo de costos se actualiza según los registros de tiempo realizados. + +* El margen bruto es la diferencia entre el monto total de costos y el monto total de facturación + +###Facturación + +Puedes crear/enlazar una [Sales Order](/docs/user/manual/en/selling/sales-order.html) a un proyecto. Una vez asociada puedes usar el módulo de ventas para facturar a un cliente sobre el proyecto. + +Project - Sales Order + +###Gantt Chart + +Un Gantt Chart muestra la planificación del proyecto. +ERPNext te provee con una vista para visualizar las tareas de forma calendarizada usando un Gantt Chart (Hoja de Gantt). + +* Para visualizar el gantt chart de un proyecto, ve hasta el proyecto y dar click en 'Gantt Chart' + +Project - View Gantt Chart + +Project - Gantt Chart + +{next} diff --git a/erpnext/docs/user/manual/es/projects/tasks.md b/erpnext/docs/user/manual/es/projects/tasks.md new file mode 100644 index 0000000000..b07b305b7a --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/tasks.md @@ -0,0 +1,61 @@ +# Tareas + +Proyecto es dividido en Tareas. +En ERPNext, puedes crear las tareas de forma independiente. + +Task + +### Estado de una Tarea + +Una tarea puede tener uno de los siguientes estados - Abierto, Trabajando, Pendiente de Revisión, Cerrado, o Cancelado. + +Task - Status + +* Por defecto, cada nueva tarea creada se le establece el estado 'Abierto'. + +* Si un registro de tiempo es realizado sobre una tarea, su estado es asignado a 'Working'. + +### Tarea Dependiente + +Puedes especificar una lista de tareas dependientes en la sección 'Depende de' + +Depends On + +* No puedes cerrar una tarea padre hasta que todas las tareas dependientes esten cerradas. + +* Si una tarea dependiente se encuentra en retraso y se sobrepone con la fecha esperada de inicio de la tarea padre, el sistema va a re calandarizar la tarea padre. + +### Manejando el tiempo + +ERPNext usa [Time Log](/docs/user/manual/en/projects/time-log.html) para seguir el progreso de una tarea. +Puedes crear varios registros de tiempo para cada tarea. +El tiempo de inicio y fin actual junto con el costo es actualizado en base al Registro de Tiempo. + +* Para ver el Registro de tiempo realizado a una tarea, dar click en 'Time Logs' + +Task - View Time Log + +Task - Time Log List + +* Puedes también crear un Registro de Tiempo directamente y luego asociarlo a una Tarea. + +Task - Link Time Log + +### Gestión de gastos + +Puede reservar la [Reclamación de gastos](/docs/user/manual/en/human-resources/expense-claim.html) contra una tarea de proyecto. +El sistema actualizará el monto total de las reclamaciones de gastos en la sección de costos del proyecto. + +* Para ver las reclamaciones de gastos realizadas en un proyecto, haga clic en 'Reclamaciones de gastos' + +Task - View Expense Claim + +* También puede crear un Reclamo de gastos directamente y vincularlo al Proyecto. + +Task - Link Expense Claim + +* El monto total de los Reclamos de gastos reservados contra un proyecto se muestra en 'Reclamo de gastos totales' en la Sección de Costos del proyecto + +Task - Total Expense Claim + +{next} diff --git a/erpnext/docs/user/manual/es/projects/time-log-batch.md b/erpnext/docs/user/manual/es/projects/time-log-batch.md new file mode 100644 index 0000000000..72c77b4ff8 --- /dev/null +++ b/erpnext/docs/user/manual/es/projects/time-log-batch.md @@ -0,0 +1,25 @@ +# Lote de registro de tiempo + +Puede facturar Registros de tiempo viéndolos juntos. Esto le da la flexibilidad de administrar la facturación de su cliente de la manera que desee. Para crear una nueva hoja de tiempo, ve a + +> Projects > Time Sheet > New Time Sheet + +O + +Simplemente abra su lista de registro de tiempo y marque los elementos que desea agregar al registro de tiempo. A continuación, haga clic en el botón "Crear hoja de tiempo" y se seleccionarán estos registros de tiempo. + +Time Log - Drag Calender + +###Creando Factura de Venta + +* Despues de crear la Hoja de Tiempo/Horario, el botón "Crear Factura" debe aparecer. + +Time Log - Drag Calender + +* Haga clic en ese botón para hacer una factura de venta usando la hoja de tiempo. + +Time Log - Drag Calender + +* Cuando "Presente" la Factura de Ventas, el número de Factura de Ventas se actualizará en los Registros de Tiempo y la Hoja de Horario y su estado cambiará a "Facturado". + +{next} \ No newline at end of file From 07ab4622e89295427925bb4de1c97603f1a78a44 Mon Sep 17 00:00:00 2001 From: Pawan Mehta Date: Wed, 25 Oct 2017 11:55:49 +0530 Subject: [PATCH 13/38] [fix] #9824 - Calculate costing amount even if billable is unchecked (#11310) * [fix] #9824 * fix code --- erpnext/projects/doctype/timesheet/timesheet.js | 4 +--- erpnext/projects/doctype/timesheet/timesheet.py | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 1ea5962911..43f5705e82 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -191,7 +191,6 @@ var update_time_rates = function(frm, cdt, cdn){ var child = locals[cdt][cdn]; if(!child.billable){ frappe.model.set_value(cdt, cdn, 'billing_rate', 0.0); - frappe.model.set_value(cdt, cdn, 'costing_rate', 0.0); } } @@ -202,9 +201,8 @@ var calculate_billing_costing_amount = function(frm, cdt, cdn){ if(child.billing_hours && child.billable){ billing_amount = (child.billing_hours * child.billing_rate); - costing_amount = flt(child.costing_rate * child.billing_hours); } - + costing_amount = flt(child.costing_rate * child.hours); frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount); frappe.model.set_value(cdt, cdn, 'costing_amount', costing_amount); calculate_time_and_amount(frm); diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index ad566d5ac1..01552a50c9 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -49,10 +49,10 @@ class Timesheet(Document): self.update_time_rates(d) self.total_hours += flt(d.hours) + self.total_costing_amount += flt(d.costing_amount) if d.billable: self.total_billable_hours += flt(d.billing_hours) self.total_billable_amount += flt(d.billing_amount) - self.total_costing_amount += flt(d.costing_amount) self.total_billed_amount += flt(d.billing_amount) if d.sales_invoice else 0.0 self.total_billed_hours += flt(d.billing_hours) if d.sales_invoice else 0.0 @@ -265,19 +265,19 @@ class Timesheet(Document): def update_cost(self): for data in self.time_logs: - if data.activity_type and data.billable: + if data.activity_type or data.billable: rate = get_activity_cost(self.employee, data.activity_type) hours = data.billing_hours or 0 + costing_hours = data.billing_hours or data.hours or 0 if rate: data.billing_rate = flt(rate.get('billing_rate')) if flt(data.billing_rate) == 0 else data.billing_rate data.costing_rate = flt(rate.get('costing_rate')) if flt(data.costing_rate) == 0 else data.costing_rate data.billing_amount = data.billing_rate * hours - data.costing_amount = data.costing_rate * hours + data.costing_amount = data.costing_rate * costing_hours def update_time_rates(self, ts_detail): if not ts_detail.billable: ts_detail.billing_rate = 0.0 - ts_detail.costing_rate = 0.0 @frappe.whitelist() def get_projectwise_timesheet_data(project, parent=None): From cba7a11d75e7d9159108cf651a7560a6d337cfb9 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Wed, 25 Oct 2017 07:27:10 +0100 Subject: [PATCH 14/38] Make Variant does not copy UOM properties (#10887) (#11261) * add `uoms` to Item Variant Settings if not already added * add new patch to patch list * change uoms field to no_copy:0 --- erpnext/patches.txt | 1 + erpnext/patches/v9_0/set_uoms_in_variant_field.py | 14 ++++++++++++++ erpnext/stock/doctype/item/item.json | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v9_0/set_uoms_in_variant_field.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e7c0614b7a..8a4d3a5b97 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -451,3 +451,4 @@ erpnext.patches.v8_9.update_billing_gstin_for_indian_account erpnext.patches.v9_0.fix_subscription_next_date erpnext.patches.v9_0.add_healthcare_domain erpnext.patches.v9_0.set_variant_item_description +erpnext.patches.v9_0.set_uoms_in_variant_field diff --git a/erpnext/patches/v9_0/set_uoms_in_variant_field.py b/erpnext/patches/v9_0/set_uoms_in_variant_field.py new file mode 100644 index 0000000000..9e783d99be --- /dev/null +++ b/erpnext/patches/v9_0/set_uoms_in_variant_field.py @@ -0,0 +1,14 @@ +from __future__ import unicode_literals +import frappe + + +def execute(): + doc = frappe.get_doc('Item Variant Settings') + variant_field_names = [vf.field_name for vf in doc.fields] + if 'uoms' not in variant_field_names: + doc.append( + 'fields', { + 'field_name': 'uoms' + } + ) + doc.save() diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 525321c5a9..105cdf5c57 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -1072,7 +1072,7 @@ "in_standard_filter": 0, "label": "UOMs", "length": 0, - "no_copy": 1, + "no_copy": 0, "oldfieldname": "uom_conversion_details", "oldfieldtype": "Table", "options": "UOM Conversion Detail", From b7483f6dfd30b21d28ff12d35f95679fe7ff629d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 11:58:02 +0530 Subject: [PATCH 15/38] Update item.json --- erpnext/stock/doctype/item/item.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 105cdf5c57..fd6c0beca2 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -3205,7 +3205,7 @@ "issingle": 0, "istable": 0, "max_attachments": 1, - "modified": "2017-09-27 14:08:02.948326", + "modified": "2017-10-25 14:08:02.948326", "modified_by": "Administrator", "module": "Stock", "name": "Item", @@ -3382,4 +3382,4 @@ "title_field": "item_name", "track_changes": 1, "track_seen": 0 -} \ No newline at end of file +} From 1c1237537baca967b978f51026390af8bbfed372 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 12:10:44 +0530 Subject: [PATCH 16/38] Subscription fixes (#11292) * Subscription fixes * Removed recurring fields, cleanup code --- .../purchase_invoice/purchase_invoice.json | 369 +------------- .../doctype/sales_invoice/sales_invoice.json | 456 ++---------------- .../doctype/subscription/subscription.js | 3 +- .../doctype/subscription/subscription.json | 76 +-- .../doctype/subscription/subscription.py | 100 ++-- .../purchase_order/purchase_order.json | 454 ++--------------- erpnext/patches.txt | 2 +- .../make_subscription_from_recurring_data.py | 33 +- .../v9_0/fix_subscription_next_date.py | 46 +- .../doctype/sales_order/sales_order.json | 455 ++--------------- .../doctype/sales_order/sales_order.py | 8 +- 11 files changed, 242 insertions(+), 1760 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index e3c5fb4942..3d70b3b0b9 100755 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -3440,139 +3440,13 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription", - "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": "Subscription", - "length": 0, - "no_copy": 1, - "options": "Subscription", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "is_recurring", - "columns": 0, - "depends_on": "eval:doc.docstatus<2 && !doc.__islocal", - "fieldname": "recurring_invoice", - "fieldtype": "Section Break", - "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": "Recurring Invoice", - "length": 0, - "no_copy": 0, - "options": "fa fa-time", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:doc.docstatus<2", - "description": "", - "fieldname": "is_recurring", - "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": "Is Recurring", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring==1", - "description": "Select the period when the invoice will be generated automatically", - "fieldname": "recurring_type", - "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": "Recurring Type", - "length": 0, - "no_copy": 1, - "options": "Monthly\nQuarterly\nHalf-yearly\nYearly", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring==1", + "depends_on": "", "description": "Start date of current invoice's period", "fieldname": "from_date", "fieldtype": "Date", @@ -3603,7 +3477,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:doc.is_recurring==1", + "depends_on": "", "description": "End date of current invoice's period", "fieldname": "to_date", "fieldtype": "Date", @@ -3628,138 +3502,13 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "fieldname": "submit_on_creation", - "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": "Submit on creation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "notify_by_email", - "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": "Notify by email", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring==1", - "description": "The day of the month on which auto invoice will be generated e.g. 05, 28 etc", - "fieldname": "repeat_on_day_of_month", - "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": "Repeat on Day of Month", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring==1", - "description": "The date on which recurring invoice will be stop", - "fieldname": "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": "End Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "column_break_82", + "fieldname": "column_break_114", "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, @@ -3771,101 +3520,8 @@ "length": 0, "no_copy": 0, "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring==1", - "description": "The date on which next invoice will be generated. It is generated on submit.", - "fieldname": "next_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": "Next Date", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring==1", - "description": "The unique id for tracking all recurring invoices. It is generated on submit.", - "fieldname": "recurring_id", - "fieldtype": "Data", - "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": "Recurring Id", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring==1", - "description": "Enter Email Address separated by commas, invoice will be mailed automatically on particular date", - "fieldname": "notification_email_address", - "fieldtype": "Small Text", - "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": "Notification Email Address", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, + "precision": "", + "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, "remember_last_selected_value": 0, @@ -3881,8 +3537,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:doc.is_recurring==1", - "fieldname": "recurring_print_format", + "fieldname": "subscription", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -3891,15 +3546,15 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Recurring Print Format", + "label": "Subscription", "length": 0, - "no_copy": 0, - "options": "Print Format", + "no_copy": 1, + "options": "Subscription", "permlevel": 0, "precision": "", - "print_hide": 0, + "print_hide": 1, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -3920,7 +3575,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-09-19 11:22:47.074420", + "modified": "2017-10-24 12:51:51.199594", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 9aa0e6b6a2..ecd7cf9069 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -4273,414 +4273,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "subscription", - "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": "Subscription", - "length": 0, - "no_copy": 1, - "options": "Subscription", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "is_recurring", - "columns": 0, - "depends_on": "eval:doc.docstatus<2 && !doc.__islocal", - "fieldname": "recurring_invoice", - "fieldtype": "Section Break", - "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": "Recurring Invoice", - "length": 0, - "no_copy": 0, - "options": "fa fa-time", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break11", - "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, - "label": "Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus<2", - "description": "", - "fieldname": "is_recurring", - "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": "Is Recurring", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_recurring", - "description": "", - "fieldname": "recurring_id", - "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": "Reference Document", - "length": 0, - "no_copy": 1, - "options": "Sales Invoice", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "recurring_type", - "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": "Frequency", - "length": 0, - "no_copy": 1, - "options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "repeat_on_day_of_month", - "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": "Repeat on Day of Month", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "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": "End Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "fieldname": "submit_on_creation", - "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": "Submit on creation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "notify_by_email", - "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": "Notify by email", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name", - "description": "", - "fieldname": "notification_email_address", - "fieldtype": "Code", - "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": "Notification Email Address", - "length": 0, - "no_copy": 1, - "options": "Email", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name", - "fieldname": "recurring_print_format", - "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": "Recurring Print Format", - "length": 0, - "no_copy": 0, - "options": "Print Format", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break12", - "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, - "label": "This Document", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, - "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_recurring", + "depends_on": "", "description": "", "fieldname": "from_date", "fieldtype": "Date", @@ -4711,7 +4304,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "is_recurring", + "depends_on": "", "description": "", "fieldname": "to_date", "fieldtype": "Date", @@ -4742,10 +4335,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "is_recurring", - "description": "", - "fieldname": "next_date", - "fieldtype": "Date", + "fieldname": "column_break_140", + "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -4753,11 +4344,11 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Next Date", "length": 0, - "no_copy": 1, + "no_copy": 0, "permlevel": 0, - "print_hide": 1, + "precision": "", + "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, "remember_last_selected_value": 0, @@ -4767,6 +4358,37 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_on_submit": 0, @@ -4811,7 +4433,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2017-09-19 11:23:08.675028", + "modified": "2017-10-24 12:46:48.331723", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index 15927d5961..8db5be8772 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -12,7 +12,8 @@ frappe.ui.form.on('Subscription', { frm.fields_dict['reference_document'].get_query = function() { return { filters: { - "docstatus": 1 + "docstatus": 1, + "subscription": '' } }; }; diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json index 167a92f502..7ff2e4b632 100644 --- a/erpnext/accounts/doctype/subscription/subscription.json +++ b/erpnext/accounts/doctype/subscription/subscription.json @@ -315,22 +315,23 @@ }, { "allow_bulk_edit": 0, - "allow_on_submit": 1, + "allow_on_submit": 0, "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "from_date", - "fieldtype": "Date", + "fieldname": "frequency", + "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_standard_filter": 0, - "label": "From Date", + "label": "Frequency", "length": 0, "no_copy": 0, + "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf-yearly\nYearly", "permlevel": 0, "precision": "", "print_hide": 0, @@ -338,37 +339,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_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": "To 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, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -402,37 +373,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "frequency", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Frequency", - "length": 0, - "no_copy": 0, - "options": "\nDaily\nWeekly\nMonthly\nQuarterly\nHalf-yearly\nYearly", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 1, @@ -844,7 +784,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-10 17:28:10.105561", + "modified": "2017-10-23 18:28:08.966403", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 20cf031503..fb01efe13b 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -17,6 +17,7 @@ month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12} class Subscription(Document): def validate(self): self.update_status() + self.validate_reference_doctype() self.validate_dates() self.validate_next_schedule_date() self.validate_email_id() @@ -25,25 +26,28 @@ class Subscription(Document): validate_template(self.message or "") def before_submit(self): - self.set_next_schedule_date() + if not self.next_schedule_date: + self.next_schedule_date = get_next_schedule_date(self.start_date, + self.frequency, self.repeat_on_day) def on_submit(self): - # self.update_subscription_id() - self.update_subscription_data() + self.update_subscription_id() def on_update_after_submit(self): - self.update_subscription_data() self.validate_dates() self.set_next_schedule_date() def before_cancel(self): self.unlink_subscription_id() + self.next_schedule_date = None def unlink_subscription_id(self): - doc = frappe.get_doc(self.reference_doctype, self.reference_document) - if doc.meta.get_field('subscription'): - doc.subscription = None - doc.db_update() + frappe.db.sql("update `tab{0}` set subscription = null where subscription=%s" + .format(self.reference_doctype), self.name) + + def validate_reference_doctype(self): + if not frappe.get_meta(self.reference_doctype).has_field('subscription'): + frappe.throw(_("Add custom field Subscription in the doctype {0}").format(self.reference_doctype)) def validate_dates(self): if self.end_date and getdate(self.start_date) > getdate(self.end_date): @@ -76,30 +80,11 @@ class Subscription(Document): frappe.throw(_("'Recipients' not specified")) def set_next_schedule_date(self): - self.next_schedule_date = get_next_schedule_date(self.start_date, - self.frequency, self.repeat_on_day) - - def update_subscription_data(self): - update_doc = False - doc = frappe.get_doc(self.reference_doctype, self.reference_document) - if frappe.get_meta(self.reference_doctype).get_field("from_date"): - doc.from_date = self.from_date - doc.to_date = self.to_date - update_doc = True - - if not doc.subscription: - doc.subscription = self.name - update_doc = True - - if update_doc: - doc.db_update() + if self.repeat_on_day: + self.next_schedule_date = get_next_date(self.next_schedule_date, 0, self.repeat_on_day) def update_subscription_id(self): - doc = frappe.get_doc(self.reference_doctype, self.reference_document) - if not doc.meta.get_field('subscription'): - frappe.throw(_("Add custom field Subscription Id in the doctype {0}").format(self.reference_doctype)) - - doc.db_set('subscription', self.name) + frappe.db.set_value(self.reference_doctype, self.reference_document, "subscription", self.name) def update_status(self, status=None): self.status = { @@ -142,9 +127,6 @@ def get_subscription_entries(date): def create_documents(data, schedule_date): try: doc = make_new_document(data, schedule_date) - if getattr(doc, "from_date", None): - update_subscription_period(data, doc) - if data.notify_by_email and data.recipients: print_format = data.print_format or "Standard" send_notification(doc, data, print_format=print_format) @@ -159,13 +141,6 @@ def create_documents(data, schedule_date): if data.reference_document and not frappe.flags.in_test: notify_error_to_user(data) -def update_subscription_period(data, doc): - from_date = doc.from_date - to_date = doc.to_date - - frappe.db.set_value('Subscription', data.name, 'from_date', from_date) - frappe.db.set_value('Subscription', data.name, 'to_date', to_date) - def disable_subscription(data): subscription = frappe.get_doc('Subscription', data.name) subscription.db_set('disabled', 1) @@ -216,24 +191,38 @@ def update_doc(new_document, reference_doc, args, schedule_date): for fieldname in ("page_break",): item.set(fieldname, reference_doc.items[i].get(fieldname)) - if args.from_date and args.to_date: - from_date = get_next_date(args.from_date, mcount) - - if (cstr(get_first_day(args.from_date)) == cstr(args.from_date)) and \ - (cstr(get_last_day(args.to_date)) == cstr(args.to_date)): - to_date = get_last_day(get_next_date(args.to_date, mcount)) - else: - to_date = get_next_date(args.to_date, mcount) - - if new_document.meta.get_field('from_date'): - new_document.set('from_date', from_date) - new_document.set('to_date', to_date) - - new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args) for data in new_document.meta.fields: if data.fieldtype == 'Date' and data.reqd: new_document.set(data.fieldname, schedule_date) + set_subscription_period(args, mcount, new_document) + + new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args) + +def set_subscription_period(args, mcount, new_document): + if new_document.meta.get_field('from_date') and new_document.meta.get_field('to_date'): + last_ref_doc = frappe.db.sql(""" + select name, from_date, to_date + from `tab{0}` + where subscription=%s and docstatus < 2 + order by creation desc + limit 1 + """.format(args.reference_doctype), args.name, as_dict=1) + + if not last_ref_doc: + return + + from_date = get_next_date(last_ref_doc[0].from_date, mcount) + + if (cstr(get_first_day(last_ref_doc[0].from_date)) == cstr(last_ref_doc[0].from_date)) and \ + (cstr(get_last_day(last_ref_doc[0].to_date)) == cstr(last_ref_doc[0].to_date)): + to_date = get_last_day(get_next_date(last_ref_doc[0].to_date, mcount)) + else: + to_date = get_next_date(last_ref_doc[0].to_date, mcount) + + new_document.set('from_date', from_date) + new_document.set('to_date', to_date) + def get_next_date(dt, mcount, day=None): dt = getdate(dt) dt += relativedelta(months=mcount, day=day) @@ -287,8 +276,11 @@ def assign_task_to_owner(name, msg, users): @frappe.whitelist() def make_subscription(doctype, docname): doc = frappe.new_doc('Subscription') + + reference_doc = frappe.get_doc(doctype, docname) doc.reference_doctype = doctype doc.reference_document = docname + doc.start_date = reference_doc.get('posting_date') or reference_doc.get('transaction_date') return doc @frappe.whitelist() diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 07a80b87bd..5ee750e9d8 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -2917,418 +2917,13 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription", - "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": "Subscription", - "length": 0, - "no_copy": 1, - "options": "Subscription", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "is_recurring", - "columns": 0, - "depends_on": "eval:doc.docstatus<2 && !doc.__islocal", - "fieldname": "recurring_order", - "fieldtype": "Section Break", - "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": "Recurring Order", - "length": 0, - "no_copy": 0, - "options": "fa fa-time", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break", - "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, - "label": "Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:doc.docstatus<2", - "description": "", - "fieldname": "is_recurring", - "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": "Is Recurring", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_recurring", - "description": "", - "fieldname": "recurring_id", - "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": "Reference Document", - "length": 0, - "no_copy": 1, - "options": "Purchase Order", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "recurring_type", - "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": "Frequency", - "length": 0, - "no_copy": 1, - "options": "Monthly\nQuarterly\nHalf-yearly\nYearly", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "repeat_on_day_of_month", - "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": "Repeat on Day of Month", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "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": "End Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "fieldname": "submit_on_creation", - "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": "Submit on creation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "notify_by_email", - "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": "Notify by email", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name", - "description": "", - "fieldname": "notification_email_address", - "fieldtype": "Code", - "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": "Notification Email Address", - "length": 0, - "no_copy": 1, - "options": "Email", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name", - "fieldname": "recurring_print_format", - "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": "Recurring Print Format", - "length": 0, - "no_copy": 0, - "options": "Print Format", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break83", - "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, - "label": "This Document", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_recurring", + "depends_on": "", "description": "", "fieldname": "from_date", "fieldtype": "Date", @@ -3359,7 +2954,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "is_recurring", + "depends_on": "", "description": "", "fieldname": "to_date", "fieldtype": "Date", @@ -3390,10 +2985,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "is_recurring", - "description": "", - "fieldname": "next_date", - "fieldtype": "Date", + "fieldname": "column_break_97", + "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -3401,11 +2994,11 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Next Date", "length": 0, - "no_copy": 1, + "no_copy": 0, "permlevel": 0, - "print_hide": 1, + "precision": "", + "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, "remember_last_selected_value": 0, @@ -3414,6 +3007,37 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 1, + "options": "Subscription", + "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, + "unique": 0 } ], "has_web_view": 0, @@ -3427,7 +3051,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-09-22 16:11:49.856808", + "modified": "2017-10-24 12:52:11.272306", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 8a4d3a5b97..f6289f59ce 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -448,7 +448,7 @@ erpnext.patches.v8_9.remove_employee_from_salary_structure_parent erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts erpnext.patches.v8_9.set_default_fields_in_variant_settings erpnext.patches.v8_9.update_billing_gstin_for_indian_account -erpnext.patches.v9_0.fix_subscription_next_date +erpnext.patches.v9_0.fix_subscription_next_date #2017-10-23 erpnext.patches.v9_0.add_healthcare_domain erpnext.patches.v9_0.set_variant_item_description erpnext.patches.v9_0.set_uoms_in_variant_field diff --git a/erpnext/patches/v8_7/make_subscription_from_recurring_data.py b/erpnext/patches/v8_7/make_subscription_from_recurring_data.py index c5d7d7279a..2932749116 100644 --- a/erpnext/patches/v8_7/make_subscription_from_recurring_data.py +++ b/erpnext/patches/v8_7/make_subscription_from_recurring_data.py @@ -18,24 +18,33 @@ def execute(): frappe.reload_doc('accounts', 'doctype', 'journal_entry') frappe.reload_doc('accounts', 'doctype', 'payment_entry') - for doctype in ['Sales Order', 'Sales Invoice', - 'Purchase Invoice', 'Purchase Invoice']: - for data in get_data(doctype): - make_subscription(doctype, data) + for doctype in ['Sales Order', 'Sales Invoice', 'Purchase Order', 'Purchase Invoice']: + date_field = "transaction_date" + if doctype in ("Sales Invoice", "Purchase Invoice"): + date_field = "posting_date" -def get_data(doctype): - return frappe.db.sql(""" select name, from_date, end_date, recurring_type,recurring_id, + for data in get_data(doctype, date_field): + make_subscription(doctype, data, date_field) + +def get_data(doctype, date_field): + return frappe.db.sql(""" select name, from_date, end_date, recurring_type, recurring_id, next_date, notify_by_email, notification_email_address, recurring_print_format, - repeat_on_day_of_month, submit_on_creation, docstatus - from `tab{0}` where is_recurring = 1 and next_date >= %s and docstatus < 2 - """.format(doctype), today(), as_dict=1) + repeat_on_day_of_month, submit_on_creation, docstatus, {0} + from `tab{1}` where is_recurring = 1 and next_date >= %s and docstatus < 2 + order by next_date desc + """.format(date_field, doctype), today(), as_dict=1) + +def make_subscription(doctype, data, date_field): + if data.name == data.recurring_id: + start_date = data.get(date_field) + else: + start_date = frappe.db.get_value(doctype, data.recurring_id, date_field) -def make_subscription(doctype, data): doc = frappe.get_doc({ 'doctype': 'Subscription', 'reference_doctype': doctype, - 'reference_document': data.name, - 'start_date': data.from_date, + 'reference_document': data.recurring_id, + 'start_date': start_date, 'end_date': data.end_date, 'frequency': data.recurring_type, 'repeat_on_day': data.repeat_on_day_of_month, diff --git a/erpnext/patches/v9_0/fix_subscription_next_date.py b/erpnext/patches/v9_0/fix_subscription_next_date.py index a36e60ccaa..07af7129f6 100644 --- a/erpnext/patches/v9_0/fix_subscription_next_date.py +++ b/erpnext/patches/v9_0/fix_subscription_next_date.py @@ -3,25 +3,41 @@ from __future__ import unicode_literals import frappe +from frappe.utils import getdate +from erpnext.accounts.doctype.subscription.subscription import get_next_schedule_date def execute(): frappe.reload_doctype('Subscription') doctypes = ('Purchase Order', 'Sales Order', 'Purchase Invoice', 'Sales Invoice') - for data in frappe.get_all('Subscription', fields = ["name", "reference_doctype", "reference_document"], - filters = {'reference_doctype': ('in', doctypes)}): - doc = frappe.get_doc('Subscription', data.name) - fields = ['transaction_date'] - if doc.reference_doctype in ['Sales Invoice', 'Purchase Invoice']: - fields = ['posting_date'] + for data in frappe.get_all('Subscription', + fields = ["name", "reference_doctype", "reference_document", + "start_date", "frequency", "repeat_on_day"], + filters = {'reference_doctype': ('in', doctypes), 'docstatus': 1}): - fields.extend(['from_date', 'to_date']) - reference_data = frappe.db.get_value(data.reference_doctype, - data.reference_document, fields, as_dict=1) + recurring_id = frappe.db.get_value(data.reference_doctype, data.reference_document, "recurring_id") + if recurring_id: + frappe.db.sql("update `tab{0}` set subscription=%s where recurring_id=%s" + .format(data.reference_doctype), (data.name, recurring_id)) - if reference_data: - doc.start_date = reference_data.get('posting_date') or reference_data.get('transaction_date') - doc.from_date = reference_data.get('from_date') - doc.to_date = reference_data.get('to_date') - doc.set_next_schedule_date() - doc.db_update() \ No newline at end of file + date_field = 'transaction_date' + if data.reference_doctype in ['Sales Invoice', 'Purchase Invoice']: + date_field = 'posting_date' + + start_date = frappe.db.get_value(data.reference_doctype, data.reference_document, date_field) + + if start_date and getdate(start_date) != getdate(data.start_date): + last_ref_date = frappe.db.sql(""" + select {0} + from `tab{1}` + where subscription=%s and docstatus < 2 + order by creation desc + limit 1 + """.format(date_field, data.reference_doctype), data.name)[0][0] + + next_schedule_date = get_next_schedule_date(last_ref_date, data.frequency, data.repeat_on_day) + + frappe.db.set_value("Subscription", data.name, { + "start_date": start_date, + "next_schedule_date": next_schedule_date + }, None) \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index b57895ad7c..36f1284c43 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -3272,419 +3272,13 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription", - "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": "Subscription", - "length": 0, - "no_copy": 0, - "options": "Subscription", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "is_recurring", - "columns": 0, - "depends_on": "eval:doc.docstatus<2 && !doc.__islocal", - "fieldname": "recurring_order", - "fieldtype": "Section Break", - "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": "Recurring Order", - "length": 0, - "no_copy": 0, - "options": "fa fa-time", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "settings", - "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, - "label": "Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_bulk_edit": 0, "allow_on_submit": 1, "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "eval:doc.docstatus<2", - "description": "", - "fieldname": "is_recurring", - "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": "Is Recurring", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_recurring", - "description": "", - "fieldname": "recurring_id", - "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": "Reference Document", - "length": 0, - "no_copy": 1, - "options": "Sales Order", - "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, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "recurring_type", - "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": "Frequency", - "length": 0, - "no_copy": 1, - "options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "repeat_on_day_of_month", - "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": "Repeat on Day of Month", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "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": "Recurring Upto", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "fieldname": "submit_on_creation", - "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": "Submit on creation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name", - "description": "", - "fieldname": "notify_by_email", - "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": "Notify by email", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name", - "description": "", - "fieldname": "notification_email_address", - "fieldtype": "Code", - "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": "Notification Email Address", - "length": 0, - "no_copy": 1, - "options": "Email", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name", - "fieldname": "recurring_print_format", - "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": "Recurring Print Format", - "length": 0, - "no_copy": 0, - "options": "Print Format", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break83", - "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, - "label": "This Document", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_recurring", + "depends_on": "", "description": "", "fieldname": "from_date", "fieldtype": "Date", @@ -3715,7 +3309,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "is_recurring", + "depends_on": "", "description": "", "fieldname": "to_date", "fieldtype": "Date", @@ -3746,10 +3340,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "depends_on": "is_recurring", - "description": "", - "fieldname": "next_date", - "fieldtype": "Date", + "fieldname": "column_break_108", + "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, @@ -3757,11 +3349,42 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Next Date", "length": 0, - "no_copy": 1, + "no_copy": 0, "permlevel": 0, - "print_hide": 1, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "subscription", + "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": "Subscription", + "length": 0, + "no_copy": 0, + "options": "Subscription", + "permlevel": 0, + "precision": "", + "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, "remember_last_selected_value": 0, @@ -3783,7 +3406,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-09-19 11:21:36.332326", + "modified": "2017-10-24 12:52:28.115742", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 98333c495e..3821ec930b 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -347,15 +347,15 @@ class SalesOrder(SellingController): return items def on_recurring(self, reference_doc, subscription_doc): - self.set("delivery_date", get_next_schedule_date(reference_doc.delivery_date, subscription_doc.frequency, - cint(subscription_doc.repeat_on_day))) + self.set("delivery_date", get_next_schedule_date(reference_doc.delivery_date, + subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) for d in self.get("items"): reference_delivery_date = frappe.db.get_value("Sales Order Item", {"parent": reference_doc.name, "item_code": d.item_code, "idx": d.idx}, "delivery_date") - d.set("delivery_date", - get_next_schedule_date(reference_delivery_date, subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) + d.set("delivery_date", get_next_schedule_date(reference_delivery_date, + subscription_doc.frequency, cint(subscription_doc.repeat_on_day))) def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context From 464289b7260f675af2ce0f338c78c7d5e98583b2 Mon Sep 17 00:00:00 2001 From: Manas Solanki Date: Wed, 25 Oct 2017 12:13:42 +0530 Subject: [PATCH 17/38] copy the older total amount field and corrsp. changes in report (#11322) --- erpnext/patches.txt | 1 + erpnext/patches/v9_0/copy_old_fees_field_data.py | 11 +++++++++++ erpnext/schools/doctype/fees/fees.py | 2 +- .../student_fee_collection.json | 6 +++--- 4 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 erpnext/patches/v9_0/copy_old_fees_field_data.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f6289f59ce..805e1ef259 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -452,3 +452,4 @@ erpnext.patches.v9_0.fix_subscription_next_date #2017-10-23 erpnext.patches.v9_0.add_healthcare_domain erpnext.patches.v9_0.set_variant_item_description erpnext.patches.v9_0.set_uoms_in_variant_field +erpnext.patches.v9_0.copy_old_fees_field_data diff --git a/erpnext/patches/v9_0/copy_old_fees_field_data.py b/erpnext/patches/v9_0/copy_old_fees_field_data.py new file mode 100644 index 0000000000..fb11ee5a56 --- /dev/null +++ b/erpnext/patches/v9_0/copy_old_fees_field_data.py @@ -0,0 +1,11 @@ +# Copyright (c) 2017, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + if "total_amount" not in frappe.db.get_table_columns("Fees"): + return + + frappe.db.sql("""update tabFees set grand_total=total_amount where grand_total = 0.0""") \ No newline at end of file diff --git a/erpnext/schools/doctype/fees/fees.py b/erpnext/schools/doctype/fees/fees.py index 42b329e4b7..bfe6af4bdb 100644 --- a/erpnext/schools/doctype/fees/fees.py +++ b/erpnext/schools/doctype/fees/fees.py @@ -112,7 +112,7 @@ def get_fee_list(doctype, txt, filters, limit_start, limit_page_length=20, order user = frappe.session.user student = frappe.db.sql("select name from `tabStudent` where student_email_id= %s", user) if student: - return frappe. db.sql('''select name, program, due_date, paid_amount, outstanding_amount, total_amount from `tabFees` + return frappe. db.sql('''select name, program, due_date, paid_amount, outstanding_amount, grand_total from `tabFees` where student= %s and docstatus=1 order by due_date asc limit {0} , {1}''' .format(limit_start, limit_page_length), student, as_dict = True) diff --git a/erpnext/schools/report/student_fee_collection/student_fee_collection.json b/erpnext/schools/report/student_fee_collection/student_fee_collection.json index 264638f821..5c63765cf8 100644 --- a/erpnext/schools/report/student_fee_collection/student_fee_collection.json +++ b/erpnext/schools/report/student_fee_collection/student_fee_collection.json @@ -5,14 +5,14 @@ "disabled": 0, "docstatus": 0, "doctype": "Report", - "idx": 2, + "idx": 3, "is_standard": "Yes", - "modified": "2017-02-24 20:05:08.514320", + "modified": "2017-10-25 11:59:26.003899", "modified_by": "Administrator", "module": "Schools", "name": "Student Fee Collection", "owner": "Administrator", - "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(paid_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(total_amount) as \"Total Amount:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student", + "query": "SELECT\n student as \"Student:Link/Student:200\",\n student_name as \"Student Name::200\",\n sum(paid_amount) as \"Paid Amount:Currency:150\",\n sum(outstanding_amount) as \"Outstanding Amount:Currency:150\",\n sum(grand_total) as \"Grand Total:Currency:150\"\nFROM\n `tabFees` \nGROUP BY\n student", "ref_doctype": "Fees", "report_name": "Student Fee Collection", "report_type": "Query Report", From 49a7bde6e2ff7f0ce8f19924490ff49993f5aba2 Mon Sep 17 00:00:00 2001 From: Doridel Cahanap Date: Wed, 25 Oct 2017 14:45:17 +0800 Subject: [PATCH 18/38] [minor] Label for Training Event in Training Program Dashboard (#11280) --- .../hr/doctype/training_program/training_program_dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/training_program/training_program_dashboard.py b/erpnext/hr/doctype/training_program/training_program_dashboard.py index b5d9f19a26..a314081c6b 100644 --- a/erpnext/hr/doctype/training_program/training_program_dashboard.py +++ b/erpnext/hr/doctype/training_program/training_program_dashboard.py @@ -5,7 +5,7 @@ def get_data(): 'fieldname': 'training_program', 'transactions': [ { - 'label': _('Training Event'), + 'label': _('Training Events'), 'items': ['Training Event'] }, ] From e6712c129c9921ce250ba209f0ed42ba56e36d2c Mon Sep 17 00:00:00 2001 From: Makarand Bauskar Date: Wed, 25 Oct 2017 12:17:40 +0530 Subject: [PATCH 19/38] [hotfix] fixed Permissions Error while fetching allow_stale field value from Accounts Settings (#11182) * [hotfix] fixed Permissions Error while fetching allow_stale field value from Accounts Settings * [minor] renamed the allow_stale_rate method to stale_rate_allowed --- .../accounts/doctype/payment_entry/payment_entry.js | 12 ++---------- erpnext/public/js/controllers/transaction.js | 6 +----- erpnext/public/js/utils.js | 4 ++++ erpnext/startup/boot.py | 2 ++ 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index fc9fc97332..17ac1f7056 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -405,11 +405,7 @@ frappe.ui.form.on('Payment Entry', { } // Make read only if Accounts Settings doesn't allow stale rates - frappe.model.get_value("Accounts Settings", null, "allow_stale", - function(d){ - frm.set_df_property("source_exchange_rate", "read_only", cint(d.allow_stale) ? 0 : 1); - } - ); + frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed()); }, target_exchange_rate: function(frm) { @@ -430,11 +426,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_paid_amount_based_on_received_amount = false; // Make read only if Accounts Settings doesn't allow stale rates - frappe.model.get_value("Accounts Settings", null, "allow_stale", - function(d){ - frm.set_df_property("target_exchange_rate", "read_only", cint(d.allow_stale) ? 0 : 1); - } - ); + frm.set_df_property("target_exchange_rate", "read_only", erpnext.stale_rate_allowed()); }, paid_amount: function(frm) { diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 5b647f80d2..41fdc6e646 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -542,11 +542,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } // Make read only if Accounts Settings doesn't allow stale rates - frappe.model.get_value("Accounts Settings", null, "allow_stale", - function(d){ - me.set_df_property("conversion_rate", "read_only", cint(d.allow_stale) ? 0 : 1); - } - ); + this.frm.set_df_property("conversion_rate", "read_only", erpnext.stale_rate_allowed()); }, set_actual_charges_based_on_currency: function() { diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 721f216888..efb04d1c09 100644 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -37,6 +37,10 @@ $.extend(erpnext, { } }, + stale_rate_allowed: () => { + return cint(frappe.boot.sysdefaults.allow_stale) || 1; + }, + setup_serial_no: function() { var grid_row = cur_frm.open_grid_row(); if(!grid_row || !grid_row.grid_form.fields_dict.serial_no || diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index 3164306109..2080224a70 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -19,6 +19,8 @@ def boot_session(bootinfo): 'territory') bootinfo.sysdefaults.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') + bootinfo.sysdefaults.allow_stale = frappe.db.get_single_value('Accounts Settings', + 'allow_stale') or 1 bootinfo.notification_settings = frappe.get_doc("Notification Control", "Notification Control") From 085b4842a1b0b50bcf4709754a54763be4d687eb Mon Sep 17 00:00:00 2001 From: Makarand Bauskar Date: Wed, 25 Oct 2017 12:19:10 +0530 Subject: [PATCH 20/38] [minor] minor fixes in get_company_details for Hub page (#11168) --- erpnext/hub_node/page/hub/hub.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hub_node/page/hub/hub.js b/erpnext/hub_node/page/hub/hub.js index 143f55444f..c253155b93 100644 --- a/erpnext/hub_node/page/hub/hub.js +++ b/erpnext/hub_node/page/hub/hub.js @@ -483,7 +483,7 @@ erpnext.hub.Hub = class Hub { } frappe.call({ method: 'erpnext.hub_node.get_company_details', - args: {company_id: company_id} + args: {hub_sync_id: company_id} }).then((r) => { if (r.message) { const company_details = r.message.company_details; From bafcd7418ab21402835032bc21d29164eaa1bd82 Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Wed, 25 Oct 2017 07:52:08 +0100 Subject: [PATCH 21/38] Add Bundle Description Field To Product Bundle (#11122) * add description field that shows in list view * description field should be simply `description` --- .../product_bundle/product_bundle.json | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.json b/erpnext/selling/doctype/product_bundle/product_bundle.json index 5a1aeb4eb5..b63fb4bdcf 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.json +++ b/erpnext/selling/doctype/product_bundle/product_bundle.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, "beta": 0, @@ -12,6 +13,7 @@ "editable_grid": 0, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -40,6 +42,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -72,6 +75,37 @@ "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "description", + "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": "Description", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -101,6 +135,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -132,6 +167,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -160,6 +196,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -190,19 +227,19 @@ "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "fa fa-sitemap", "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-02-22 05:06:30.143089", - "modified_by": "Administrator", + "modified": "2017-10-18 14:23:06.538568", + "modified_by": "tundebabzy@gmail.com", "module": "Selling", "name": "Product Bundle", "owner": "Administrator", From 40a02769c5c8bd9f2c2c917aab228941f2273b6d Mon Sep 17 00:00:00 2001 From: tundebabzy Date: Wed, 25 Oct 2017 07:54:34 +0100 Subject: [PATCH 22/38] Error in the Address fetched in Sales Order.(#11129) (#11145) * add new function - `get_party_shipping_address` * `swap `get_default_address` with `get_party_shipping_address` * test cases * properly sets order by direction * move `get_party_shipping_address` to party.py * fix test module import --- erpnext/accounts/party.py | 31 ++++++++++- erpnext/accounts/test/__init__.py | 0 erpnext/accounts/test/test_utils.py | 84 +++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 erpnext/accounts/test/__init__.py create mode 100644 erpnext/accounts/test/test_utils.py diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index bcec0a29c9..ce049f5d87 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -76,7 +76,7 @@ def set_address_details(out, party, party_type, doctype=None, company=None): # shipping address if party_type in ["Customer", "Lead"]: - out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address') + out.shipping_address_name = get_party_shipping_address(party_type, party.name) out.shipping_address = get_address_display(out["shipping_address_name"]) if doctype: out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name)) @@ -418,3 +418,32 @@ def get_dashboard_info(party_type, party): info["total_unpaid"] = -1 * info["total_unpaid"] return info + + +def get_party_shipping_address(doctype, name): + """ + Returns an Address name (best guess) for the given doctype and name for which `address_type == 'Shipping'` is true. + and/or `is_shipping_address = 1`. + + It returns an empty string if there is no matching record. + + :param doctype: Party Doctype + :param name: Party name + :return: String + """ + out = frappe.db.sql( + 'SELECT dl.parent ' + 'from `tabDynamic Link` dl join `tabAddress` ta on dl.parent=ta.name ' + 'where ' + 'dl.link_doctype=%s ' + 'and dl.link_name=%s ' + 'and dl.parenttype="Address" ' + 'and ' + '(ta.address_type="Shipping" or ta.is_shipping_address=1) ' + 'order by ta.is_shipping_address desc, ta.address_type desc limit 1', + (doctype, name) + ) + if out: + return out[0][0] + else: + return '' diff --git a/erpnext/accounts/test/__init__.py b/erpnext/accounts/test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py new file mode 100644 index 0000000000..0fca470fe5 --- /dev/null +++ b/erpnext/accounts/test/test_utils.py @@ -0,0 +1,84 @@ +import unittest +from erpnext.accounts.party import get_party_shipping_address +from frappe.test_runner import make_test_objects + + +class TestUtils(unittest.TestCase): + @classmethod + def setUpClass(cls): + super(TestUtils, cls).setUpClass() + make_test_objects('Address', ADDRESS_RECORDS) + + def test_get_party_shipping_address(self): + address = get_party_shipping_address('Customer', '_Test Customer 1') + self.assertEqual(address, '_Test Billing Address 2 Title-Billing') + + def test_get_party_shipping_address2(self): + address = get_party_shipping_address('Customer', '_Test Customer 2') + self.assertEqual(address, '_Test Shipping Address 2 Title-Shipping') + + +ADDRESS_RECORDS = [ + { + "doctype": "Address", + "address_type": "Billing", + "address_line1": "Address line 1", + "address_title": "_Test Billing Address Title", + "city": "Lagos", + "country": "Nigeria", + "links": [ + { + "link_doctype": "Customer", + "link_name": "_Test Customer 2", + "doctype": "Dynamic Link" + } + ] + }, + { + "doctype": "Address", + "address_type": "Shipping", + "address_line1": "Address line 2", + "address_title": "_Test Shipping Address 1 Title", + "city": "Lagos", + "country": "Nigeria", + "links": [ + { + "link_doctype": "Customer", + "link_name": "_Test Customer 2", + "doctype": "Dynamic Link" + } + ] + }, + { + "doctype": "Address", + "address_type": "Shipping", + "address_line1": "Address line 3", + "address_title": "_Test Shipping Address 2 Title", + "city": "Lagos", + "country": "Nigeria", + "is_shipping_address": "1", + "links": [ + { + "link_doctype": "Customer", + "link_name": "_Test Customer 2", + "doctype": "Dynamic Link" + } + ] + }, + { + "doctype": "Address", + "address_type": "Billing", + "address_line1": "Address line 4", + "address_title": "_Test Billing Address 2 Title", + "city": "Lagos", + "country": "Nigeria", + "is_shipping_address": "1", + "links": [ + { + "link_doctype": "Customer", + "link_name": "_Test Customer 1", + "doctype": "Dynamic Link" + } + ] + } +] From 3dc21b099d42ff6f5d81847ff9d35d46ea16a0c5 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 25 Oct 2017 12:38:17 +0530 Subject: [PATCH 23/38] [Fix] Translation issue (#11323) --- erpnext/templates/emails/recurring_document_failed.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/templates/emails/recurring_document_failed.html b/erpnext/templates/emails/recurring_document_failed.html index 27c43bc0fc..f9e8c2dafc 100644 --- a/erpnext/templates/emails/recurring_document_failed.html +++ b/erpnext/templates/emails/recurring_document_failed.html @@ -2,8 +2,8 @@

{{_("An error occured while creating recurring")}} {{ type }} {{ name }} {{_("for")}} {{ party }}.

{{_("This could be because of some invalid Email Addresses in the")}} {{ type }}.

-

{{_("To stop sending repetitive error notifications from the system, we have checked "Disabled" field in the subscription")}} {{ subscription}} {{_("for the")}} {{ type }} {{ name }}.

-

{{_("Please correct the")}} {{ type }} {{_('and unchcked "Disabled" in the')}} {{ subscription }} {{_("for making recurring again.")}}

+

{{_("To stop sending repetitive error notifications from the system, we have checked Disabled field in the subscription")}} {{ subscription}} {{_("for the")}} {{ type }} {{ name }}.

+

{{_("Please correct the")}} {{ type }} {{_('and unchcked Disabled in the')}} {{ subscription }} {{_("for making recurring again.")}}


{{_("It is necessary to take this action today itself for the above mentioned recurring")}} {{ type }} {{_('to be generated. If delayed, you will have to manually change the "Repeat on Day of Month" field From 4a60554b910460ff1e8355dc3d3060639827733e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 25 Oct 2017 13:59:29 +0530 Subject: [PATCH 24/38] Green indicator in the cart for non stock item (#11325) --- .../page/point_of_sale/point_of_sale.js | 30 +++++++++++++++++-- .../page/point_of_sale/point_of_sale.py | 2 +- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index 7110d4c199..a810636646 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -113,6 +113,9 @@ erpnext.pos.PointOfSale = class PointOfSale { }, on_select_change: () => { this.cart.numpad.set_inactive(); + }, + get_item_details: (item_code) => { + return this.items.get(item_code); } } }); @@ -408,6 +411,7 @@ erpnext.pos.PointOfSale = class PointOfSale { class POSCart { constructor({frm, wrapper, pos_profile, events}) { this.frm = frm; + this.item_data = {}; this.wrapper = wrapper; this.events = events; this.pos_profile = pos_profile; @@ -667,7 +671,8 @@ class POSCart { const $item = this.$cart_items.find(`[data-item-code="${item.item_code}"]`); if(item.qty > 0) { - const indicator_class = item.actual_qty >= item.qty ? 'green' : 'red'; + const is_stock_item = this.get_item_details(item.item_code).is_stock_item; + const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red'; const remove_class = indicator_class == 'green' ? 'red' : 'green'; $item.find('.quantity input').val(item.qty); @@ -681,8 +686,9 @@ class POSCart { } get_item_html(item) { + const is_stock_item = this.get_item_details(item.item_code).is_stock_item; const rate = format_currency(item.rate, this.frm.doc.currency); - const indicator_class = item.actual_qty >= item.qty ? 'green' : 'red'; + const indicator_class = (!is_stock_item || item.actual_qty >= item.qty) ? 'green' : 'red'; return `

@@ -717,6 +723,14 @@ class POSCart { } } + get_item_details(item_code) { + if (!this.item_data[item_code]) { + this.item_data[item_code] = this.events.get_item_details(item_code); + } + + return this.item_data[item_code]; + } + exists(item_code) { let $item = this.$cart_items.find(`[data-item-code="${item_code}"]`); return $item.length > 0; @@ -965,11 +979,13 @@ class POSItems { this.search_index = this.search_index || {}; if (this.search_index[search_term]) { const items = this.search_index[search_term]; + this.items = items; this.render_items(items); this.set_item_in_the_cart(items); return; } } else if (item_group == "All Item Groups") { + this.items = this.all_items; return this.render_items(this.all_items); } @@ -979,6 +995,7 @@ class POSItems { this.search_index[search_term] = items; } + this.items = items; this.render_items(items); this.set_item_in_the_cart(items, serial_no, batch_no); }); @@ -1021,7 +1038,14 @@ class POSItems { } get(item_code) { - return this.items[item_code]; + let item = {}; + this.items.map(data => { + if (data.item_code === item_code) { + item = data; + } + }) + + return item } get_all() { diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index b92c653030..7400e432ae 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -36,7 +36,7 @@ def get_items(start, page_length, price_list, item_group, search_value=""): lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) # locate function is used to sort by closest match from the beginning of the value res = frappe.db.sql("""select i.name as item_code, i.item_name, i.image as item_image, - item_det.price_list_rate, item_det.currency + i.is_stock_item, item_det.price_list_rate, item_det.currency from `tabItem` i LEFT JOIN (select item_code, price_list_rate, currency from `tabItem Price` where price_list=%(price_list)s) item_det From c7bd77111ea513e81613e75e9b7ad6cc839000cf Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 14:32:32 +0600 Subject: [PATCH 25/38] bumped to version 9.1.8 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0c7daad89f..1ebff9ac24 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from erpnext.hooks import regional_overrides -__version__ = '9.1.7' +__version__ = '9.1.8' def get_default_company(user=None): '''Get default company for user''' From e146143bd13a2008b09853c7e65a50cdcae35a96 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 14:40:38 +0600 Subject: [PATCH 26/38] bumped to version 9.2.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 1ebff9ac24..e822fda512 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from erpnext.hooks import regional_overrides -__version__ = '9.1.8' +__version__ = '9.2.0' def get_default_company(user=None): '''Get default company for user''' From fb734976eb6e2f9d442788d180f4e083ed687a5b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 15:53:07 +0530 Subject: [PATCH 27/38] Removed appintment field from Invoice, it should be a custom field and will be added from domain settings --- .../doctype/fee_validity/test_fee_validity.py | 8 +- .../patient_appointment.js | 18 +- .../patient_appointment.json | 3 +- .../patient_appointment.py | 159 ++++++++++++------ .../doctype/physician/physician.json | 3 +- .../healthcare/doctype/physician/physician.py | 14 +- .../doctype/physician/test_physician.py | 31 +++- .../physician_schedule/physician_schedule.py | 4 +- 8 files changed, 169 insertions(+), 71 deletions(-) diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py index 2345872983..53f82e034c 100644 --- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py @@ -19,13 +19,13 @@ class TestFeeValidity(unittest.TestCase): patient = frappe.new_doc("Patient") patient.patient_name = "Test Patient" patient.sex = "Male" - patient.save(ignore_permissions = True) + patient.save(ignore_permissions=True) patient = patient.name if not physician: physician = frappe.new_doc("Physician") - physician.first_name= "Amit Jain" - physician.save(ignore_permissions = True) + physician.first_name = "Amit Jain" + physician.save(ignore_permissions=True) physician = physician.name frappe.db.set_value("Healthcare Settings", None, "max_visit", 2) @@ -50,5 +50,5 @@ def create_appointment(patient, physician, appointment_date): appointment.patient = patient appointment.physician = physician appointment.appointment_date = appointment_date - appointment.save(ignore_permissions = True) + appointment.save(ignore_permissions=True) return appointment diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index 2532ed115a..1942b66f7b 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -30,6 +30,14 @@ frappe.ui.form.on('Patient Appointment', { frm.add_custom_button(__('Cancel'), function() { btn_update_status(frm, "Cancelled"); }); + + frm.add_custom_button(__("Consultation"),function(){ + btn_create_consultation(frm); + },"Create"); + + frm.add_custom_button(__('Vital Signs'), function() { + btn_create_vital_signs(frm); + },"Create"); } if(frm.doc.status == "Pending"){ frm.add_custom_button(__('Set Open'), function() { @@ -40,14 +48,6 @@ frappe.ui.form.on('Patient Appointment', { }); } - frm.add_custom_button(__("Consultation"),function(){ - btn_create_consultation(frm); - },"Create"); - - frm.add_custom_button(__('Vital Signs'), function() { - btn_create_vital_signs(frm); - },"Create"); - if(!frm.doc.__islocal){ if(frm.doc.sales_invoice && frappe.user.has_role("Accounts User")){ frm.add_custom_button(__('Invoice'), function() { @@ -188,7 +188,7 @@ var btn_update_status = function(frm, status){ frappe.call({ method: "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_status", - args: {appointmentId: doc.name, status:status}, + args: {appointment_id: doc.name, status:status}, callback: function(data){ if(!data.exc){ frm.reload_doc(); diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json index 57e568b6b3..1663c5b64f 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json @@ -234,6 +234,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "eval:!doc.__islocal", "fieldname": "section_break_1", "fieldtype": "Section Break", "hidden": 0, @@ -755,7 +756,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-05 12:13:03.204936", + "modified": "2017-10-25 23:33:36.060803", "modified_by": "Administrator", "module": "Healthcare", "name": "Patient Appointment", diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 2647034f78..4379986ddc 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -6,66 +6,100 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document import json -from frappe.utils import getdate +from frappe.utils import getdate, cint from frappe import _ import datetime from frappe.core.doctype.sms_settings.sms_settings import send_sms from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account + class PatientAppointment(Document): def on_update(self): today = datetime.date.today() appointment_date = getdate(self.appointment_date) - #If appointment created for today set as open - if(today == appointment_date): - frappe.db.set_value("Patient Appointment",self.name,"status","Open") + + # If appointment created for today set as open + if today == appointment_date: + frappe.db.set_value("Patient Appointment", self.name, "status", "Open") self.reload() def after_insert(self): - #Check fee validity exists + # Check fee validity exists appointment = self validity_exist = validity_exists(appointment.physician, appointment.patient) - if validity_exist : - fee_validity = frappe.get_doc("Fee Validity",validity_exist[0][0]) - #Check if the validity is valid + if validity_exist: + fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) + + # Check if the validity is valid appointment_date = getdate(appointment.appointment_date) - if((fee_validity.valid_till >= appointment_date) and (fee_validity.visited < fee_validity.max_visit)): + if (fee_validity.valid_till >= appointment_date) and (fee_validity.visited < fee_validity.max_visit): visited = fee_validity.visited + 1 - frappe.db.set_value("Fee Validity",fee_validity.name,"visited",visited) - if(fee_validity.ref_invoice): - frappe.db.set_value("Patient Appointment",appointment.name,"sales_invoice",fee_validity.ref_invoice) + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + if fee_validity.ref_invoice: + frappe.db.set_value("Patient Appointment", appointment.name, "sales_invoice", fee_validity.ref_invoice) frappe.msgprint(_("{0} has fee validity till {1}").format(appointment.patient, fee_validity.valid_till)) confirm_sms(self) -def appointment_cancel(appointmentId): - appointment = frappe.get_doc("Patient Appointment",appointmentId) - #If invoice --> fee_validity update with -1 visit - if (appointment.sales_invoice): - validity = frappe.db.exists({"doctype": "Fee Validity","ref_invoice": appointment.sales_invoice}) - if(validity): - fee_validity = frappe.get_doc("Fee Validity",validity[0][0]) - visited = fee_validity.visited - 1 - frappe.db.set_value("Fee Validity",fee_validity.name,"visited",visited) - if visited <= 0: - frappe.msgprint(_("Appointment cancelled, Please review and cancel the invoice {0}".format(appointment.sales_invoice))) - else: - frappe.msgprint(_("Appointment cancelled")) + def save(self, *args, **kwargs): + # duration is the only changeable field in the document + if not self.is_new(): + self.db_set('duration', cint(self.duration)) + else: + super(PatientAppointment, self).save(*args, **kwargs) + + +def appointment_cancel(appointment_id): + appointment = frappe.get_doc("Patient Appointment", appointment_id) + + # If invoice --> fee_validity update with -1 visit + if appointment.sales_invoice: + validity = frappe.db.exists({"doctype": "Fee Validity", "ref_invoice": appointment.sales_invoice}) + if validity: + fee_validity = frappe.get_doc("Fee Validity", validity[0][0]) + visited = fee_validity.visited - 1 + frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) + if visited <= 0: + frappe.msgprint( + _("Appointment cancelled, Please review and cancel the invoice {0}".format(appointment.sales_invoice)) + ) + else: + frappe.msgprint(_("Appointment cancelled")) + @frappe.whitelist() def get_availability_data(date, physician): - # get availability data of 'physician' on 'date' + """ + Get availability data of 'physician' on 'date' + :param date: Date to check in schedule + :param physician: Name of the physician + :return: dict containing a list of available slots, list of appointments and time of appointments + """ + date = getdate(date) weekday = date.strftime("%A") available_slots = [] + physician_schedule_name = None + physician_schedule = None + time_per_appointment = None + # get physicians schedule physician_schedule_name = frappe.db.get_value("Physician", physician, "physician_schedule") - physician_schedule = frappe.get_doc("Physician Schedule", physician_schedule_name) - time_per_appointment = frappe.db.get_value("Physician", physician, "time_per_appointment") + if physician_schedule_name: + physician_schedule = frappe.get_doc("Physician Schedule", physician_schedule_name) + time_per_appointment = frappe.db.get_value("Physician", physician, "time_per_appointment") + else: + frappe.throw(_("Dr {0} does not have a Physician Schedule. Add it in Physician master".format(physician))) - for t in physician_schedule.time_slots: - if weekday == t.day: - available_slots.append(t) + if physician_schedule: + for t in physician_schedule.time_slots: + if weekday == t.day: + available_slots.append(t) + + # `time_per_appointment` should never be None since validation in `Patient` is supposed to prevent + # that. However, it isn't impossible so we'll prepare for that. + if not time_per_appointment: + frappe.throw(_('"Time Per Appointment" hasn"t been set for Dr {0}. Add it in Physician master.').format(physician)) # if physician not available return if not available_slots: @@ -89,27 +123,36 @@ def get_availability_data(date, physician): "time_per_appointment": time_per_appointment } + @frappe.whitelist() -def update_status(appointmentId, status): - frappe.db.set_value("Patient Appointment",appointmentId,"status",status) - if(status=="Cancelled"): - appointment_cancel(appointmentId) +def update_status(appointment_id, status): + frappe.db.set_value("Patient Appointment", appointment_id, "status", status) + if status == "Cancelled": + appointment_cancel(appointment_id) + @frappe.whitelist() def set_open_appointments(): today = getdate() - frappe.db.sql("""update `tabPatient Appointment` set status='Open' where status = 'Scheduled' and appointment_date = %s""",(today)) + frappe.db.sql( + "update `tabPatient Appointment` set status='Open' where status = 'Scheduled'" + " and appointment_date = %s", today) + @frappe.whitelist() def set_pending_appointments(): today = getdate() - frappe.db.sql("""update `tabPatient Appointment` set status='Pending' where status in ('Scheduled','Open') and appointment_date < %s""",(today)) + frappe.db.sql( + "update `tabPatient Appointment` set status='Pending' where status in " + "('Scheduled','Open') and appointment_date < %s", today) + def confirm_sms(doc): - if (frappe.db.get_value("Healthcare Settings", None, "app_con")=='1'): + if frappe.db.get_value("Healthcare Settings", None, "app_con") == '1': message = frappe.db.get_value("Healthcare Settings", None, "app_con_msg") send_message(doc, message) + @frappe.whitelist() def create_invoice(company, physician, patient, appointment_id, appointment_date): if not appointment_id: @@ -134,21 +177,24 @@ def create_invoice(company, physician, patient, appointment_id, appointment_date frappe.db.set_value("Consultation", consultation[0][0], "invoice", sales_invoice.name) return sales_invoice.name + def get_fee_validity(physician, patient, date): validity_exist = validity_exists(physician, patient) - if validity_exist : - fee_validity = frappe.get_doc("Fee Validity",validity_exist[0][0]) + if validity_exist: + fee_validity = frappe.get_doc("Fee Validity", validity_exist[0][0]) fee_validity = update_fee_validity(fee_validity, date) else: fee_validity = create_fee_validity(physician, patient, date) return fee_validity + def validity_exists(physician, patient): return frappe.db.exists({ "doctype": "Fee Validity", "physician": physician, "patient": patient}) + def update_fee_validity(fee_validity, date): max_visit = frappe.db.get_value("Healthcare Settings", None, "max_visit") valid_days = frappe.db.get_value("Healthcare Settings", None, "valid_days") @@ -164,6 +210,7 @@ def update_fee_validity(fee_validity, date): fee_validity.save(ignore_permissions=True) return fee_validity + def create_fee_validity(physician, patient, date): fee_validity = frappe.new_doc("Fee Validity") fee_validity.physician = physician @@ -171,6 +218,7 @@ def create_fee_validity(physician, patient, date): fee_validity = update_fee_validity(fee_validity, date) return fee_validity + def create_invoice_items(appointment_id, physician, company, invoice): item_line = invoice.append("items") item_line.item_name = "Consulting Charges" @@ -178,16 +226,17 @@ def create_invoice_items(appointment_id, physician, company, invoice): item_line.qty = 1 item_line.uom = "Nos" item_line.conversion_factor = 1 - item_line.income_account = get_income_account(physician,company) + item_line.income_account = get_income_account(physician, company) op_consulting_charge = frappe.db.get_value("Physician", physician, "op_consulting_charge") if op_consulting_charge: item_line.rate = op_consulting_charge item_line.amount = op_consulting_charge return invoice + @frappe.whitelist() def create_consultation(appointment): - appointment = frappe.get_doc("Patient Appointment",appointment) + appointment = frappe.get_doc("Patient Appointment", appointment) consultation = frappe.new_doc("Consultation") consultation.appointment = appointment.name consultation.patient = appointment.patient @@ -199,29 +248,37 @@ def create_consultation(appointment): consultation.invoice = appointment.sales_invoice return consultation.as_dict() + def remind_appointment(): - if (frappe.db.get_value("Healthcare Settings", None, "app_rem")=='1'): + if frappe.db.get_value("Healthcare Settings", None, "app_rem") == '1': rem_before = datetime.datetime.strptime(frappe.get_value("Healthcare Settings", None, "rem_before"), "%H:%M:%S") - rem_dt = datetime.datetime.now() + datetime.timedelta(hours = rem_before.hour, minutes=rem_before.minute, seconds= rem_before.second) + rem_dt = datetime.datetime.now() + datetime.timedelta( + hours=rem_before.hour, minutes=rem_before.minute, seconds=rem_before.second) - appointment_list = frappe.db.sql("select name from `tabPatient Appointment` where start_dt between %s and %s and reminded = 0 ", (datetime.datetime.now(), rem_dt)) + appointment_list = frappe.db.sql( + "select name from `tabPatient Appointment` where start_dt between %s and %s and reminded = 0 ", + (datetime.datetime.now(), rem_dt) + ) - for i in range (0,len(appointment_list)): + for i in range(0, len(appointment_list)): doc = frappe.get_doc("Patient Appointment", appointment_list[i][0]) message = frappe.db.get_value("Healthcare Settings", None, "app_rem_msg") send_message(doc, message) - frappe.db.set_value("Patient Appointment",doc.name,"reminded",1) + frappe.db.set_value("Patient Appointment", doc.name, "reminded",1) + def send_message(doc, message): - patient = frappe.get_doc("Patient",doc.patient) - if(patient.mobile): + patient = frappe.get_doc("Patient", doc.patient) + if patient.mobile: context = {"doc": doc, "alert": doc, "comments": None} if doc.get("_comments"): context["comments"] = json.loads(doc.get("_comments")) - #jinja to string convertion happens here + + # jinja to string convertion happens here message = frappe.render_template(message, context) number = [patient.mobile] - send_sms(number,message) + send_sms(number, message) + @frappe.whitelist() def get_events(start, end, filters=None): diff --git a/erpnext/healthcare/doctype/physician/physician.json b/erpnext/healthcare/doctype/physician/physician.json index 4348e9035e..3edad0b827 100644 --- a/erpnext/healthcare/doctype/physician/physician.json +++ b/erpnext/healthcare/doctype/physician/physician.json @@ -501,6 +501,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "description": "In minutes", "fieldname": "time_per_appointment", "fieldtype": "Data", "hidden": 0, @@ -809,7 +810,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-04 17:35:44.363742", + "modified": "2017-10-05 16:08:24.624644", "modified_by": "Administrator", "module": "Healthcare", "name": "Physician", diff --git a/erpnext/healthcare/doctype/physician/physician.py b/erpnext/healthcare/doctype/physician/physician.py index 8680d5d127..4d035d39f6 100644 --- a/erpnext/healthcare/doctype/physician/physician.py +++ b/erpnext/healthcare/doctype/physician/physician.py @@ -20,22 +20,32 @@ class Physician(Document): [cstr(self.get(f)).strip() for f in ["first_name","middle_name","last_name"]])) def validate(self): + self.validate_schedule_and_time() validate_party_accounts(self) + if self.user_id: self.validate_for_enabled_user_id() self.validate_duplicate_user_id() existing_user_id = frappe.db.get_value("Physician", self.name, "user_id") - if(self.user_id != existing_user_id): + if self.user_id != existing_user_id: frappe.permissions.remove_user_permission( "Physician", self.name, existing_user_id) - else: existing_user_id = frappe.db.get_value("Physician", self.name, "user_id") if existing_user_id: frappe.permissions.remove_user_permission( "Physician", self.name, existing_user_id) + def validate_schedule_and_time(self): + if (self.physician_schedule or self.time_per_appointment) and \ + not (self.physician_schedule and self.time_per_appointment): + frappe.msgprint( + _('Both "Physician Schedule" and Time Per Appointment" must be set for Dr {0}').format( + self.first_name), + title='Error', raise_exception=1, indicator='red' + ) + def on_update(self): if self.user_id: frappe.permissions.add_user_permission("Physician", self.name, self.user_id) diff --git a/erpnext/healthcare/doctype/physician/test_physician.py b/erpnext/healthcare/doctype/physician/test_physician.py index b6ea92cc72..e57bdae46e 100644 --- a/erpnext/healthcare/doctype/physician/test_physician.py +++ b/erpnext/healthcare/doctype/physician/test_physician.py @@ -3,8 +3,35 @@ # See license.txt from __future__ import unicode_literals import unittest +import frappe + +test_dependencies = ['Physician Schedule'] -# test_records = frappe.get_test_records('Physician') class TestPhysician(unittest.TestCase): - pass + def tearDown(self): + frappe.delete_doc_if_exists('Physician', '_Testdoctor2', force=1) + + def test_schedule_and_time(self): + physician = frappe.new_doc('Physician') + physician.first_name = '_Testdoctor2' + physician.physician_schedule = '_Test Testdoctor Schedule' + + self.assertRaises(frappe.ValidationError, physician.insert) + + physician.physician_schedule = '' + physician.time_per_appointment = 15 + + self.assertRaises(frappe.ValidationError, physician.insert) + + physician.physician_schedule = '_Test Testdoctor Schedule' + physician.time_per_appointment = 15 + + physician.insert() + + def test_new_physician_without_schedule(self): + physician = frappe.new_doc('Physician') + physician.first_name = '_Testdoctor2' + + physician.insert() + self.assertEqual(frappe.get_value('Physician', '_Testdoctor2', 'first_name'), '_Testdoctor2') diff --git a/erpnext/healthcare/doctype/physician_schedule/physician_schedule.py b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.py index 5cbdd126a9..167e9cd4b8 100644 --- a/erpnext/healthcare/doctype/physician_schedule/physician_schedule.py +++ b/erpnext/healthcare/doctype/physician_schedule/physician_schedule.py @@ -5,5 +5,7 @@ from __future__ import unicode_literals from frappe.model.document import Document + class PhysicianSchedule(Document): - pass + def autoname(self): + self.name = self.schedule_name From 2e6f6d6749dbaa18e8c84d0c73d63287a51978cf Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 25 Oct 2017 17:03:49 +0530 Subject: [PATCH 28/38] [minor] cleanup code (#11331) --- erpnext/accounts/doctype/subscription/subscription.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index fb01efe13b..1103b701dc 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -200,7 +200,7 @@ def update_doc(new_document, reference_doc, args, schedule_date): new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args) def set_subscription_period(args, mcount, new_document): - if new_document.meta.get_field('from_date') and new_document.meta.get_field('to_date'): + if mcount and new_document.meta.get_field('from_date') and new_document.meta.get_field('to_date'): last_ref_doc = frappe.db.sql(""" select name, from_date, to_date from `tab{0}` From e02ee898e9165614c38458b6b51a83a2b86acb47 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 25 Oct 2017 17:04:31 +0530 Subject: [PATCH 29/38] [Fix] Production order not displaying in the calendar view, if po has no operations (#11328) --- .../manufacturing/doctype/production_order/production_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_order/production_order.py b/erpnext/manufacturing/doctype/production_order/production_order.py index f4d37760d3..89ecbe7ec5 100644 --- a/erpnext/manufacturing/doctype/production_order/production_order.py +++ b/erpnext/manufacturing/doctype/production_order/production_order.py @@ -569,7 +569,7 @@ def get_events(start, end, filters=None): where ((ifnull(planned_start_date, '0000-00-00')!= '0000-00-00') \ and (planned_start_date <= %(end)s) \ and ((ifnull(planned_start_date, '0000-00-00')!= '0000-00-00') \ - and planned_end_date >= %(start)s)) {conditions} + and ifnull(planned_end_date, '2199-12-31 00:00:00') >= %(start)s)) {conditions} """.format(conditions=conditions), { "start": start, "end": end From c65f1d77459f94f7774f8533a8e44734fb4a3b3e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 25 Oct 2017 17:04:56 +0530 Subject: [PATCH 30/38] Search item using name instead of item code (#11327) --- erpnext/selling/page/point_of_sale/point_of_sale.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 7400e432ae..20b5bb05b6 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -71,9 +71,9 @@ def get_items(start, page_length, price_list, item_group, search_value=""): def get_conditions(item_code, serial_no, batch_no, barcode): if serial_no or batch_no or barcode: - return frappe.db.escape(item_code), "i.item_code = %(item_code)s" + return frappe.db.escape(item_code), "i.name = %(item_code)s" - condition = """(i.item_code like %(item_code)s + condition = """(i.name like %(item_code)s or i.item_name like %(item_code)s)""" return '%%%s%%'%(frappe.db.escape(item_code)), condition From 6a4e230cde1e35a3618db3452023cd4c1361394e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 25 Oct 2017 17:06:30 +0530 Subject: [PATCH 31/38] [fix] Status of Item (#11326) --- erpnext/stock/doctype/item/item_list.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/item/item_list.js b/erpnext/stock/doctype/item/item_list.js index cc46177f53..db53ae9651 100644 --- a/erpnext/stock/doctype/item/item_list.js +++ b/erpnext/stock/doctype/item/item_list.js @@ -4,12 +4,12 @@ frappe.listview_settings['Item'] = { filters: [["disabled", "=", "0"]], get_indicator: function(doc) { - if(doc.total_projected_qty < 0) { - return [__("Shortage"), "red", "total_projected_qty,<,0"]; - } else if (doc.disabled) { + if (doc.disabled) { return [__("Disabled"), "grey", "disabled,=,Yes"]; } else if (doc.end_of_life && doc.end_of_life < frappe.datetime.get_today()) { return [__("Expired"), "grey", "end_of_life,<,Today"]; + } else if(doc.total_projected_qty < 0) { + return [__("Shortage"), "red", "total_projected_qty,<,0"]; } else if (doc.has_variants) { return [__("Template"), "orange", "has_variants,=,Yes"]; } else if (doc.variant_of) { From d6e67ce123ef5f89352cfcf5af8ccd0f41edd60b Mon Sep 17 00:00:00 2001 From: Saurabh Date: Wed, 25 Oct 2017 17:44:22 +0530 Subject: [PATCH 32/38] [fix] check for null date fields (#11334) --- ...hedule_date_for_material_request_and_purchase_order.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py b/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py index 7ab0e2ca83..3d012978fa 100644 --- a/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py +++ b/erpnext/patches/v9_0/set_schedule_date_for_material_request_and_purchase_order.py @@ -17,6 +17,8 @@ def execute(): doc = frappe.get_doc(doctype, record) if doc.items: if not doc.schedule_date: - min_schedule_date = min([d.schedule_date for d in doc.items]) - frappe.db.set_value(doctype, record, - "schedule_date", min_schedule_date, update_modified=False) \ No newline at end of file + schedule_dates = [d.schedule_date for d in doc.items if d.schedule_date] + if len(schedule_dates) > 0: + min_schedule_date = min(schedule_dates) + frappe.db.set_value(doctype, record, + "schedule_date", min_schedule_date, update_modified=False) \ No newline at end of file From 92a6155bb0701a4f3f858cb53f8e7984582eea30 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Thu, 26 Oct 2017 09:44:40 +0530 Subject: [PATCH 33/38] [fix] translate domain --- erpnext/setup/setup_wizard/setup_wizard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py index f7b09d6934..766f9b5223 100644 --- a/erpnext/setup/setup_wizard/setup_wizard.py +++ b/erpnext/setup/setup_wizard/setup_wizard.py @@ -40,7 +40,7 @@ def setup_complete(args=None): frappe.local.message_log = [] domain_settings = frappe.get_single('Domain Settings') - domain_settings.set_active_domains([args.get('domain')]) + domain_settings.set_active_domains([_(args.get('domain'))]) frappe.db.commit() login_as_first_user(args) From d0131762cc8dd19175f89d0c538c7463ae00e609 Mon Sep 17 00:00:00 2001 From: Saurabh Date: Thu, 26 Oct 2017 10:29:22 +0600 Subject: [PATCH 34/38] bumped to version 9.2.1 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e822fda512..84b2b0388a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from erpnext.hooks import regional_overrides -__version__ = '9.2.0' +__version__ = '9.2.1' def get_default_company(user=None): '''Get default company for user''' From 52909b73bbcc807a8d0fe5064a55075674bcfd13 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 26 Oct 2017 11:06:07 +0530 Subject: [PATCH 35/38] [test] Fixed advance jv cancellation (#11333) --- .../accounts/doctype/purchase_invoice/test_purchase_invoice.py | 1 + erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 639620f910..474329fc9a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -473,6 +473,7 @@ class TestPurchaseInvoice(unittest.TestCase): import test_records as jv_test_records jv = frappe.copy_doc(jv_test_records[1]) + jv.accounts[0].is_advance = 'Yes' jv.insert() jv.submit() diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 264f027486..50d2ce810b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1133,6 +1133,7 @@ class TestSalesInvoice(unittest.TestCase): import test_records as jv_test_records jv = frappe.copy_doc(jv_test_records[0]) + jv.accounts[0].is_advance = 'Yes' jv.insert() jv.submit() From aa7896f1e11cc57329f9afb0ca5dc39761ca4eb4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 26 Oct 2017 14:33:28 +0530 Subject: [PATCH 36/38] [test] Fixed physician test cases (#11347) --- erpnext/healthcare/doctype/physician/test_physician.py | 4 ++-- .../doctype/physician_schedule/test_records.json | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 erpnext/healthcare/doctype/physician_schedule/test_records.json diff --git a/erpnext/healthcare/doctype/physician/test_physician.py b/erpnext/healthcare/doctype/physician/test_physician.py index e57bdae46e..4bd497a4e9 100644 --- a/erpnext/healthcare/doctype/physician/test_physician.py +++ b/erpnext/healthcare/doctype/physician/test_physician.py @@ -15,7 +15,7 @@ class TestPhysician(unittest.TestCase): def test_schedule_and_time(self): physician = frappe.new_doc('Physician') physician.first_name = '_Testdoctor2' - physician.physician_schedule = '_Test Testdoctor Schedule' + physician.physician_schedule = '_Testdoctor2 Schedule' self.assertRaises(frappe.ValidationError, physician.insert) @@ -24,7 +24,7 @@ class TestPhysician(unittest.TestCase): self.assertRaises(frappe.ValidationError, physician.insert) - physician.physician_schedule = '_Test Testdoctor Schedule' + physician.physician_schedule = '_Testdoctor2 Schedule' physician.time_per_appointment = 15 physician.insert() diff --git a/erpnext/healthcare/doctype/physician_schedule/test_records.json b/erpnext/healthcare/doctype/physician_schedule/test_records.json new file mode 100644 index 0000000000..1e6230dd98 --- /dev/null +++ b/erpnext/healthcare/doctype/physician_schedule/test_records.json @@ -0,0 +1,8 @@ +[ + { + "schedule_name": "_Testdoctor1 Schedule" + }, + { + "schedule_name": "_Testdoctor2 Schedule" + } +] \ No newline at end of file From 6887cedaeaeea8998bc968d4eef467202fd36463 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 26 Oct 2017 14:34:15 +0530 Subject: [PATCH 37/38] [fix] item qty trigger (#11349) --- .../page/point_of_sale/point_of_sale.js | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index e23dcdbc95..196bb960df 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -208,7 +208,9 @@ erpnext.pos.PointOfSale = class PointOfSale { this.update_item_in_frm(item) .then(() => { // update cart - this.remove_item_from_cart(item); + if (item.qty === 0) { + frappe.model.clear_doc(item.doctype, item.name); + } this.update_cart_data(item); }); }, true); @@ -227,22 +229,18 @@ erpnext.pos.PointOfSale = class PointOfSale { } if (field) { - frappe.model.set_value(item.doctype, item.name, field, value); + return frappe.model.set_value(item.doctype, item.name, field, value) + .then(() => this.frm.script_manager.trigger('qty', item.doctype, item.name)) + .then(() => { + console.log(item.qty, item.amount); + + if (field === 'qty' && item.qty === 0) { + frappe.model.clear_doc(item.doctype, item.name); + } + }) } - return this.frm.script_manager - .trigger('qty', item.doctype, item.name) - .then(() => { - if (field === 'qty') { - this.remove_item_from_cart(item); - } - }); - } - - remove_item_from_cart(item) { - if (item.qty === 0) { - frappe.model.clear_doc(item.doctype, item.name); - } + return Promise.resolve(); } make_payment_modal() { From 819c50c042698e03e2118aea2704f63add334ebd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 26 Oct 2017 15:05:33 +0600 Subject: [PATCH 38/38] bumped to version 9.2.2 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 84b2b0388a..b245f56172 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -4,7 +4,7 @@ import inspect import frappe from erpnext.hooks import regional_overrides -__version__ = '9.2.1' +__version__ = '9.2.2' def get_default_company(user=None): '''Get default company for user'''