diff --git a/erpnext/accounts/doctype/account/account.json b/erpnext/accounts/doctype/account/account.json index ce038772c1..8fc7b55aa9 100644 --- a/erpnext/accounts/doctype/account/account.json +++ b/erpnext/accounts/doctype/account/account.json @@ -17,6 +17,7 @@ "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "", @@ -41,6 +42,7 @@ "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "length": 0, @@ -64,6 +66,7 @@ "fieldtype": "Data", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 1, "in_list_view": 1, "label": "Account Name", @@ -90,6 +93,7 @@ "fieldtype": "Check", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Is Group", @@ -114,6 +118,7 @@ "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 1, "in_list_view": 0, "label": "Company", @@ -140,6 +145,7 @@ "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Root Type", @@ -164,6 +170,7 @@ "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Report Type", @@ -189,6 +196,7 @@ "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Currency", @@ -214,6 +222,7 @@ "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "length": 0, @@ -237,6 +246,7 @@ "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 1, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Parent Account", @@ -264,6 +274,7 @@ "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 1, "in_list_view": 0, "label": "Account Type", @@ -271,7 +282,7 @@ "no_copy": 0, "oldfieldname": "account_type", "oldfieldtype": "Select", - "options": "\nBank\nCash\nDepreciation\nTax\nChargeable\nWarehouse\nReceivable\nPayable\nEquity\nFixed Asset\nCost of Goods Sold\nExpense Account\nRound Off\nIncome Account\nStock Received But Not Billed\nExpenses Included In Valuation\nStock Adjustment\nStock\nTemporary", + "options": "\nAccumulated Depreciation\nBank\nCash\nChargeable\nCost of Goods Sold\nDepreciation\nEquity\nExpense Account\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nTax\nTemporary\nWarehouse", "permlevel": 0, "print_hide": 0, "print_hide_if_no_value": 0, @@ -291,6 +302,7 @@ "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Rate", @@ -317,6 +329,7 @@ "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Frozen", @@ -343,6 +356,7 @@ "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Warehouse", @@ -367,6 +381,7 @@ "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Balance must be", @@ -391,6 +406,7 @@ "fieldtype": "Int", "hidden": 1, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Lft", @@ -414,6 +430,7 @@ "fieldtype": "Int", "hidden": 1, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Rgt", @@ -437,6 +454,7 @@ "fieldtype": "Data", "hidden": 1, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Old Parent", @@ -463,7 +481,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2015-12-12 10:19:54.365839", + "modified": "2016-03-31 05:15:51.062604", "modified_by": "Administrator", "module": "Accounts", "name": "Account", @@ -572,5 +590,7 @@ ], "read_only": 0, "read_only_onload": 0, - "search_fields": "" + "search_fields": "", + "sort_order": "ASC", + "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py index 532a0151a1..ee1d007593 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py @@ -53,7 +53,9 @@ def get(): _("Plant and Machinery"): { "account_type": "Fixed Asset" }, - _("Accumulated Depreciations"): {} + _("Accumulated Depreciation"): { + "account_type": "Accumulated Depreciation" + } }, _("Investments"): { "is_group": 1 @@ -89,7 +91,7 @@ def get(): "account_type": "Expense Account" }, _("Depreciation"): { - "account_type": "Expense Account" + "account_type": "Depreciation" }, _("Entertainment Expenses"): { "account_type": "Expense Account" diff --git a/erpnext/accounts/doctype/asset/asset.json b/erpnext/accounts/doctype/asset/asset.json index bf46074a05..b23b472b98 100644 --- a/erpnext/accounts/doctype/asset/asset.json +++ b/erpnext/accounts/doctype/asset/asset.json @@ -108,7 +108,7 @@ "print_hide_if_no_value": 0, "read_only": 1, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -576,14 +576,14 @@ ], "hide_heading": 0, "hide_toolbar": 0, - "idx": 0, + "idx": 72, "in_create": 0, "in_dialog": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-03-30 05:25:56.710106", + "modified": "2016-03-31 05:02:49.890116", "modified_by": "Administrator", "module": "Accounts", "name": "Asset", @@ -609,26 +609,6 @@ "share": 1, "submit": 1, "write": 1 - }, - { - "amend": 1, - "apply_user_permissions": 0, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 } ], "read_only": 0, diff --git a/erpnext/accounts/doctype/asset/asset.py b/erpnext/accounts/doctype/asset/asset.py index d6547a6347..3b141f63ef 100644 --- a/erpnext/accounts/doctype/asset/asset.py +++ b/erpnext/accounts/doctype/asset/asset.py @@ -10,60 +10,60 @@ from frappe.model.document import Document class Asset(Document): def validate(self): - self.set_status() + self.status = self.get_status() self.validate_fixed_asset_item() self.validate_asset_values() self.set_depreciation_settings() self.make_depreciation_schedule() self.validate_depreciation_settings_in_company() - + def on_submit(self): self.set_status() - + def on_cancel(self): self.validate_cancellation() self.delete_depreciation_entries() self.set_status() - + def validate_fixed_asset_item(self): item = frappe.get_doc("Item", self.item_code) if item.disabled: frappe.throw(_("Item {0} has been disabled").format(self.item_code)) if item.is_stock_item: frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code)) - + def validate_asset_values(self): if flt(self.expected_value_after_useful_life) >= flt(self.gross_purchase_amount): frappe.throw(_("Expected Value After Useful Life must be less than Gross Purchase Amount")) - + if not flt(self.gross_purchase_amount): frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError) - + if not self.current_value and self.next_depreciation_date: self.current_value = flt(self.gross_purchase_amount) else: if flt(self.current_value) > flt(self.gross_purchase_amount): frappe.throw(_("Current Value After Depreciation must be less than equal to {0}") .format(flt(self.gross_purchase_amount))) - + def set_depreciation_settings(self): asset_category = frappe.get_doc("Asset Category", self.asset_category) - + for field in ("depreciation_method", "number_of_depreciations", "number_of_months_in_a_period"): if not self.get(field): self.set(field, asset_category.get(field)) - + def make_depreciation_schedule(self): self.schedules = [] if not self.get("schedules") and self.next_depreciation_date: accumulated_depreciation = 0 value_after_depreciation = flt(self.current_value) for n in xrange(self.number_of_depreciations): - schedule_date = add_months(self.next_depreciation_date, + schedule_date = add_months(self.next_depreciation_date, n * cint(self.number_of_months_in_a_period)) - + depreciation_amount = self.get_depreciation_amount(value_after_depreciation) - + self.append("schedules", { "schedule_date": schedule_date, "depreciation_amount": depreciation_amount, @@ -71,10 +71,10 @@ class Asset(Document): }) accumulated_depreciation += flt(depreciation_amount) value_after_depreciation -= flt(depreciation_amount) - + def get_depreciation_amount(self, depreciable_value): if self.depreciation_method == "Straight Line": - depreciation_amount = (flt(self.current_value) - + depreciation_amount = (flt(self.current_value) - flt(self.expected_value_after_useful_life)) / cint(self.number_of_depreciations) else: factor = 200.0 / cint(self.number_of_depreciations) @@ -85,45 +85,51 @@ class Asset(Document): depreciation_amount = flt(depreciable_value) - flt(self.expected_value_after_useful_life) return depreciation_amount - + def validate_cancellation(self): if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"): frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status)) - + if self.purchase_invoice: frappe.throw(_("Please cancel Purchase Invoice {0} first").format(self.purchase_invoice)) - + def delete_depreciation_entries(self): total_depreciation_amount = 0 for d in self.get("schedules"): if d.journal_entry: frappe.get_doc("Journal Entry", d.journal_entry).cancel() - + d.db_set("journal_entry", None) total_depreciation_amount += flt(d.depreciation_amount) self.db_set("current_value", (self.current_value + total_depreciation_amount)) - + def validate_depreciation_settings_in_company(self): company = frappe.get_doc("Company", self.company) - for field in ("accumulated_depreciation_account", "depreciation_expense_account", + for field in ("accumulated_depreciation_account", "depreciation_expense_account", "disposal_account", "depreciation_cost_center"): if not company.get(field): frappe.throw(_("Please set {0} in Company {1}") .format(company.meta.get_label(field), self.company)) - - def set_status(self, status=None): - if not status: - if self.docstatus == 0: - status = "Draft" - elif self.docstatus == 1: - status = "Submitted" - if self.journal_entry_for_scrap: - status = "Scrapped" - elif flt(self.current_value) <= flt(self.expected_value_after_useful_life): - status = "Fully Depreciated" - elif flt(self.current_value) < flt(self.gross_purchase_amount): - status = 'Partially Depreciated' - elif self.docstatus == 2: - status = "Cancelled" - self.db_set("status", status) \ No newline at end of file + def set_status(self, status=None): + '''Set asset and update status''' + if not status: + status = self.get_status() + self.db_set("status", status) + + def get_status(self): + '''Returns status based on whether it is draft, submitted, scrapped or depreciated''' + if self.docstatus == 0: + status = "Draft" + elif self.docstatus == 1: + status = "Submitted" + if self.journal_entry_for_scrap: + status = "Scrapped" + elif flt(self.current_value) <= flt(self.expected_value_after_useful_life): + status = "Fully Depreciated" + elif flt(self.current_value) < flt(self.gross_purchase_amount): + status = 'Partially Depreciated' + elif self.docstatus == 2: + status = "Cancelled" + + return status diff --git a/erpnext/controllers/recurring_document.py b/erpnext/controllers/recurring_document.py index 42b847e72f..a0367608d2 100644 --- a/erpnext/controllers/recurring_document.py +++ b/erpnext/controllers/recurring_document.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals import frappe +import calendar import frappe.utils import frappe.defaults @@ -166,14 +167,24 @@ def validate_recurring_document(doc): elif not (doc.from_date and doc.to_date): frappe.throw(_("Period From and Period To dates mandatory for recurring {0}").format(doc.doctype)) - + def validate_recurring_next_date(doc): posting_date = doc.get("posting_date") or doc.get("transaction_date") if getdate(posting_date) > getdate(doc.next_date): frappe.throw(_("Next Date must be greater than Posting Date")) - if getdate(doc.next_date).day != doc.repeat_on_day_of_month: - frappe.throw(_("Next Date's day and Repeat on Day of Month must be equal")) + next_date = getdate(doc.next_date) + if next_date.day != doc.repeat_on_day_of_month: + + # if the repeat day is the last day of the month (31) + # and the current month does not have as many days, + # then the last day of the current month is a valid date + lastday = calendar.monthrange(next_date.year, next_date.month)[1] + if doc.repeat_on_day_of_month < lastday: + + # the specified day of the month is not same as the day specified + # or the last day of the month + frappe.throw(_("Next Date's day and Repeat on Day of Month must be equal")) def convert_to_recurring(doc, posting_date): if doc.is_recurring: @@ -181,13 +192,13 @@ def convert_to_recurring(doc, posting_date): doc.db_set("recurring_id", doc.name) set_next_date(doc, posting_date) - + if doc.next_date: validate_recurring_next_date(doc) - + elif doc.recurring_id: doc.db_set("recurring_id", None) - + def validate_notification_email_id(doc): if doc.notify_by_email: if doc.notification_email_address: diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 6689d66748..97a689adb4 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -30,16 +30,16 @@ class Company(Document): self.validate_abbr() self.validate_default_accounts() self.validate_currency() - + def validate_abbr(self): self.abbr = self.abbr.strip() - + if self.get('__islocal') and len(self.abbr) > 5: frappe.throw(_("Abbreviation cannot have more than 5 characters")) if not self.abbr.strip(): frappe.throw(_("Abbreviation is mandatory")) - + if frappe.db.sql("select abbr from tabCompany where name!=%s and abbr=%s", (self.name, self.abbr)): frappe.throw(_("Abbreviation already used for another company")) @@ -111,6 +111,8 @@ class Company(Document): self._set_default_account("default_cash_account", "Cash") self._set_default_account("default_bank_account", "Bank") self._set_default_account("round_off_account", "Round Off") + self._set_default_account("accumulated_depreciation_account", "Accumulated Depreciation") + self._set_default_account("depreciation_expense_account", "Depreciation") if cint(frappe.db.get_single_value("Accounts Settings", "auto_accounting_for_stock")): self._set_default_account("stock_received_but_not_billed", "Stock Received But Not Billed") @@ -159,6 +161,7 @@ class Company(Document): frappe.db.set(self, "cost_center", _("Main") + " - " + self.abbr) frappe.db.set(self, "round_off_cost_center", _("Main") + " - " + self.abbr) + frappe.db.set(self, "depreciation_cost_center", _("Main") + " - " + self.abbr) def before_rename(self, olddn, newdn, merge=False): if merge: diff --git a/erpnext/shopping_cart/test_shopping_cart.py b/erpnext/shopping_cart/test_shopping_cart.py index 9b0278de9d..17fc73e3a1 100644 --- a/erpnext/shopping_cart/test_shopping_cart.py +++ b/erpnext/shopping_cart/test_shopping_cart.py @@ -191,7 +191,8 @@ class TestShoppingCart(unittest.TestCase): frappe.set_user("test_cart_user@example.com") def login_as_customer(self): - self.create_user_if_not_exists("test_contact_customer@example.com") + self.create_user_if_not_exists("test_contact_customer@example.com", + "_Test Contact For _Test Customer") frappe.set_user("test_contact_customer@example.com") def remove_all_items_from_cart(self): @@ -199,7 +200,7 @@ class TestShoppingCart(unittest.TestCase): quotation.set("items", []) quotation.save(ignore_permissions=True) - def create_user_if_not_exists(self, email): + def create_user_if_not_exists(self, email, first_name = None): if frappe.db.exists("User", email): return @@ -208,7 +209,7 @@ class TestShoppingCart(unittest.TestCase): "user_type": "Website User", "email": email, "send_welcome_email": 0, - "first_name": email.split("@")[0] + "first_name": first_name or email.split("@")[0] }).insert(ignore_permissions=True) test_dependencies = ["Sales Taxes and Charges Template", "Price List", "Item Price", "Shipping Rule", "Currency Exchange",