diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 0fade8c456..7e9211af9f 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -317,13 +317,13 @@ def make_payment_request(**args): "payment_request_type": args.get("payment_request_type"), "currency": ref_doc.currency, "grand_total": grand_total, - "email_to": args.recipient_id or "", + "email_to": args.recipient_id or ref_doc.owner, "subject": _("Payment Request for {0}").format(args.dn), "message": gateway_account.get("message") or get_dummy_message(ref_doc), "reference_doctype": args.dt, "reference_name": args.dn, - "party_type": args.get("party_type"), - "party": args.get("party"), + "party_type": args.get("party_type") or "Customer", + "party": args.get("party") or ref_doc.customer, "bank_account": bank_account }) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index a3264a4c0f..3111a3a7d5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -499,7 +499,8 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( reference_doctype: me.frm.doctype, reference_name: me.frm.docname, content: __('Reason for hold: ')+data.reason_for_hold, - comment_email: frappe.session.user + comment_email: frappe.session.user, + comment_by: frappe.session.user_fullname }, callback: function(r) { if(!r.exc) { diff --git a/erpnext/communication/desk_page/communication/communication.json b/erpnext/communication/desk_page/communication/communication.json deleted file mode 100644 index 59318fb8cd..0000000000 --- a/erpnext/communication/desk_page/communication/communication.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "cards": [], - "charts": [], - "creation": "2020-01-28 11:49:55.003637", - "developer_mode_only": 0, - "disable_user_customization": 0, - "docstatus": 0, - "doctype": "Desk Page", - "extends_another_page": 0, - "icon": "", - "idx": 0, - "is_standard": 1, - "label": "Communication", - "modified": "2020-03-12 16:30:40.534226", - "modified_by": "Administrator", - "module": "Communication", - "name": "Communication", - "owner": "Administrator", - "pin_to_bottom": 0, - "pin_to_top": 0, - "shortcuts": [] -} \ No newline at end of file diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 88c8dba4c6..fcc9098ba1 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -672,19 +672,32 @@ class BuyingController(StockController): # If asset has to be auto created # Check for asset naming series if item_data.get('asset_naming_series'): + created_assets = [] + for qty in range(cint(d.qty)): - self.make_asset(d) - is_plural = 's' if cint(d.qty) != 1 else '' - messages.append(_('{0} Asset{2} Created for {1}').format(cint(d.qty), d.item_code, is_plural)) + asset = self.make_asset(d) + created_assets.append(asset) + + if len(created_assets) > 5: + # dont show asset form links if more than 5 assets are created + messages.append(_('{} Asset{} created for {}').format(len(created_assets), is_plural, frappe.bold(d.item_code))) + else: + assets_link = list(map(lambda d: frappe.utils.get_link_to_form('Asset', d), created_assets)) + assets_link = frappe.bold(','.join(assets_link)) + + is_plural = 's' if len(created_assets) != 1 else '' + messages.append( + _('Asset{} {assets_link} created for {}').format(is_plural, frappe.bold(d.item_code), assets_link=assets_link) + ) else: - frappe.throw(_("Row {1}: Asset Naming Series is mandatory for the auto creation for item {0}") - .format(d.item_code, d.idx)) + frappe.throw(_("Row {}: Asset Naming Series is mandatory for the auto creation for item {}") + .format(d.idx, frappe.bold(d.item_code))) else: - messages.append(_("Assets not created for {0}. You will have to create asset manually.") - .format(d.item_code)) + messages.append(_("Assets not created for {0}. You will have to create asset manually.") + .format(frappe.bold(d.item_code))) for message in messages: - frappe.msgprint(message, title="Success") + frappe.msgprint(message, title="Success", indicator="green") def make_asset(self, row): if not row.asset_location: @@ -716,6 +729,8 @@ class BuyingController(StockController): asset.set_missing_values() asset.insert() + return asset.name + def update_fixed_asset(self, field, delete_asset = False): for d in self.get("items"): if d.is_fixed_asset: @@ -1026,4 +1041,4 @@ def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty available_batches.append({'batch': batch, 'qty': available_qty}) required_qty -= available_qty - return available_batches \ No newline at end of file + return available_batches diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index d18f8e54d8..163ef72ee1 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -3,6 +3,7 @@ from __future__ import unicode_literals import frappe +import erpnext from frappe.desk.reportview import get_match_cond, get_filters_cond from frappe.utils import nowdate, getdate from collections import defaultdict @@ -129,23 +130,26 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters): }) def tax_account_query(doctype, txt, searchfield, start, page_len, filters): + company_currency = erpnext.get_company_currency(filters.get('company')) + tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount where tabAccount.docstatus!=2 and account_type in (%s) and is_group = 0 and company = %s + and account_currency = %s and `%s` LIKE %s order by idx desc, name limit %s, %s""" % - (", ".join(['%s']*len(filters.get("account_type"))), "%s", searchfield, "%s", "%s", "%s"), - tuple(filters.get("account_type") + [filters.get("company"), "%%%s%%" % txt, + (", ".join(['%s']*len(filters.get("account_type"))), "%s", "%s", searchfield, "%s", "%s", "%s"), + tuple(filters.get("account_type") + [filters.get("company"), company_currency, "%%%s%%" % txt, start, page_len])) if not tax_accounts: tax_accounts = frappe.db.sql("""select name, parent_account from tabAccount where tabAccount.docstatus!=2 and is_group = 0 - and company = %s and `%s` LIKE %s limit %s, %s""" - % ("%s", searchfield, "%s", "%s", "%s"), - (filters.get("company"), "%%%s%%" % txt, start, page_len)) + and company = %s and account_currency = %s and `%s` LIKE %s limit %s, %s""" #nosec + % ("%s", "%s", searchfield, "%s", "%s", "%s"), + (filters.get("company"), company_currency, "%%%s%%" % txt, start, page_len)) return tax_accounts diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index bc007b146f..5299368f42 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -171,7 +171,6 @@ "options": "Customer" }, { - "depends_on": "eval: doc.source==\"Campaign\"", "fieldname": "campaign_name", "fieldtype": "Link", "label": "Campaign Name", @@ -512,4 +511,4 @@ "sort_field": "modified", "sort_order": "DESC", "title_field": "title" -} \ No newline at end of file +} diff --git a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py index 4aad1420e3..3c9758eb84 100644 --- a/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py +++ b/erpnext/patches/v12_0/move_bank_account_swift_number_to_bank.py @@ -4,11 +4,12 @@ import frappe def execute(): frappe.reload_doc('accounts', 'doctype', 'bank', force=1) - frappe.db.sql(""" - UPDATE `tabBank` b, `tabBank Account` ba - SET b.swift_number = ba.swift_number, b.branch_code = ba.branch_code - WHERE b.name = ba.bank - """) + if frappe.db.table_exists('Bank') and frappe.db.table_exists('Bank Account'): + frappe.db.sql(""" + UPDATE `tabBank` b, `tabBank Account` ba + SET b.swift_number = ba.swift_number, b.branch_code = ba.branch_code + WHERE b.name = ba.bank + """) frappe.reload_doc('accounts', 'doctype', 'bank_account') frappe.reload_doc('accounts', 'doctype', 'payment_request') \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 3570a0f2be..5862963496 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -18,7 +18,7 @@ frappe.ui.form.on("Project", { }; }, onload: function (frm) { - var so = frappe.meta.get_docfield("Project", "sales_order"); + var so = frm.get_docfield("Project", "sales_order"); so.get_route_options_for_new_doc = function (field) { if (frm.is_new()) return; return { @@ -135,4 +135,4 @@ function open_form(frm, doctype, child_doctype, parentfield) { frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); -} \ No newline at end of file +} diff --git a/erpnext/regional/report/datev/datev.js b/erpnext/regional/report/datev/datev.js index 1e000b673e..d8638ab05d 100644 --- a/erpnext/regional/report/datev/datev.js +++ b/erpnext/regional/report/datev/datev.js @@ -21,6 +21,12 @@ frappe.query_reports["DATEV"] = { "default": frappe.datetime.now_date(), "fieldtype": "Date", "reqd": 1 + }, + { + "fieldname": "voucher_type", + "label": __("Voucher Type"), + "fieldtype": "Select", + "options": "\nSales Invoice\nPurchase Invoice\nPayment Entry\nExpense Claim\nPayroll Entry\nBank Reconciliation\nAsset\nStock Entry" } ], onload: function(query_report) { diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index e9b42356a2..a6579121cb 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -62,6 +62,7 @@ def get_transactions(filters, as_dict=1): filters -- dict of filters to be passed to the sql query as_dict -- return as list of dicts [0,1] """ + filter_by_voucher = 'AND gl.voucher_type = %(voucher_type)s' if filters.get('voucher_type') else '' gl_entries = frappe.db.sql(""" SELECT @@ -80,8 +81,10 @@ def get_transactions(filters, as_dict=1): gl.posting_date as 'Belegdatum', gl.voucher_no as 'Belegfeld 1', gl.remarks as 'Buchungstext', - gl.against_voucher_type as 'Beleginfo - Art 1', - gl.against_voucher as 'Beleginfo - Inhalt 1' + gl.voucher_type as 'Beleginfo - Art 1', + gl.voucher_no as 'Beleginfo - Inhalt 1', + gl.against_voucher_type as 'Beleginfo - Art 2', + gl.against_voucher as 'Beleginfo - Inhalt 2' FROM `tabGL Entry` gl @@ -109,7 +112,8 @@ def get_transactions(filters, as_dict=1): WHERE gl.company = %(company)s AND DATE(gl.posting_date) >= %(from_date)s AND DATE(gl.posting_date) <= %(to_date)s - ORDER BY 'Belegdatum', gl.voucher_no""", filters, as_dict=as_dict) + {} + ORDER BY 'Belegdatum', gl.voucher_no""".format(filter_by_voucher), filters, as_dict=as_dict) return gl_entries @@ -281,24 +285,24 @@ def get_datev_csv(data, filters, csv_class): def get_header(filters, csv_class): coa = frappe.get_value("Company", filters.get("company"), "chart_of_accounts") - coa_used = "SKR04" if "SKR04" in coa else ("SKR03" if "SKR03" in coa else "") + coa_used = "04" if "SKR04" in coa else ("03" if "SKR03" in coa else "") header = [ # DATEV format - # "DTVF" = created by DATEV software, - # "EXTF" = created by other software + # "DTVF" = created by DATEV software, + # "EXTF" = created by other software '"EXTF"', # version of the DATEV format - # 141 = 1.41, - # 510 = 5.10, - # 720 = 7.20 + # 141 = 1.41, + # 510 = 5.10, + # 720 = 7.20 '700', csv_class.DATA_CATEGORY, '"%s"' % csv_class.FORMAT_NAME, # Format version (regarding format name) csv_class.FORMAT_VERSION, # Generated on - datetime.datetime.now().strftime("%Y%m%d%H%M%S"), + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000', # Imported on -- stays empty '', # Origin. Any two symbols, will be replaced by "SV" on import. @@ -328,13 +332,21 @@ def get_header(filters, csv_class): # R = Diktatkürzel '', # S = Buchungstyp - # 1 = Transaction batch (Finanzbuchführung), - # 2 = Annual financial statement (Jahresabschluss) + # 1 = Transaction batch (Finanzbuchführung), + # 2 = Annual financial statement (Jahresabschluss) '1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', # T = Rechnungslegungszweck - '', + # 0 oder leer = vom Rechnungslegungszweck unabhängig + # 50 = Handelsrecht + # 30 = Steuerrecht + # 64 = IFRS + # 40 = Kalkulatorik + # 11 = Reserviert + # 12 = Reserviert + '0', # U = Festschreibung - '', + # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1" + '0', # V = Default currency, for example, "EUR" '"%s"' % frappe.get_value("Company", filters.get("company"), "default_currency"), # reserviert diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/report/datev/datev_constants.py index a4cd5fc10e..a059ed365a 100644 --- a/erpnext/regional/report/datev/datev_constants.py +++ b/erpnext/regional/report/datev/datev_constants.py @@ -498,13 +498,27 @@ QUERY_REPORT_COLUMNS = [ }, { "label": "Beleginfo - Art 1", - "fieldname": "Beleginfo - Art 2", - "fieldtype": "Data", + "fieldname": "Beleginfo - Art 1", + "fieldtype": "Link", + "options": "DocType" }, { "label": "Beleginfo - Inhalt 1", + "fieldname": "Beleginfo - Inhalt 1", + "fieldtype": "Dynamic Link", + "options": "Beleginfo - Art 1" + }, + { + "label": "Beleginfo - Art 2", + "fieldname": "Beleginfo - Art 2", + "fieldtype": "Link", + "options": "DocType" + }, + { + "label": "Beleginfo - Inhalt 2", "fieldname": "Beleginfo - Inhalt 2", - "fieldtype": "Data", + "fieldtype": "Dynamic Link", + "options": "Beleginfo - Art 2" } ] diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index fa765dfaad..61aa608dd3 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -664,7 +664,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( reference_doctype: me.frm.doctype, reference_name: me.frm.docname, content: __('Reason for hold: ')+data.reason_for_hold, - comment_email: frappe.session.user + comment_email: frappe.session.user, + comment_by: frappe.session.user_fullname }, callback: function(r) { if(!r.exc) { diff --git a/erpnext/setup/desk_page/getting_started/getting_started.json b/erpnext/setup/desk_page/getting_started/getting_started.json index 00236ba5fe..b045b5d083 100644 --- a/erpnext/setup/desk_page/getting_started/getting_started.json +++ b/erpnext/setup/desk_page/getting_started/getting_started.json @@ -41,8 +41,7 @@ "charts": [ { "chart_name": "Bank Balance", - "label": "All Your Money", - "size": "Full" + "label": "Bank Balance" } ], "creation": "2020-01-23 13:46:38.833076", @@ -55,7 +54,7 @@ "idx": 0, "is_standard": 1, "label": "Getting Started", - "modified": "2020-03-12 16:30:37.821762", + "modified": "2020-03-23 11:20:49.161823", "modified_by": "Administrator", "module": "Setup", "name": "Getting Started", @@ -82,6 +81,16 @@ "is_query_report": 0, "link_to": "Sales Invoice", "type": "DocType" + }, + { + "is_query_report": 0, + "link_to": "dashboard", + "type": "Page" + }, + { + "is_query_report": 0, + "link_to": "leaderboard", + "type": "Page" } ] } \ No newline at end of file diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index d97b699a0f..5ad0e13db9 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -8,6 +8,7 @@ from frappe.utils import flt from frappe.model.meta import get_field_precision from frappe.model.document import Document from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos +from erpnext.accounts.doctype.account.account import get_account_currency class LandedCostVoucher(Document): def get_items_from_purchase_receipts(self): @@ -43,6 +44,7 @@ class LandedCostVoucher(Document): else: self.validate_applicable_charges_for_item() self.validate_purchase_receipts() + self.validate_expense_accounts() self.set_total_taxes_and_charges() def check_mandatory(self): @@ -71,6 +73,14 @@ class LandedCostVoucher(Document): frappe.throw(_("Row {0}: Cost center is required for an item {1}") .format(item.idx, item.item_code)) + def validate_expense_accounts(self): + company_currency = erpnext.get_company_currency(self.company) + for account in self.taxes: + if get_account_currency(account.expense_account) != company_currency: + frappe.throw(msg=_(""" Row {0}: Expense account currency should be same as company's default currency. + Please select expense account with account currency as {1}""") + .format(account.idx, frappe.bold(company_currency)), title=_("Invalid Account Currency")) + def set_total_taxes_and_charges(self): self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")])