diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a81cc856255..e61c0d13f6f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '10.1.59' +__version__ = '10.1.60' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 5fa07073337..f3e85b231d2 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -123,7 +123,7 @@ class PaymentRequest(Document): "reference_doctype": "Payment Request", "reference_docname": self.name, "payer_email": self.email_to or frappe.session.user, - "payer_name": data.customer_name, + "payer_name": frappe.safe_decode(data.customer_name), "order_id": self.name, "currency": self.currency }) diff --git a/erpnext/accounts/doctype/subscriber/__init__.py b/erpnext/accounts/doctype/subscriber/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/erpnext/accounts/doctype/subscriber/subscriber.js b/erpnext/accounts/doctype/subscriber/subscriber.js deleted file mode 100644 index f5ea8047c6a..00000000000 --- a/erpnext/accounts/doctype/subscriber/subscriber.js +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt diff --git a/erpnext/accounts/doctype/subscriber/subscriber.json b/erpnext/accounts/doctype/subscriber/subscriber.json deleted file mode 100644 index 3fb7fdb0872..00000000000 --- a/erpnext/accounts/doctype/subscriber/subscriber.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:subscriber_name", - "beta": 0, - "creation": "2018-02-24 11:17:46.809140", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Customer", - "length": 0, - "no_copy": 0, - "options": "Customer", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "customer.customer_name", - "fieldname": "subscriber_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Subscriber Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 1 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-11 15:13:30.056470", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Subscriber", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/accounts/doctype/subscriber/subscriber.py b/erpnext/accounts/doctype/subscriber/subscriber.py deleted file mode 100644 index 03eb0f5ebb0..00000000000 --- a/erpnext/accounts/doctype/subscriber/subscriber.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -from frappe.model.document import Document - -class Subscriber(Document): - pass diff --git a/erpnext/accounts/doctype/subscriber/subscriber_dashboard.py b/erpnext/accounts/doctype/subscriber/subscriber_dashboard.py deleted file mode 100644 index 3120384c6a6..00000000000 --- a/erpnext/accounts/doctype/subscriber/subscriber_dashboard.py +++ /dev/null @@ -1,14 +0,0 @@ -from frappe import _ - -def get_data(): - return { - 'heatmap': True, - 'heatmap_message': _('This is based on transactions against this Subscriber. See timeline below for details'), - 'fieldname': 'subscriber', - 'transactions': [ - { - 'label': _('Subscriptions'), - 'items': ['Subscription'] - } - ] - } \ No newline at end of file diff --git a/erpnext/accounts/doctype/subscriber/test_subscriber.js b/erpnext/accounts/doctype/subscriber/test_subscriber.js deleted file mode 100644 index 1fd4a1e011c..00000000000 --- a/erpnext/accounts/doctype/subscriber/test_subscriber.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Subscriber", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Subscriber - () => frappe.tests.make('Subscriber', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/accounts/doctype/subscriber/test_subscriber.py b/erpnext/accounts/doctype/subscriber/test_subscriber.py deleted file mode 100644 index 3e2fc07c7b2..00000000000 --- a/erpnext/accounts/doctype/subscriber/test_subscriber.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import unittest - -class TestSubscriber(unittest.TestCase): - pass diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json index 39ad0d4a807..c9305164f06 100644 --- a/erpnext/accounts/doctype/subscription/subscription.json +++ b/erpnext/accounts/doctype/subscription/subscription.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, @@ -20,7 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "subscriber", + "fieldname": "customer", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, @@ -29,10 +30,10 @@ "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 0, - "label": "Subscriber", + "label": "Customer", "length": 0, "no_copy": 0, - "options": "Subscriber", + "options": "Customer", "permlevel": 0, "precision": "", "print_hide": 0, @@ -437,6 +438,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "generate_invoice_at_period_start", + "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": "Generate Invoice At Beginning Of Period", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -814,7 +847,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:44.533482", + "modified": "2018-10-14 10:38:55.545540", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", @@ -890,4 +923,4 @@ "track_changes": 1, "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index fe391619602..2bb2c004aff 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -239,7 +239,7 @@ class Subscription(Document): invoice = frappe.new_doc('Sales Invoice') invoice.set_posting_time = 1 invoice.posting_date = self.current_invoice_start - invoice.customer = self.get_customer(self.subscriber) + invoice.customer = self.customer # Subscription is better suited for service items. I won't update `update_stock` # for that reason @@ -282,13 +282,6 @@ class Subscription(Document): return invoice - @staticmethod - def get_customer(subscriber_name): - """ - Returns the `Customer` linked to the `Subscriber` - """ - return frappe.db.get_value('Subscriber', subscriber_name, 'customer') - def get_items_from_plans(self, plans, prorate=0): """ Returns the `Item`s linked to `Subscription Plan` @@ -297,7 +290,7 @@ class Subscription(Document): prorate_factor = get_prorata_factor(self.current_invoice_end, self.current_invoice_start) items = [] - customer = self.get_customer(self.subscriber) + customer = self.customer for plan in plans: item_code = frappe.db.get_value("Subscription Plan", plan.plan, "item") if not prorate: @@ -321,6 +314,23 @@ class Subscription(Document): self.save() + @property + def is_postpaid_to_invoice(self): + return getdate(nowdate()) > getdate(self.current_invoice_end) or \ + (getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and \ + not self.has_outstanding_invoice() + + @property + def is_prepaid_to_invoice(self): + if not self.generate_invoice_at_period_start: + return False + + if self.is_new_subscription(): + return True + + # Check invoice dates and make sure it doesn't have outstanding invoices + return getdate(nowdate()) >= getdate(self.current_invoice_start) and not self.has_outstanding_invoice() + def process_for_active(self): """ Called by `process` if the status of the `Subscription` is 'Active'. @@ -330,7 +340,7 @@ class Subscription(Document): 2. Change the `Subscription` status to 'Past Due Date' 3. Change the `Subscription` status to 'Cancelled' """ - if getdate(nowdate()) > getdate(self.current_invoice_end) or (getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and not self.has_outstanding_invoice(): + if self.is_postpaid_to_invoice or self.is_prepaid_to_invoice: self.generate_invoice() if self.current_invoice_is_past_due(): self.status = 'Past Due Date' @@ -338,7 +348,7 @@ class Subscription(Document): if self.current_invoice_is_past_due() and getdate(nowdate()) > getdate(self.current_invoice_end): self.status = 'Past Due Date' - if self.cancel_at_period_end and getdate(nowdate()) > self.current_invoice_end: + if self.cancel_at_period_end and getdate(nowdate()) > getdate(self.current_invoice_end): self.cancel_subscription_at_period_end() def cancel_subscription_at_period_end(self): diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index c42b8e824b9..a5285ea16a3 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -500,3 +500,51 @@ class TestSubscription(unittest.TestCase): self.assertEqual(invoice.apply_discount_on, 'Grand Total') subscription.delete() + + def test_prepaid_subscriptions(self): + # Create a non pre-billed subscription, processing should not create + # invoices. + subscription = frappe.new_doc('Subscription') + subscription.subscriber = '_Test Customer' + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) + subscription.save() + subscription.process() + + self.assertEqual(len(subscription.invoices), 0) + + # Change the subscription type to prebilled and process it. + # Prepaid invoice should be generated + subscription.generate_invoice_at_period_start = True + subscription.save() + subscription.process() + + self.assertEqual(len(subscription.invoices), 1) + + def test_prepaid_subscriptions_with_prorate_true(self): + settings = frappe.get_single('Subscription Settings') + to_prorate = settings.prorate + settings.prorate = 1 + settings.save() + + subscription = frappe.new_doc('Subscription') + subscription.subscriber = '_Test Customer' + subscription.generate_invoice_at_period_start = True + subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) + subscription.save() + subscription.cancel_subscription() + + self.assertEqual(len(subscription.invoices), 1) + + current_inv = subscription.get_current_invoice() + self.assertEqual(current_inv.status, "Unpaid") + + diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1) + plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1) + prorate_factor = flt(diff / plan_days) + + self.assertEqual(flt(current_inv.grand_total, 2), flt(prorate_factor * 900, 2)) + + settings.prorate = to_prorate + settings.save() + + subscription.delete() diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index 4573a427c1b..2284fcd69e0 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -189,15 +189,15 @@ {% } %}
{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}
\ No newline at end of file +{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}
diff --git a/erpnext/demo/data/employee.json b/erpnext/demo/data/employee.json index d9f4d2a2206..2d2dbe894bc 100644 --- a/erpnext/demo/data/employee.json +++ b/erpnext/demo/data/employee.json @@ -1,72 +1,92 @@ [ - { - "date_of_birth": "1982-01-03", - "date_of_joining": "2001-10-10", - "employee_name": "Dikman Shervashidze Shervashidze", - "gender": "Female", - "user_id": "DikmanShervashidze@example.com" - }, - { - "date_of_birth": "1959-02-03", - "date_of_joining": "1976-09-16", - "employee_name": "Zukutakitoteka", - "gender": "Female", - "user_id": "Zukutakitoteka@example.com" - }, - { - "date_of_birth": "1982-03-03", - "date_of_joining": "2000-06-16", - "employee_name": "Hatsue Kashiwagi", - "gender": "Female", - "user_id": "HatsueKashiwagi@example.com" - }, - { - "date_of_birth": "1945-04-04", - "date_of_joining": "1969-07-01", - "employee_name": "Nuran Verkleij", - "gender": "Female", - "user_id": "NuranVerkleij@example.com" - }, - { - "date_of_birth": "1978-05-03", - "date_of_joining": "1999-12-24", - "employee_name": "\u0414\u043c\u0438\u0442\u0440\u0438\u0439 \u041f\u0438\u0440\u043e\u0433\u043e\u0432", - "gender": "Male", - "user_id": "aromn@example.com" - }, - { - "date_of_birth": "1964-06-03", - "date_of_joining": "1981-08-05", - "employee_name": "Tilde Lindqvist", - "gender": "Female", - "user_id": "TildeLindqvist@example.com" - }, - { - "date_of_birth": "1982-07-03", - "date_of_joining": "2006-06-10", - "employee_name": "Micha\u0142 Sobczak", - "gender": "Male", - "user_id": "MichalSobczak@example.com" - }, - { - "date_of_birth": "1969-08-03", - "date_of_joining": "1993-10-21", - "employee_name": "Gabrielle Loftus", - "gender": "Female", - "user_id": "GabrielleLoftus@example.com" - }, - { - "date_of_birth": "1982-09-03", - "date_of_joining": "2005-09-06", - "employee_name": "Vakhita Ryzaev", - "gender": "Male", - "user_id": "VakhitaRyzaev@example.com" - }, - { - "date_of_birth": "1985-10-03", - "date_of_joining": "2007-12-25", - "employee_name": "Charmaine Gaudreau", - "gender": "Female", - "user_id": "CharmaineGaudreau@example.com" - } + { + "date_of_birth": "1982-01-03", + "date_of_joining": "2001-10-10", + "employee_name": "Diana Prince", + "first_name": "Diana", + "last_name": "Prince", + "gender": "Female", + "user_id": "DianaPrince@example.com" + }, + { + "date_of_birth": "1959-02-03", + "date_of_joining": "1976-09-16", + "employee_name": "Zatanna Zatara", + "gender": "Female", + "user_id": "ZatannaZatara@example.com", + "first_name": "Zatanna", + "last_name": "Zatara" + }, + { + "date_of_birth": "1982-03-03", + "date_of_joining": "2000-06-16", + "employee_name": "Holly Granger", + "gender": "Female", + "user_id": "HollyGranger@example.com", + "first_name": "Holly", + "last_name": "Granger" + }, + { + "date_of_birth": "1945-04-04", + "date_of_joining": "1969-07-01", + "employee_name": "Neptunia Aquaria", + "gender": "Female", + "user_id": "NeptuniaAquaria@example.com", + "first_name": "Neptunia", + "last_name": "Aquaria" + }, + { + "date_of_birth": "1978-05-03", + "date_of_joining": "1999-12-24", + "employee_name": "Arthur Curry", + "gender": "Male", + "user_id": "ArthurCurry@example.com", + "first_name": "Arthur", + "last_name": "Curry" + }, + { + "date_of_birth": "1964-06-03", + "date_of_joining": "1981-08-05", + "employee_name": "Thalia Al Ghul", + "gender": "Female", + "user_id": "ThaliaAlGhul@example.com", + "first_name": "Thalia", + "last_name": "Al Ghul" + }, + { + "date_of_birth": "1982-07-03", + "date_of_joining": "2006-06-10", + "employee_name": "Maxwell Lord", + "gender": "Male", + "user_id": "MaxwellLord@example.com", + "first_name": "Maxwell", + "last_name": "Lord" + }, + { + "date_of_birth": "1969-08-03", + "date_of_joining": "1993-10-21", + "employee_name": "Grace Choi", + "gender": "Female", + "user_id": "GraceChoi@example.com", + "first_name": "Grace", + "last_name": "Choi" + }, + { + "date_of_birth": "1982-09-03", + "date_of_joining": "2005-09-06", + "employee_name": "Vandal Savage", + "gender": "Male", + "user_id": "VandalSavage@example.com", + "first_name": "Vandal", + "last_name": "Savage" + }, + { + "date_of_birth": "1985-10-03", + "date_of_joining": "2007-12-25", + "employee_name": "Caitlin Snow", + "gender": "Female", + "user_id": "CaitlinSnow@example.com", + "first_name": "Caitlin", + "last_name": "Snow" + } ] \ No newline at end of file diff --git a/erpnext/demo/data/item.json b/erpnext/demo/data/item.json index 908de15d0b5..1d4ed343be6 100644 --- a/erpnext/demo/data/item.json +++ b/erpnext/demo/data/item.json @@ -1,337 +1,493 @@ [ - { - "default_supplier": "Asiatic Solutions", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "For Upper Bearing", - "image": "/assets/erpnext_demo/images/disc.png", - "item_code": "Disc Collars", - "item_group": "Raw Material", - "item_name": "Disc Collars" - }, - { - "default_supplier": "Nan Duskin", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "CAST IRON, MCMASTER PART NO. 3710T13", - "image": "/assets/erpnext_demo/images/bearing.jpg", - "item_code": "Bearing Block", - "item_group": "Raw Material", - "item_name": "Bearing Block" - }, - { - "default_supplier": null, - "item_defaults": [{ - "default_warehouse": "Finished Goods" - }], - "description": "Wind Mill C Series for Commercial Use 18ft", - "image": "/assets/erpnext_demo/images/wind-turbine-2.png", - "item_code": "Wind MIll C Series", - "item_group": "Products", - "item_name": "Wind MIll C Series" - }, - { - "default_supplier": null, - "item_defaults": [{ - "default_warehouse": "Finished Goods" - }], - "description": "Wind Mill A Series for Home Use 9ft", - "image": "/assets/erpnext_demo/images/wind-turbine.png", - "item_code": "Wind Mill A Series", - "item_group": "Products", - "item_name": "Wind Mill A Series" - }, - { - "default_supplier": null, - "item_defaults": [{ - "default_warehouse": "Finished Goods" - }], - "description": "Small Wind Turbine for Home Use\n\n\n", - "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg", - "item_code": "Wind Turbine", - "item_group": "Products", - "item_name": "Wind Turbine", - "has_variants": 1, - "has_serial_no": 1, - "attributes":[ - { "attribute": "Size" } - ] - }, - { - "default_supplier": "HomeBase", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "1.5 in. Diameter x 36 in. Mild Steel Tubing", - "image": null, - "item_code": "Bearing Pipe", - "item_group": "Raw Material", - "item_name": "Bearing Pipe" - }, - { - "default_supplier": "New World Realty", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet", - "image": null, - "item_code": "Wing Sheet", - "item_group": "Raw Material", - "item_name": "Wing Sheet" - }, - { - "default_supplier": "Eagle Hardware", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate", - "image": null, - "item_code": "Upper Bearing Plate", - "item_group": "Raw Material", - "item_name": "Upper Bearing Plate" - }, - { - "default_supplier": "Asiatic Solutions", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "Bearing Assembly", - "image": null, - "item_code": "Bearing Assembly", - "item_group": "Sub Assemblies", - "item_name": "Bearing Assembly" - }, - { - "default_supplier": "HomeBase", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "3/4 in. x 2 ft. x 4 ft. Pine Plywood", - "image": null, - "item_code": "Base Plate", - "item_group": "Raw Material", - "item_name": "Base Plate", - "is_sub_contracted_item": 1 - }, - { - "default_supplier": "Scott Ties", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "N/A", - "image": null, - "item_code": "Stand", - "item_group": "Raw Material", - "item_name": "Stand" - }, - { - "default_supplier": "Eagle Hardware", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar", - "image": null, - "item_code": "Bearing Collar", - "item_group": "Raw Material", - "item_name": "Bearing Collar" - }, - { - "default_supplier": "Eagle Hardware", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "1/4 in. x 6 in. x 6 in. Mild Steel Plate", - "image": null, - "item_code": "Base Bearing Plate", - "item_group": "Raw Material", - "item_name": "Base Bearing Plate" - }, - { - "default_supplier": "HomeBase", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing", - "image": null, - "item_code": "External Disc", - "item_group": "Raw Material", - "item_name": "External Disc" - }, - { - "default_supplier": "Eagle Hardware", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "1.25 in. Diameter x 6 ft. Mild Steel Tubing", - "image": null, - "item_code": "Shaft", - "item_group": "Raw Material", - "item_name": "Shaft" - }, - { - "default_supplier": "Ks Merchandise", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "1/2 in. x 2 ft. x 4 ft. Pine Plywood", - "image": null, - "item_code": "Blade Rib", - "item_group": "Raw Material", - "item_name": "Blade Rib" - }, - { - "default_supplier": "HomeBase", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "For Bearing Collar", - "image": null, - "item_code": "Internal Disc", - "item_group": "Raw Material", - "item_name": "Internal Disc" - }, - { - "default_supplier": null, - "item_defaults": [{ - "default_warehouse": "Finished Goods" - }], - "description": "Small Wind Turbine for Home Use\n\n\n\nSize: Small
", - "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg", - "item_code": "Wind Turbine-S", - "item_group": "Products", - "item_name": "Wind Turbine-S", - "variant_of": "Wind Turbine", - "valuation_rate": 300, - "attributes":[ - { - "attribute": "Size", - "attribute_value": "Small" - } - ] - }, - { - "default_supplier": null, - "item_defaults": [{ - "default_warehouse": "Finished Goods" - }], - "description": "Small Wind Turbine for Home Use\n\n\n\nSize: Medium
", - "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg", - "item_code": "Wind Turbine-M", - "item_group": "Products", - "item_name": "Wind Turbine-M", - "variant_of": "Wind Turbine", - "valuation_rate": 300, - "attributes":[ - { - "attribute": "Size", - "attribute_value": "Medium" - } - ] - }, - { - "default_supplier": null, - "item_defaults": [{ - "default_warehouse": "Finished Goods" - }], - "description": "Small Wind Turbine for Home Use\n\n\n\nSize: Large
", - "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg", - "item_code": "Wind Turbine-L", - "item_group": "Products", - "item_name": "Wind Turbine-L", - "variant_of": "Wind Turbine", - "valuation_rate": 300, - "attributes":[ - { - "attribute": "Size", - "attribute_value": "Large" - } - ] - }, - { - "is_stock_item": 0, - "description": "Wind Mill A Series with Spare Bearing", - "item_code": "Wind Mill A Series with Spare Bearing", - "item_group": "Products", - "item_name": "Wind Mill A Series with Spare Bearing" - }, - { - "default_supplier": "HomeBase", - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "3/4 in. x 2 ft. x 4 ft. Pine Plywood", - "image": null, - "item_code": "Base Plate Un Painted", - "item_group": "Raw Material", - "item_name": "Base Plate Un Painted" - }, - { - "is_fixed_asset": 1, - "asset_category": "Furnitures", - "is_stock_item": 0, - "description": "Table", - "item_code": "Table", - "item_name": "Table", - "item_group": "Products" - }, - { - "is_fixed_asset": 1, - "asset_category": "Furnitures", - "is_stock_item": 0, - "description": "Chair", - "item_code": "Chair", - "item_name": "Chair", - "item_group": "Products" - }, - { - "is_fixed_asset": 1, - "asset_category": "Electronic Equipments", - "is_stock_item": 0, - "description": "Computer", - "item_code": "Computer", - "item_name": "Computer", - "item_group": "Products" - }, - { - "is_fixed_asset": 1, - "asset_category": "Electronic Equipments", - "is_stock_item": 0, - "description": "Mobile", - "item_code": "Mobile", - "item_name": "Mobile", - "item_group": "Products" - }, - { - "is_fixed_asset": 1, - "asset_category": "Softwares", - "is_stock_item": 0, - "description": "ERP", - "item_code": "ERP", - "item_name": "ERP", - "item_group": "All Item Groups" - }, - { - "is_fixed_asset": 1, - "asset_category": "Softwares", - "is_stock_item": 0, - "description": "Autocad", - "item_code": "Autocad", - "item_name": "Autocad", - "item_group": "All Item Groups" - }, - { - "is_stock_item": 1, - "has_batch_no": 1, - "create_new_batch": 1, - "valuation_rate": 200, - "item_defaults": [{ - "default_warehouse": "Stores" - }], - "description": "Corrugated Box", - "item_code": "Corrugated Box", - "item_name": "Corrugated Box", - "item_group": "All Item Groups" - } + { + "item_defaults": [ + { + "default_supplier": "Asiatic Solutions", + "default_warehouse": "Stores" + } + ], + "description": "For Upper Bearing", + "image": "/assets/erpnext_demo/images/disc.png", + "item_code": "Disc Collars", + "item_group": "Raw Material", + "item_name": "Disc Collars" + }, + { + "item_defaults": [ + { + "default_supplier": "Nan Duskin", + "default_warehouse": "Stores" + } + ], + "description": "CAST IRON, MCMASTER PART NO. 3710T13", + "image": "/assets/erpnext_demo/images/bearing.jpg", + "item_code": "Bearing Block", + "item_group": "Raw Material", + "item_name": "Bearing Block" + }, + { + "item_defaults": [ + { + "default_supplier": null, + "default_warehouse": "Finished Goods" + } + ], + "description": "Wind Mill C Series for Commercial Use 18ft", + "image": "/assets/erpnext_demo/images/wind-turbine-2.png", + "item_code": "Wind MIll C Series", + "item_group": "Products", + "item_name": "Wind MIll C Series" + }, + { + "item_defaults": [ + { + "default_supplier": null, + "default_warehouse": "Finished Goods" + } + ], + "description": "Wind Mill A Series for Home Use 9ft", + "image": "/assets/erpnext_demo/images/wind-turbine.png", + "item_code": "Wind Mill A Series", + "item_group": "Products", + "item_name": "Wind Mill A Series" + }, + { + "item_defaults": [ + { + "default_supplier": null, + "default_warehouse": "Finished Goods" + } + ], + "description": "Small Wind Turbine for Home Use\n\n\n", + "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg", + "item_code": "Wind Turbine", + "item_group": "Products", + "item_name": "Wind Turbine", + "has_variants": 1, + "has_serial_no": 1, + "attributes": [ + { + "attribute": "Size" + } + ] + }, + { + "item_defaults": [ + { + "default_supplier": "HomeBase", + "default_warehouse": "Stores" + } + ], + "description": "1.5 in. Diameter x 36 in. Mild Steel Tubing", + "image": null, + "item_code": "Bearing Pipe", + "item_group": "Raw Material", + "item_name": "Bearing Pipe" + }, + { + "item_defaults": [ + { + "default_supplier": "New World Realty", + "default_warehouse": "Stores" + } + ], + "description": "1/32 in. x 24 in. x 47 in. HDPE Opaque Sheet", + "image": null, + "item_code": "Wing Sheet", + "item_group": "Raw Material", + "item_name": "Wing Sheet" + }, + { + "item_defaults": [ + { + "default_supplier": "Eagle Hardware", + "default_warehouse": "Stores" + } + ], + "description": "3/16 in. x 6 in. x 6 in. Low Carbon Steel Plate", + "image": null, + "item_code": "Upper Bearing Plate", + "item_group": "Raw Material", + "item_name": "Upper Bearing Plate" + }, + { + "item_defaults": [ + { + "default_supplier": "Asiatic Solutions", + "default_warehouse": "Stores" + } + ], + "description": "Bearing Assembly", + "image": null, + "item_code": "Bearing Assembly", + "item_group": "Sub Assemblies", + "item_name": "Bearing Assembly" + }, + { + "item_defaults": [ + { + "default_supplier": "HomeBase", + "default_warehouse": "Stores" + } + ], + "description": "3/4 in. x 2 ft. x 4 ft. Pine Plywood", + "image": null, + "item_code": "Base Plate", + "item_group": "Raw Material", + "item_name": "Base Plate", + "is_sub_contracted_item": 1 + }, + { + "item_defaults": [ + { + "default_supplier": "Scott Ties", + "default_warehouse": "Stores" + } + ], + "description": "N/A", + "image": null, + "item_code": "Stand", + "item_group": "Raw Material", + "item_name": "Stand" + }, + { + "item_defaults": [ + { + "default_supplier": "Eagle Hardware", + "default_warehouse": "Stores" + } + ], + "description": "1 in. x 3 in. x 1 ft. Multipurpose Al Alloy Bar", + "image": null, + "item_code": "Bearing Collar", + "item_group": "Raw Material", + "item_name": "Bearing Collar" + }, + { + "item_defaults": [ + { + "default_supplier": "Eagle Hardware", + "default_warehouse": "Stores" + } + ], + "description": "1/4 in. x 6 in. x 6 in. Mild Steel Plate", + "image": null, + "item_code": "Base Bearing Plate", + "item_group": "Raw Material", + "item_name": "Base Bearing Plate" + }, + { + "item_defaults": [ + { + "default_supplier": "HomeBase", + "default_warehouse": "Stores" + } + ], + "description": "15/32 in. x 4 ft. x 8 ft. 3-Ply Rtd Sheathing", + "image": null, + "item_code": "External Disc", + "item_group": "Raw Material", + "item_name": "External Disc" + }, + { + "item_defaults": [ + { + "default_supplier": "Eagle Hardware", + "default_warehouse": "Stores" + } + ], + "description": "1.25 in. Diameter x 6 ft. Mild Steel Tubing", + "image": null, + "item_code": "Shaft", + "item_group": "Raw Material", + "item_name": "Shaft" + }, + { + "item_defaults": [ + { + "default_supplier": "Ks Merchandise", + "default_warehouse": "Stores" + } + ], + "description": "1/2 in. x 2 ft. x 4 ft. Pine Plywood", + "image": null, + "item_code": "Blade Rib", + "item_group": "Raw Material", + "item_name": "Blade Rib" + }, + { + "item_defaults": [ + { + "default_supplier": "HomeBase", + "default_warehouse": "Stores" + } + ], + "description": "For Bearing Collar", + "image": null, + "item_code": "Internal Disc", + "item_group": "Raw Material", + "item_name": "Internal Disc" + }, + { + "item_defaults": [ + { + "default_supplier": null, + "default_warehouse": "Finished Goods" + } + ], + "description": "Small Wind Turbine for Home Use\n\n\n\nSize: Small
", + "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg", + "item_code": "Wind Turbine-S", + "item_group": "Products", + "item_name": "Wind Turbine-S", + "variant_of": "Wind Turbine", + "valuation_rate": 300, + "attributes": [ + { + "attribute": "Size", + "attribute_value": "Small" + } + ] + }, + { + "item_defaults": [ + { + "default_supplier": null, + "default_warehouse": "Finished Goods" + } + ], + "description": "Small Wind Turbine for Home Use\n\n\n\nSize: Medium
", + "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg", + "item_code": "Wind Turbine-M", + "item_group": "Products", + "item_name": "Wind Turbine-M", + "variant_of": "Wind Turbine", + "valuation_rate": 300, + "attributes": [ + { + "attribute": "Size", + "attribute_value": "Medium" + } + ] + }, + { + "item_defaults": [ + { + "default_supplier": null, + "default_warehouse": "Finished Goods" + } + ], + "description": "Small Wind Turbine for Home Use\n\n\n\nSize: Large
", + "image": "/assets/erpnext_demo/images/wind-turbine-1.jpg", + "item_code": "Wind Turbine-L", + "item_group": "Products", + "item_name": "Wind Turbine-L", + "variant_of": "Wind Turbine", + "valuation_rate": 300, + "attributes": [ + { + "attribute": "Size", + "attribute_value": "Large" + } + ] + }, + { + "is_stock_item": 0, + "description": "Wind Mill A Series with Spare Bearing", + "item_code": "Wind Mill A Series with Spare Bearing", + "item_group": "Products", + "item_name": "Wind Mill A Series with Spare Bearing" + }, + { + "item_defaults": [ + { + "default_supplier": "HomeBase", + "default_warehouse": "Stores" + } + ], + "description": "3/4 in. x 2 ft. x 4 ft. Pine Plywood", + "image": null, + "item_code": "Base Plate Un Painted", + "item_group": "Raw Material", + "item_name": "Base Plate Un Painted" + }, + { + "is_fixed_asset": 1, + "asset_category": "Furnitures", + "is_stock_item": 0, + "description": "Table", + "item_code": "Table", + "item_name": "Table", + "item_group": "Products" + }, + { + "is_fixed_asset": 1, + "asset_category": "Furnitures", + "is_stock_item": 0, + "description": "Chair", + "item_code": "Chair", + "item_name": "Chair", + "item_group": "Products" + }, + { + "is_fixed_asset": 1, + "asset_category": "Electronic Equipments", + "is_stock_item": 0, + "description": "Computer", + "item_code": "Computer", + "item_name": "Computer", + "item_group": "Products" + }, + { + "is_fixed_asset": 1, + "asset_category": "Electronic Equipments", + "is_stock_item": 0, + "description": "Mobile", + "item_code": "Mobile", + "item_name": "Mobile", + "item_group": "Products" + }, + { + "is_fixed_asset": 1, + "asset_category": "Softwares", + "is_stock_item": 0, + "description": "ERP", + "item_code": "ERP", + "item_name": "ERP", + "item_group": "All Item Groups" + }, + { + "is_fixed_asset": 1, + "asset_category": "Softwares", + "is_stock_item": 0, + "description": "Autocad", + "item_code": "Autocad", + "item_name": "Autocad", + "item_group": "All Item Groups" + }, + { + "is_stock_item": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "valuation_rate": 200, + "item_defaults": [ + { + "default_warehouse": "Stores" + } + ], + "description": "Corrugated Box", + "item_code": "Corrugated Box", + "item_name": "Corrugated Box", + "item_group": "All Item Groups" + }, + { + "item_defaults": [ + { + "default_warehouse": "Finished Goods" + } + ], + "is_stock_item": 1, + "description": "OnePlus 6", + "item_code": "OnePlus 6", + "item_name": "OnePlus 6", + "item_group": "Products", + "domain": "Retail" + }, + { + "item_defaults": [ + { + "default_warehouse": "Finished Goods" + } + ], + "is_stock_item": 1, + "description": "OnePlus 6T", + "item_code": "OnePlus 6T", + "item_name": "OnePlus 6T", + "item_group": "Products", + "domain": "Retail" + }, + { + "item_defaults": [ + { + "default_warehouse": "Finished Goods" + } + ], + "is_stock_item": 1, + "description": "Xiaomi Poco F1", + "item_code": "Xiaomi Poco F1", + "item_name": "Xiaomi Poco F1", + "item_group": "Products", + "domain": "Retail" + }, + { + "item_defaults": [ + { + "default_warehouse": "Finished Goods" + } + ], + "is_stock_item": 1, + "description": "Iphone XS", + "item_code": "Iphone XS", + "item_name": "Iphone XS", + "item_group": "Products", + "domain": "Retail" + }, + { + "item_defaults": [ + { + "default_warehouse": "Finished Goods" + } + ], + "is_stock_item": 1, + "description": "Samsung Galaxy S9", + "item_code": "Samsung Galaxy S9", + "item_name": "Samsung Galaxy S9", + "item_group": "Products", + "domain": "Retail" + }, + { + "item_defaults": [ + { + "default_warehouse": "Finished Goods" + } + ], + "is_stock_item": 1, + "description": "Sony Bluetooth Headphone", + "item_code": "Sony Bluetooth Headphone", + "item_name": "Sony Bluetooth Headphone", + "item_group": "Products", + "domain": "Retail" + }, + { + "is_stock_item": 0, + "description": "Samsung Phone Repair", + "item_code": "Samsung Phone Repair", + "item_name": "Samsung Phone Repair", + "item_group": "Services", + "domain": "Retail" + }, + { + "is_stock_item": 0, + "description": "OnePlus Phone Repair", + "item_code": "OnePlus Phone Repair", + "item_name": "OnePlus Phone Repair", + "item_group": "Services", + "domain": "Retail" + }, + { + "is_stock_item": 0, + "description": "Xiaomi Phone Repair", + "item_code": "Xiaomi Phone Repair", + "item_name": "Xiaomi Phone Repair", + "item_group": "Services", + "domain": "Retail" + }, + { + "is_stock_item": 0, + "description": "Apple Phone Repair", + "item_code": "Apple Phone Repair", + "item_name": "Apple Phone Repair", + "item_group": "Services", + "domain": "Retail" + } ] \ No newline at end of file diff --git a/erpnext/demo/data/user.json b/erpnext/demo/data/user.json index 811e8afa525..9ee5e780ac1 100644 --- a/erpnext/demo/data/user.json +++ b/erpnext/demo/data/user.json @@ -5,104 +5,104 @@ "last_name": "User" }, { - "email": "DikmanShervashidze@example.com", - "first_name": "Dikman", - "last_name": "Shervashidze" + "email": "DianaPrince@example.com", + "first_name": "Diana", + "last_name": "Prince" }, { - "email": "Zukutakitoteka@example.com", - "first_name": "Zukutakitoteka", - "last_name": null + "email": "ZatannaZatara@example.com", + "first_name": "Zatanna", + "last_name": "Zatara" }, { - "email": "HatsueKashiwagi@example.com", - "first_name": "Hatsue", - "last_name": "Kashiwagi" + "email": "HollyGranger@example.com", + "first_name": "Holly", + "last_name": "Granger" }, { - "email": "NuranVerkleij@example.com", - "first_name": "Nuran", - "last_name": "Verkleij" + "email": "NeptuniaAquaria@example.com", + "first_name": "Neptunia", + "last_name": "Aquaria" }, { - "email": "aromn@example.com", - "first_name": "Arom", - "last_name": "Nolan" + "email": "ArthurCurry@example.com", + "first_name": "Arthur", + "last_name": "Curry" }, { - "email": "TildeLindqvist@example.com", - "first_name": "Tilde", - "last_name": "Lindqvist" + "email": "ThaliaAlGhul@example.com", + "first_name": "Thalia", + "last_name": "Al Ghul" }, { - "email": "MichalSobczak@example.com", - "first_name": "Micha\u0142", - "last_name": "Sobczak" + "email": "MaxwellLord@example.com", + "first_name": "Maxwell", + "last_name": "Lord" }, { - "email": "GabrielleLoftus@example.com", - "first_name": "Gabrielle", - "last_name": "Loftus" + "email": "GraceChoi@example.com", + "first_name": "Grace", + "last_name": "Choi" }, { - "email": "VakhitaRyzaev@example.com", - "first_name": "Vakhita", - "last_name": "Ryzaev" + "email": "VandalSavage@example.com", + "first_name": "Vandal", + "last_name": "Savage" }, { - "email": "CharmaineGaudreau@example.com", - "first_name": "Charmaine", - "last_name": "Gaudreau" + "email": "CaitlinSnow@example.com", + "first_name": "Caitlin", + "last_name": "Snow" }, { - "email": "RafaelaMaartens@example.com", - "first_name": "Rafa\u00ebla", - "last_name": "Maartens" + "email": "RipHunter@example.com", + "first_name": "Rip", + "last_name": "Hunter" }, { - "email": "NuguseYohannes@example.com", - "first_name": "Nuguse", - "last_name": "Yohannes" + "email": "NicholasFury@example.com", + "first_name": "Nicholas", + "last_name": "Fury" }, { - "email": "panca@example.com", - "first_name": "\u0420\u0430\u0438\u0441\u0430", - "last_name": "\u0411\u0435\u043b\u044f\u043a\u043e\u0432\u0430" + "email": "PeterParker@example.com", + "first_name": "Peter", + "last_name": "Parker" }, { - "email": "CaYinLong@example.com", - "first_name": "\u80e4\u9686", - "last_name": "\u8521" + "email": "JohnConstantine@example.com", + "first_name": "John", + "last_name": "Constantine" }, { - "email": "FreddieScott@example.com", - "first_name": "Freddie", - "last_name": "Scott" + "email": "HalJordan@example.com", + "first_name": "Hal", + "last_name": "Jordan" }, { - "email": "BergoraVigfusdottir@example.com", - "first_name": "Berg\u00fe\u00f3ra", - "last_name": "Vigf\u00fasd\u00f3ttir" + "email": "VictorStone@example.com", + "first_name": "Victor", + "last_name": "Stone" }, { - "email": "WardNajmalDinKalb@example.com", - "first_name": "Ward", - "last_name": "Kalb" + "email": "BruceWayne@example.com", + "first_name": "Bruce", + "last_name": "Wayne" }, { - "email": "WanMai@example.com", - "first_name": "Wan", - "last_name": "Mai" + "email": "ClarkKent@example.com", + "first_name": "Clark", + "last_name": "Kent" }, { - "email": "LeonAbdulov@example.com", - "first_name": "Leon", - "last_name": "Abdulov" + "email": "BarryAllen@example.com", + "first_name": "Barry", + "last_name": "Allen" }, { - "email": "SabinaNovotna@example.com", - "first_name": "Sabina", - "last_name": "Novotn\u00e1" + "email": "KaraZorEl@example.com", + "first_name": "Kara", + "last_name": "Zor El" }, { "email": "demo@erpnext.com", diff --git a/erpnext/demo/demo.py b/erpnext/demo/demo.py index f765469499d..e89b6895a08 100644 --- a/erpnext/demo/demo.py +++ b/erpnext/demo/demo.py @@ -3,8 +3,9 @@ from __future__ import unicode_literals import frappe, sys import erpnext import frappe.utils -from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset, education -from erpnext.demo.setup import education, manufacture, setup_data, healthcare +from erpnext.demo.user import hr, sales, purchase, manufacturing, stock, accounts, projects, fixed_asset +from erpnext.demo.user import education as edu +from erpnext.demo.setup import education, manufacture, setup_data, healthcare, retail """ Make a demo @@ -28,6 +29,8 @@ def make(domain='Manufacturing', days=100): setup_data.setup(domain) if domain== 'Manufacturing': manufacture.setup_data() + elif domain == "Retail": + retail.setup_data() elif domain== 'Education': education.setup_data() elif domain== 'Healthcare': @@ -77,13 +80,13 @@ def simulate(domain='Manufacturing', days=100): stock.work() accounts.work() projects.run_projects(current_date) + sales.work(domain) # run_messages() if domain=='Manufacturing': - sales.work() manufacturing.work() elif domain=='Education': - education.work() + edu.work() except: frappe.db.set_global('demo_last_date', current_date) diff --git a/erpnext/demo/domains.py b/erpnext/demo/domains.py index 5ad3e04013b..d5c2bfd2f02 100644 --- a/erpnext/demo/domains.py +++ b/erpnext/demo/domains.py @@ -5,7 +5,7 @@ data = { 'company_name': 'Wind Power LLC' }, 'Retail': { - 'company_name': 'Annapurna Dairy Shop', + 'company_name': 'Mobile Next', }, 'Distribution': { 'company_name': 'Soltice Hardware', diff --git a/erpnext/demo/setup/retail.py b/erpnext/demo/setup/retail.py new file mode 100644 index 00000000000..82d1c15ea3e --- /dev/null +++ b/erpnext/demo/setup/retail.py @@ -0,0 +1,65 @@ +from __future__ import unicode_literals + +import random, json +import frappe +from frappe.utils import nowdate, add_days +from erpnext.demo.setup.setup_data import import_json +from erpnext.demo.domains import data + +from six import iteritems + +def setup_data(): + setup_item() + setup_item_price() + frappe.db.commit() + frappe.clear_cache() + +def setup_item(): + items = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data', 'item.json')).read()) + for i in items: + if not i.get("domain") == "Retail": continue + item = frappe.new_doc('Item') + item.update(i) + if hasattr(item, 'item_defaults') and item.item_defaults[0].default_warehouse: + item.item_defaults[0].company = data.get("Retail").get('company_name') + warehouse = frappe.get_all('Warehouse', filters={'warehouse_name': item.item_defaults[0].default_warehouse}, limit=1) + if warehouse: + item.item_defaults[0].default_warehouse = warehouse[0].name + item.insert() + +def setup_item_price(): + frappe.db.sql("delete from `tabItem Price`") + + standard_selling = { + "OnePlus 6": 579, + "OnePlus 6T": 600, + "Xiaomi Poco F1": 300, + "Iphone XS": 999, + "Samsung Galaxy S9": 720, + "Sony Bluetooth Headphone": 99, + "Xiaomi Phone Repair": 10, + "Samsung Phone Repair": 20, + "OnePlus Phone Repair": 15, + "Apple Phone Repair": 30, + } + + standard_buying = { + "OnePlus 6": 300, + "OnePlus 6T": 350, + "Xiaomi Poco F1": 200, + "Iphone XS": 600, + "Samsung Galaxy S9": 500, + "Sony Bluetooth Headphone": 69 + } + + for price_list in ("standard_buying", "standard_selling"): + for item, rate in iteritems(locals().get(price_list)): + frappe.get_doc({ + "doctype": "Item Price", + "price_list": price_list.replace("_", " ").title(), + "item_code": item, + "selling": 1 if price_list=="standard_selling" else 0, + "buying": 1 if price_list=="standard_buying" else 0, + "price_list_rate": rate, + "currency": "USD" + }).insert() diff --git a/erpnext/demo/setup/setup_data.py b/erpnext/demo/setup/setup_data.py index 911d41838ea..0fd7bb7ac8a 100644 --- a/erpnext/demo/setup/setup_data.py +++ b/erpnext/demo/setup/setup_data.py @@ -5,6 +5,7 @@ import frappe, erpnext from frappe.utils.nestedset import get_root_of from frappe.utils import flt, now_datetime, cstr, random_string from frappe.utils.make_random import add_random_children, get_random +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from erpnext.demo.domains import data from frappe import _ @@ -14,9 +15,10 @@ def setup(domain): setup_fiscal_year() setup_holiday_list() setup_user() - #setup_employee() + setup_employee() setup_user_roles() setup_role_permissions() + setup_custom_field_for_domain() employees = frappe.get_all('Employee', fields=['name', 'date_of_joining']) @@ -120,7 +122,7 @@ def setup_user(): user = frappe.new_doc("User") user.update(u) user.flags.no_welcome_mail = True - user.new_password = 'demo' + user.new_password = 'Demo1234567!!!' user.insert() def setup_employee(): @@ -136,6 +138,8 @@ def setup_employee(): salary_component.save() import_json('Employee') + holiday_list = frappe.db.get_value("Holiday List", {"holiday_list_name": str(now_datetime().year)}, 'name') + frappe.db.sql('''update tabEmployee set holiday_list={0}'''.format(holiday_list)) def setup_salary_structure(employees, salary_slip_based_on_timesheet=0): ss = frappe.new_doc('Salary Structure') @@ -166,12 +170,16 @@ def setup_salary_structure(employees, salary_slip_based_on_timesheet=0): "idx": 1 }) ss.insert() + ss.submit() for e in employees: sa = frappe.new_doc("Salary Structure Assignment") sa.employee = e.name + sa.salary_structure = ss.name sa.from_date = "2015-01-01" sa.base = random.random() * 10000 + sa.insert() + sa.submit() return ss @@ -184,52 +192,63 @@ def setup_user_roles(): 'Nursing User', 'Patient') if not frappe.db.get_global('demo_hr_user'): - user = frappe.get_doc('User', 'CharmaineGaudreau@example.com') + user = frappe.get_doc('User', 'CaitlinSnow@example.com') user.add_roles('HR User', 'HR Manager', 'Accounts User') frappe.db.set_global('demo_hr_user', user.name) + update_employee_department(user.name, 'Human Resources') + for d in frappe.get_all('User Permission', filters={"user": "CaitlinSnow@example.com"}): + frappe.delete_doc('User Permission', d.name) if not frappe.db.get_global('demo_sales_user_1'): - user = frappe.get_doc('User', 'VakhitaRyzaev@example.com') + user = frappe.get_doc('User', 'VandalSavage@example.com') user.add_roles('Sales User') + update_employee_department(user.name, 'Sales') frappe.db.set_global('demo_sales_user_1', user.name) if not frappe.db.get_global('demo_sales_user_2'): - user = frappe.get_doc('User', 'GabrielleLoftus@example.com') + user = frappe.get_doc('User', 'GraceChoi@example.com') user.add_roles('Sales User', 'Sales Manager', 'Accounts User') + update_employee_department(user.name, 'Sales') frappe.db.set_global('demo_sales_user_2', user.name) if not frappe.db.get_global('demo_purchase_user'): - user = frappe.get_doc('User', 'MichalSobczak@example.com') + user = frappe.get_doc('User', 'MaxwellLord@example.com') user.add_roles('Purchase User', 'Purchase Manager', 'Accounts User', 'Stock User') + update_employee_department(user.name, 'Purchase') frappe.db.set_global('demo_purchase_user', user.name) if not frappe.db.get_global('demo_manufacturing_user'): - user = frappe.get_doc('User', 'NuranVerkleij@example.com') + user = frappe.get_doc('User', 'NeptuniaAquaria@example.com') user.add_roles('Manufacturing User', 'Stock User', 'Purchase User', 'Accounts User') + update_employee_department(user.name, 'Production') frappe.db.set_global('demo_manufacturing_user', user.name) if not frappe.db.get_global('demo_stock_user'): - user = frappe.get_doc('User', 'HatsueKashiwagi@example.com') + user = frappe.get_doc('User', 'HollyGranger@example.com') user.add_roles('Manufacturing User', 'Stock User', 'Purchase User', 'Accounts User') + update_employee_department(user.name, 'Production') frappe.db.set_global('demo_stock_user', user.name) if not frappe.db.get_global('demo_accounts_user'): - user = frappe.get_doc('User', 'LeonAbdulov@example.com') + user = frappe.get_doc('User', 'BarryAllen@example.com') user.add_roles('Accounts User', 'Accounts Manager', 'Sales User', 'Purchase User') + update_employee_department(user.name, 'Accounts') frappe.db.set_global('demo_accounts_user', user.name) if not frappe.db.get_global('demo_projects_user'): - user = frappe.get_doc('User', 'panca@example.com') + user = frappe.get_doc('User', 'PeterParker@example.com') user.add_roles('HR User', 'Projects User') + update_employee_department(user.name, 'Management') frappe.db.set_global('demo_projects_user', user.name) if not frappe.db.get_global('demo_education_user'): - user = frappe.get_doc('User', 'aromn@example.com') + user = frappe.get_doc('User', 'ArthurCurry@example.com') user.add_roles('Academics User') + update_employee_department(user.name, 'Management') frappe.db.set_global('demo_education_user', user.name) #Add Expense Approver - user = frappe.get_doc('User', 'WanMai@example.com') + user = frappe.get_doc('User', 'ClarkKent@example.com') user.add_roles('Expense Approver') def setup_leave_allocation(): @@ -403,3 +422,19 @@ def import_json(doctype, submit=False, values=None): frappe.db.commit() frappe.flags.in_import = False + +def update_employee_department(user_id, department): + employee = frappe.db.get_value('Employee', {"user_id": user_id}, 'name') + department = frappe.db.get_value('Department', {'department_name': department}, 'name') + frappe.db.set_value('Employee', employee, 'department', department) + +def setup_custom_field_for_domain(): + field = { + "Item": [ + dict(fieldname='domain', label='Domain', + fieldtype='Select', hidden=1, default="Manufacturing", + options="Manufacturing\nService\nDistribution\nRetail" + ) + ] + } + create_custom_fields(field) diff --git a/erpnext/demo/user/accounts.py b/erpnext/demo/user/accounts.py index a12933bcfb1..18855041edd 100644 --- a/erpnext/demo/user/accounts.py +++ b/erpnext/demo/user/accounts.py @@ -56,7 +56,7 @@ def work(): if random.random() < 0.5: make_payment_entries("Purchase Invoice", "Accounts Payable") - if random.random() < 0.1: + if random.random() < 0.4: #make payment request against sales invoice sales_invoice_name = get_random("Sales Invoice", filters={"docstatus": 1}) if sales_invoice_name: diff --git a/erpnext/demo/user/hr.py b/erpnext/demo/user/hr.py index b8016869cf1..79f3c192878 100644 --- a/erpnext/demo/user/hr.py +++ b/erpnext/demo/user/hr.py @@ -14,34 +14,31 @@ from erpnext.hr.doctype.leave_application.leave_application import (get_leave_ba def work(): frappe.set_user(frappe.db.get_global('demo_hr_user')) year, month = frappe.flags.current_date.strftime("%Y-%m").split("-") + setup_department_approvers() mark_attendance() make_leave_application() # payroll entry if not frappe.db.sql('select name from `tabSalary Slip` where month(adddate(start_date, interval 1 month))=month(curdate())'): - # process payroll for previous month - payroll_entry = frappe.new_doc("Payroll Entry") - payroll_entry.company = frappe.flags.company - payroll_entry.payroll_frequency = 'Monthly' - - # select a posting date from the previous month - payroll_entry.posting_date = get_last_day(getdate(frappe.flags.current_date) - datetime.timedelta(days=10)) - payroll_entry.payment_account = frappe.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name") - - payroll_entry.set_start_end_dates() - # based on frequency + payroll_entry = get_payroll_entry() payroll_entry.salary_slip_based_on_timesheet = 0 + payroll_entry.save() payroll_entry.create_salary_slips() payroll_entry.submit_salary_slips() payroll_entry.make_accrual_jv_entry() + payroll_entry.submit() # payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date, # reference_number=random_string(10)) + # based on timesheet + payroll_entry = get_payroll_entry() payroll_entry.salary_slip_based_on_timesheet = 1 + payroll_entry.save() payroll_entry.create_salary_slips() payroll_entry.submit_salary_slips() payroll_entry.make_accrual_jv_entry() + payroll_entry.submit() # payroll_entry.make_journal_entry(reference_date=frappe.flags.current_date, # reference_number=random_string(10)) @@ -55,12 +52,14 @@ def work(): expense_claim.company = frappe.flags.company expense_claim.payable_account = get_payable_account(expense_claim.company) expense_claim.posting_date = frappe.flags.current_date - expense_claim.insert() + expense_claim.expense_approver = frappe.db.get_global('demo_hr_user') + expense_claim.save() rand = random.random() if rand < 0.4: update_sanctioned_amount(expense_claim) + expense_claim.approval_status = 'Approved' expense_claim.submit() if random.randint(0, 1): @@ -72,6 +71,19 @@ def work(): je.flags.ignore_permissions = 1 je.submit() +def get_payroll_entry(): + # process payroll for previous month + payroll_entry = frappe.new_doc("Payroll Entry") + payroll_entry.company = frappe.flags.company + payroll_entry.payroll_frequency = 'Monthly' + + # select a posting date from the previous month + payroll_entry.posting_date = get_last_day(getdate(frappe.flags.current_date) - datetime.timedelta(days=10)) + payroll_entry.payment_account = frappe.get_value('Account', {'account_type': 'Cash', 'company': erpnext.get_default_company(),'is_group':0}, "name") + + payroll_entry.set_start_end_dates() + return payroll_entry + def get_expenses(): expenses = [] expese_types = frappe.db.sql("""select ect.name, eca.default_account from `tabExpense Claim Type` ect, @@ -114,7 +126,7 @@ def get_timesheet_based_salary_slip_employee(): def make_timesheet_records(): employees = get_timesheet_based_salary_slip_employee() for e in employees: - ts = make_timesheet(e.employee, simulate = True, billable = 1, activity_type=get_random("Activity Type")) + ts = make_timesheet(e.employee, simulate = True, billable = 1, activity_type=get_random("Activity Type"), company=frappe.flags.company) frappe.db.commit() rand = random.random() @@ -195,3 +207,11 @@ def mark_attendance(): attendance.save() attendance.submit() frappe.db.commit() + +def setup_department_approvers(): + for d in frappe.get_all('Department', filters={'department_name': ['!=', 'All Departments']}): + doc = frappe.get_doc('Department', d.name) + doc.append("leave_approvers", {'approver': frappe.session.user}) + doc.append("expense_approvers", {'approver': frappe.session.user}) + doc.flags.ignore_mandatory = True + doc.save() diff --git a/erpnext/demo/user/manufacturing.py b/erpnext/demo/user/manufacturing.py index 0231eda947d..a28d061f382 100644 --- a/erpnext/demo/user/manufacturing.py +++ b/erpnext/demo/user/manufacturing.py @@ -4,25 +4,32 @@ from __future__ import unicode_literals import frappe, random, erpnext +from datetime import timedelta from frappe.utils.make_random import how_many from frappe.desk import query_report from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record def work(): + if random.random() < 0.3: return + frappe.set_user(frappe.db.get_global('demo_manufacturing_user')) + if not frappe.get_all('Sales Order'): return from erpnext.projects.doctype.timesheet.timesheet import OverlapError - ppt = frappe.get_doc("Production Planning Tool", "Production Planning Tool") + ppt = frappe.new_doc("Production Plan") ppt.company = erpnext.get_default_company() - ppt.use_multi_level_bom = 1 + # ppt.use_multi_level_bom = 1 #refactored ppt.get_items_from = "Sales Order" - ppt.purchase_request_for_warehouse = "Stores - WPL" + # ppt.purchase_request_for_warehouse = "Stores - WPL" # refactored ppt.run_method("get_open_sales_orders") + if not ppt.get("sales_orders"): return ppt.run_method("get_items") - ppt.run_method("raise_work_orders") ppt.run_method("raise_material_requests") + ppt.save() + ppt.submit() + ppt.run_method("raise_work_orders") frappe.db.commit() # submit work orders @@ -39,12 +46,12 @@ def work(): frappe.db.commit() # stores -> wip - if random.random() < 0.3: + if random.random() < 0.4: for pro in query_report.run("Open Work Orders")["result"][:how_many("Stock Entry for WIP")]: make_stock_entry_from_pro(pro[0], "Material Transfer for Manufacture") # wip -> fg - if random.random() < 0.3: + if random.random() < 0.4: for pro in query_report.run("Work Orders in Progress")["result"][:how_many("Stock Entry for FG")]: make_stock_entry_from_pro(pro[0], "Manufacture") @@ -55,17 +62,9 @@ def work(): stock_uom = frappe.db.get_value('Item', bom.item, 'stock_uom'), planned_start_date = frappe.flags.current_date) - # submit time logs - for timesheet in frappe.get_all("Timesheet", ["name"], {"docstatus": 0, - "work_order": ("!=", ""), "to_time": ("<", frappe.flags.current_date)}): - timesheet = frappe.get_doc("Timesheet", timesheet.name) - try: - timesheet.submit() - frappe.db.commit() - except OverlapError: - pass - except WorkstationHolidayError: - pass + # submit job card + if random.random() < 0.4: + submit_job_cards() def make_stock_entry_from_pro(pro_id, purpose): from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry @@ -86,3 +85,27 @@ def make_stock_entry_from_pro(pro_id, purpose): except (NegativeStockError, IncorrectValuationRateError, DuplicateEntryForWorkOrderError, OperationsNotCompleteError): frappe.db.rollback() + +def submit_job_cards(): + work_orders = frappe.get_all("Work Order", ["name", "creation"], {"docstatus": 1, "status": "Not Started"}) + work_order = random.choice(work_orders) + # for work_order in work_orders: + start_date = work_order.creation + work_order = frappe.get_doc("Work Order", work_order.name) + job = frappe.get_all("Job Card", ["name", "operation", "work_order"], + {"docstatus": 0, "work_order": work_order.name}) + + if not job: return + job_map = {} + for d in job: + job_map[d.operation] = frappe.get_doc("Job Card", d.name) + + for operation in work_order.operations: + job = job_map[operation.operation] + job.actual_start_date = start_date + minutes = operation.get("time_in_mins") + random_minutes = random.randint(int(minutes/2), minutes) + job.actual_end_date = job.actual_start_date + timedelta(minutes=random_minutes) + start_date = job.actual_end_date + job.save() + job.submit() diff --git a/erpnext/demo/user/projects.py b/erpnext/demo/user/projects.py index 98024476793..ee87199796e 100644 --- a/erpnext/demo/user/projects.py +++ b/erpnext/demo/user/projects.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals -import frappe +import frappe, erpnext from frappe.utils import flt from frappe.utils.make_random import get_random from erpnext.projects.doctype.timesheet.test_timesheet import make_timesheet @@ -19,7 +19,7 @@ def run_projects(current_date): def make_timesheet_for_projects(current_date ): for data in frappe.get_all("Task", ["name", "project"], {"status": "Open", "exp_end_date": ("<", current_date)}): employee = get_random("Employee") - ts = make_timesheet(employee, simulate = True, billable = 1, + ts = make_timesheet(employee, simulate = True, billable = 1, company = erpnext.get_default_company(), activity_type=get_random("Activity Type"), project=data.project, task =data.name) if flt(ts.total_billable_amount) > 0.0: diff --git a/erpnext/demo/user/purchase.py b/erpnext/demo/user/purchase.py index ad99de907a2..69930448dc9 100644 --- a/erpnext/demo/user/purchase.py +++ b/erpnext/demo/user/purchase.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals -import frappe, random +import frappe, random, json, erpnext from frappe.utils.make_random import how_many, get_random from frappe.desk import query_report from erpnext.setup.utils import get_exchange_rate @@ -16,14 +16,14 @@ from erpnext.buying.doctype.request_for_quotation.request_for_quotation import \ def work(): frappe.set_user(frappe.db.get_global('demo_purchase_user')) - if random.random() < 0.3: + if random.random() < 0.6: report = "Items To Be Requested" for row in query_report.run(report)["result"][:random.randint(1, 5)]: item_code, qty = row[0], abs(row[-1]) mr = make_material_request(item_code, qty) - if random.random() < 0.3: + if random.random() < 0.6: for mr in frappe.get_all('Material Request', filters={'material_request_type': 'Purchase', 'status': 'Open'}, limit=random.randint(1,6)): @@ -36,7 +36,7 @@ def work(): rfq.submit() # Make suppier quotation from RFQ against each supplier. - if random.random() < 0.3: + if random.random() < 0.6: for rfq in frappe.get_all('Request for Quotation', filters={'status': 'Open'}, limit=random.randint(1, 6)): if not frappe.get_all('Supplier Quotation', @@ -51,15 +51,15 @@ def work(): # get supplier details supplier = get_random("Supplier") - company_currency = frappe.get_cached_value('Company', "Wind Power LLC", "default_currency") - party_account_currency = get_party_account_currency("Supplier", supplier, "Wind Power LLC") + company_currency = frappe.get_cached_value('Company', erpnext.get_default_company(), "default_currency") + party_account_currency = get_party_account_currency("Supplier", supplier, erpnext.get_default_company()) if company_currency == party_account_currency: exchange_rate = 1 else: exchange_rate = get_exchange_rate(party_account_currency, company_currency, args="for_buying") # make supplier quotations - if random.random() < 0.2: + if random.random() < 0.5: from erpnext.stock.doctype.material_request.material_request import make_supplier_quotation report = "Material Requests for which Supplier Quotations are not created" @@ -80,16 +80,20 @@ def work(): report = "Requested Items To Be Ordered" for row in query_report.run(report)["result"][:how_many("Purchase Order")]: if row[0] != "'Total'": - po = frappe.get_doc(make_purchase_order(row[0])) - po.supplier = supplier - po.currency = party_account_currency or company_currency - po.conversion_rate = exchange_rate - po.transaction_date = frappe.flags.current_date - po.insert() - po.submit() - frappe.db.commit() + try: + po = frappe.get_doc(make_purchase_order(row[0])) + po.supplier = supplier + po.currency = party_account_currency or company_currency + po.conversion_rate = exchange_rate + po.transaction_date = frappe.flags.current_date + po.insert() + po.submit() + except Exception: + pass + else: + frappe.db.commit() - if random.random() < 0.2: + if random.random() < 0.5: make_subcontract() def make_material_request(item_code, qty): @@ -122,13 +126,14 @@ def add_suppliers(rfq): rfq.append("suppliers", { "supplier": supplier }) def make_subcontract(): - from erpnext.buying.doctype.purchase_order.purchase_order import make_stock_entry + from erpnext.buying.doctype.purchase_order.purchase_order import make_rm_stock_entry item_code = get_random("Item", {"is_sub_contracted_item": 1}) if item_code: # make sub-contract PO po = frappe.new_doc("Purchase Order") po.is_subcontracted = "Yes" po.supplier = get_random("Supplier") + po.transaction_date = frappe.flags.current_date # added po.schedule_date = frappe.utils.add_days(frappe.flags.current_date, 7) item_code = get_random("Item", {"is_sub_contracted_item": 1}) @@ -150,7 +155,20 @@ def make_subcontract(): make_material_request(po.items[0].item_code, po.items[0].qty) # transfer material for sub-contract - stock_entry = frappe.get_doc(make_stock_entry(po.name, po.items[0].item_code)) + rm_items = get_rm_item(po.items[0], po.supplied_items[0]) + stock_entry = frappe.get_doc(make_rm_stock_entry(po.name, json.dumps([rm_items]))) stock_entry.from_warehouse = "Stores - WPL" stock_entry.to_warehouse = "Supplier - WPL" stock_entry.insert() + +def get_rm_item(items, supplied_items): + return { + "item_code": items.get("item_code"), + "rm_item_code": supplied_items.get("rm_item_code"), + "item_name": supplied_items.get("rm_item_code"), + "qty": supplied_items.get("required_qty") + random.randint(3,10), + "amount": supplied_items.get("amount"), + "warehouse": supplied_items.get("reserve_warehouse"), + "rate": supplied_items.get("rate"), + "stock_uom": supplied_items.get("stock_uom") + } diff --git a/erpnext/demo/user/sales.py b/erpnext/demo/user/sales.py index 65d7f555588..69ba9007a61 100644 --- a/erpnext/demo/user/sales.py +++ b/erpnext/demo/user/sales.py @@ -3,22 +3,23 @@ from __future__ import unicode_literals -import frappe, random +import frappe, random, erpnext from frappe.utils import flt from frappe.utils.make_random import add_random_children, get_random from erpnext.setup.utils import get_exchange_rate from erpnext.accounts.party import get_party_account_currency from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request, make_payment_entry -def work(): +def work(domain="Manufacturing"): frappe.set_user(frappe.db.get_global('demo_sales_user_2')) - if random.random() < 0.5: - for i in range(random.randint(1,7)): - make_opportunity() - if random.random() < 0.5: - for i in range(random.randint(1,3)): - make_quotation() + for i in range(random.randint(1,7)): + if random.random() < 0.5: + make_opportunity(domain) + + for i in range(random.randint(1,3)): + if random.random() < 0.5: + make_quotation(domain) # lost quotations / inquiries if random.random() < 0.3: @@ -32,24 +33,27 @@ def work(): if opportunity and opportunity.status in ('Open', 'Replied'): opportunity.declare_enquiry_lost('Did not ask') - if random.random() < 0.3: - for i in range(random.randint(1,3)): + for i in range(random.randint(1,3)): + if random.random() < 0.6: make_sales_order() - if random.random() < 0.1: + if random.random() < 0.5: #make payment request against Sales Order sales_order_name = get_random("Sales Order", filters={"docstatus": 1}) - if sales_order_name: - so = frappe.get_doc("Sales Order", sales_order_name) - if flt(so.per_billed) != 100: - payment_request = make_payment_request(dt="Sales Order", dn=so.name, recipient_id=so.contact_email, - submit_doc=True, mute_email=True, use_dummy_message=True) + try: + if sales_order_name: + so = frappe.get_doc("Sales Order", sales_order_name) + if flt(so.per_billed) != 100: + payment_request = make_payment_request(dt="Sales Order", dn=so.name, recipient_id=so.contact_email, + submit_doc=True, mute_email=True, use_dummy_message=True) - payment_entry = frappe.get_doc(make_payment_entry(payment_request.name)) - payment_entry.posting_date = frappe.flags.current_date - payment_entry.submit() + payment_entry = frappe.get_doc(make_payment_entry(payment_request.name)) + payment_entry.posting_date = frappe.flags.current_date + payment_entry.submit() + except Exception: + pass -def make_opportunity(): +def make_opportunity(domain): b = frappe.get_doc({ "doctype": "Opportunity", "enquiry_from": "Customer", @@ -61,13 +65,13 @@ def make_opportunity(): add_random_children(b, "items", rows=4, randomize = { "qty": (1, 5), - "item_code": ("Item", {"has_variants": 0, "is_fixed_asset": 0}) + "item_code": ("Item", {"has_variants": 0, "is_fixed_asset": 0, "domain": domain}) }, unique="item_code") b.insert() frappe.db.commit() -def make_quotation(): +def make_quotation(domain): # get open opportunites opportunity = get_random("Opportunity", {"status": "Open", "with_items": 1}) @@ -84,8 +88,8 @@ def make_quotation(): # get customer, currency and exchange_rate customer = get_random("Customer") - company_currency = frappe.get_cached_value('Company', "Wind Power LLC", "default_currency") - party_account_currency = get_party_account_currency("Customer", customer, "Wind Power LLC") + company_currency = frappe.get_cached_value('Company', erpnext.get_default_company(), "default_currency") + party_account_currency = get_party_account_currency("Customer", customer, erpnext.get_default_company()) if company_currency == party_account_currency: exchange_rate = 1 else: @@ -104,7 +108,7 @@ def make_quotation(): add_random_children(qtn, "items", rows=3, randomize = { "qty": (1, 5), - "item_code": ("Item", {"has_variants": "0", "is_fixed_asset": 0}) + "item_code": ("Item", {"has_variants": "0", "is_fixed_asset": 0, "domain": domain}) }, unique="item_code") qtn.insert() @@ -115,8 +119,8 @@ def make_quotation(): def make_sales_order(): q = get_random("Quotation", {"status": "Submitted"}) if q: - from erpnext.selling.doctype.quotation.quotation import make_sales_order - so = frappe.get_doc(make_sales_order(q)) + from erpnext.selling.doctype.quotation.quotation import make_sales_order as mso + so = frappe.get_doc(mso(q)) so.transaction_date = frappe.flags.current_date so.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10) so.insert() diff --git a/erpnext/demo/user/stock.py b/erpnext/demo/user/stock.py index a6b0e0061d1..6036a2621f9 100644 --- a/erpnext/demo/user/stock.py +++ b/erpnext/demo/user/stock.py @@ -3,7 +3,7 @@ from __future__ import print_function, unicode_literals -import frappe, random +import frappe, random, erpnext from frappe.desk import query_report from erpnext.stock.stock_ledger import NegativeStockError from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError, SerialNoQtyError @@ -45,7 +45,7 @@ def make_delivery_note(): # make purchase requests # make delivery notes (if possible) - if random.random() < 0.3: + if random.random() < 0.6: from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note report = "Ordered Items To Be Delivered" for so in list(set([r[0] for r in query_report.run(report)["result"] @@ -56,8 +56,9 @@ def make_delivery_note(): if not d.expense_account: d.expense_account = ("Cost of Goods Sold - {0}".format( frappe.get_cached_value('Company', dn.company, 'abbr'))) - dn.insert() + try: + dn.insert() dn.submit() frappe.db.commit() except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError, UnableToSelectBatchError): @@ -68,9 +69,10 @@ def make_stock_reconciliation(): from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation \ import OpeningEntryAccountError, EmptyStockReconciliationItemsError - if random.random() < 0.1: + if random.random() < 0.4: stock_reco = frappe.new_doc("Stock Reconciliation") stock_reco.posting_date = frappe.flags.current_date + stock_reco.company = erpnext.get_default_company() stock_reco.get_items_for("Stores - WP") if stock_reco.items: for item in stock_reco.items: @@ -87,7 +89,7 @@ def make_stock_reconciliation(): def submit_draft_stock_entries(): from erpnext.stock.doctype.stock_entry.stock_entry import IncorrectValuationRateError, \ - DuplicateEntryForProductionOrderError, OperationsNotCompleteError + DuplicateEntryForWorkOrderError, OperationsNotCompleteError # try posting older drafts (if exists) frappe.db.commit() @@ -98,7 +100,7 @@ def submit_draft_stock_entries(): ste.save() ste.submit() frappe.db.commit() - except (NegativeStockError, IncorrectValuationRateError, DuplicateEntryForProductionOrderError, + except (NegativeStockError, IncorrectValuationRateError, DuplicateEntryForWorkOrderError, OperationsNotCompleteError): frappe.db.rollback() diff --git a/erpnext/hooks.py b/erpnext/hooks.py index fa3e9dbdfdd..226a76610bd 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -12,7 +12,7 @@ app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" develop_version = '12.x.x-develop' -staging_version = '11.0.3-beta.11' +staging_version = '11.0.3-beta.12' error_report_email = "support@erpnext.com" diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b2c3e75b8f1..0eab9826c32 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -368,7 +368,8 @@ class BOM(WebsiteGenerator): bom_list = self.traverse_tree(bom_list) for bom in bom_list: bom_obj = frappe.get_doc("BOM", bom) - bom_obj.on_update() + bom_obj.check_recursion() + bom_obj.update_exploded_items() return bom_list diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index 3f6cb44c490..59861ceba3a 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -62,7 +62,7 @@ class BOMUpdateTool(Document): bom_list.append(d[0]) self.get_parent_boms(d[0], bom_list) - return bom_list + return list(set(bom_list)) @frappe.whitelist() def enqueue_replace_bom(args): diff --git a/erpnext/patches/v11_0/remove_subscriber_doctype.py b/erpnext/patches/v11_0/remove_subscriber_doctype.py new file mode 100644 index 00000000000..4e50c35cd2a --- /dev/null +++ b/erpnext/patches/v11_0/remove_subscriber_doctype.py @@ -0,0 +1,15 @@ +import frappe +from frappe.model.utils.rename_field import rename_field + +def execute(): + """ copy subscribe field to customer """ + frappe.reload_doc("accounts","doctype","subscription") + + if frappe.db.exists("DocType", "Subscriber"): + if frappe.db.has_column('Subscription','subscriber'): + frappe.db.sql(""" + update `tabSubscription` s1 + set customer=(select customer from tabSubscriber where name=s1.subscriber) + """) + + frappe.delete_doc("DocType", "Subscriber") \ No newline at end of file diff --git a/erpnext/projects/doctype/project_user/project_user.json b/erpnext/projects/doctype/project_user/project_user.json index a7cc810a0a6..596681676f7 100644 --- a/erpnext/projects/doctype/project_user/project_user.json +++ b/erpnext/projects/doctype/project_user/project_user.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 0, "allow_rename": 0, "beta": 0, @@ -11,16 +12,21 @@ "editable_grid": 1, "fields": [ { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "user", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 1, + "in_standard_filter": 0, "label": "User", "length": 0, "no_copy": 0, @@ -30,23 +36,30 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, "search_index": 1, "set_only_once": 0, + "translatable": 0, "unique": 0 }, { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, + "columns": 0, "fieldname": "welcome_email_sent", "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": "Welcome email sent", "length": 0, "no_copy": 0, @@ -55,24 +68,58 @@ "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, + "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, "search_index": 0, "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 1, + "fieldname": "view_attachments", + "fieldtype": "Check", + "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": "View attachments", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "idx": 0, "image_view": 0, "in_create": 0, - "is_submittable": 0, "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-07-11 03:28:04.756894", + "modified": "2018-09-09 12:39:38.376816", "modified_by": "Administrator", "module": "Projects", "name": "Project User", @@ -82,7 +129,10 @@ "quick_entry": 0, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_seen": 0 + "track_changes": 0, + "track_seen": 0, + "track_views": 0 } \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py index 9b905f8512a..bf01363bd65 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -25,6 +25,10 @@ def get_data(): { 'label': _('Pricing'), 'items': ['Pricing Rule'] + }, + { + 'label': _('Subscriptions'), + 'items': ['Subscription'] } ] } \ No newline at end of file diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py index 88977921142..180fd092cb5 100644 --- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py +++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import msgprint, _ from frappe.utils import flt +from erpnext import get_company_currency def execute(filters=None): if not filters: filters = {} @@ -14,12 +15,14 @@ def execute(filters=None): item_details = get_item_details() data = [] + company_currency = get_company_currency(filters["company"]) + for d in entries: if d.stock_qty > 0 or filters.get('show_return_entries', 0): data.append([ d.name, d.customer, d.territory, item_details.get(d.item_code, {}).get("website_warehouse"), d.posting_date, d.item_code, item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"), - d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt + d.stock_qty, d.base_net_amount, d.sales_person, d.allocated_percentage, d.contribution_amt, company_currency ]) if data: @@ -32,13 +35,105 @@ def get_columns(filters): if not filters.get("doc_type"): msgprint(_("Please select the document type first"), raise_exception=1) - return [filters["doc_type"] + ":Link/" + filters["doc_type"] + ":140", - _("Customer") + ":Link/Customer:140", _("Territory") + ":Link/Territory:100", _("Warehouse") + ":Link/Warehouse:100", - _("Posting Date") + ":Date:100", - _("Item Code") + ":Link/Item:120", _("Item Group") + ":Link/Item Group:120", - _("Brand") + ":Link/Brand:120", _("Qty") + ":Float:100", _("Amount") + ":Currency:120", - _("Sales Person") + ":Link/Sales Person:140", _("Contribution %") + "::110", - _("Contribution Amount") + ":Currency:140"] + columns = [ + { + "label": _(filters["doc_type"]), + "options": filters["doc_type"], + "fieldname": frappe.scrub(filters['doc_type']), + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Customer"), + "options": "Customer", + "fieldname": "customer", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Territory"), + "options": "Territory", + "fieldname": "territory", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Warehouse"), + "options": "Warehouse", + "fieldname": "warehouse", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Posting Date"), + "fieldname": "posting_date", + "fieldtype": "Date", + "width": 140 + }, + { + "label": _("Item Code"), + "options": "Item", + "fieldname": "item_code", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Item Group"), + "options": "Item Group", + "fieldname": "item_group", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Brand"), + "options": "Brand", + "fieldname": "brand", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Qty"), + "fieldname": "qty", + "fieldtype": "Float", + "width": 140 + }, + { + "label": _("Amount"), + "options": "currency", + "fieldname": "amount", + "fieldtype": "Currency", + "width": 140 + }, + { + "label": _("Sales Person"), + "options": "Sales Person", + "fieldname": "sales_person", + "fieldtype": "Link", + "width": 140 + }, + { + "label": _("Contribution %"), + "fieldname": "contribution", + "fieldtype": "Float", + "width": 140 + }, + { + "label": _("Contribution Amount"), + "options": "currency", + "fieldname": "contribution_amt", + "fieldtype": "Currency", + "width": 140 + }, + { + "label":_("Currency"), + "options": "Currency", + "fieldname":"currency", + "fieldtype":"Link", + "hidden" : 1 + } + ] + + return columns def get_entries(filters): date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date" diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 75e7590d829..9620b313ed2 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -272,13 +272,13 @@ erpnext.stock.delivery_note.set_print_hide = function(doc, cdt, cdn){ var dn_item_fields = frappe.meta.docfield_map['Delivery Note Item']; var dn_fields_copy = dn_fields; var dn_item_fields_copy = dn_item_fields; - if (doc.print_without_amount) { dn_fields['currency'].print_hide = 1; dn_item_fields['rate'].print_hide = 1; dn_item_fields['discount_percentage'].print_hide = 1; dn_item_fields['price_list_rate'].print_hide = 1; dn_item_fields['amount'].print_hide = 1; + dn_item_fields['discount_amount'].print_hide = 1; dn_fields['taxes'].print_hide = 1; } else { if (dn_fields_copy['currency'].print_hide != 1) @@ -287,6 +287,8 @@ erpnext.stock.delivery_note.set_print_hide = function(doc, cdt, cdn){ dn_item_fields['rate'].print_hide = 0; if (dn_item_fields_copy['amount'].print_hide != 1) dn_item_fields['amount'].print_hide = 0; + if (dn_item_fields_copy['discount_amount'].print_hide != 1) + dn_item_fields['discount_amount'].print_hide = 0; if (dn_fields_copy['taxes'].print_hide != 1) dn_fields['taxes'].print_hide = 0; } diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 6fec6121c8c..ab624d17db1 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -74,7 +74,7 @@ class DeliveryNote(SellingController): item_meta = frappe.get_meta("Delivery Note Item") print_hide_fields = { "parent": ["grand_total", "rounded_total", "in_words", "currency", "total", "taxes"], - "items": ["rate", "amount", "price_list_rate", "discount_percentage"] + "items": ["rate", "amount", "discount_amount", "price_list_rate", "discount_percentage"] } for key, fieldname in print_hide_fields.items(): diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 363db39a4de..3b20b1cf648 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -254,7 +254,7 @@ class StockReconciliation(StockController): def get_items_for(self, warehouse): self.items = [] - for item in get_items(warehouse, self.posting_date, self.posting_time): + for item in get_items(warehouse, self.posting_date, self.posting_time, self.company): self.append("items", item) def submit(self): diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.py b/erpnext/stock/report/item_price_stock/item_price_stock.py index 30fcad8073c..e539aff59c8 100644 --- a/erpnext/stock/report/item_price_stock/item_price_stock.py +++ b/erpnext/stock/report/item_price_stock/item_price_stock.py @@ -13,12 +13,18 @@ def execute(filters=None): def get_columns(): return [ { - "label": _("Item Name"), - "fieldname": "item_name", + "label": _("Item Code"), + "fieldname": "item_code", "fieldtype": "Link", "options": "Item", "width": 120 }, + { + "label": _("Item Name"), + "fieldname": "item_name", + "fieldtype": "Data", + "width": 120 + }, { "label": _("Brand"), "fieldname": "brand", @@ -76,7 +82,7 @@ def get_item_price_qty_data(filters): if filters.get("item_code"): conditions += "where a.item_code=%(item_code)s" - item_results = frappe.db.sql("""select a.item_code as item_name, a.name as price_list_name, + item_results = frappe.db.sql("""select a.item_code, a.item_name, a.name as price_list_name, a.brand as brand, b.warehouse as warehouse, b.actual_qty as actual_qty from `tabItem Price` a left join `tabBin` b ON a.item_code = b.item_code @@ -92,6 +98,7 @@ def get_item_price_qty_data(filters): if item_results: for item_dict in item_results: data = { + 'item_code': item_dict.item_code, 'item_name': item_dict.item_name, 'brand': item_dict.brand, 'warehouse': item_dict.warehouse, diff --git a/erpnext/templates/includes/projects/project_timesheets.html b/erpnext/templates/includes/projects/project_timesheets.html index 66f47713d24..fb3806cd986 100644 --- a/erpnext/templates/includes/projects/project_timesheets.html +++ b/erpnext/templates/includes/projects/project_timesheets.html @@ -2,16 +2,16 @@