diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
index d098d8421c..43acded3a9 100644
--- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
+++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py
@@ -19,6 +19,11 @@ def get(chart_name = None, chart = None, no_cache = None, from_date = None, to_d
else:
chart = frappe._dict(frappe.parse_json(chart))
timespan = chart.timespan
+
+ if chart.timespan == 'Select Date Range':
+ from_date = chart.from_date
+ to_date = chart.to_date
+
timegrain = chart.time_interval
filters = frappe.parse_json(chart.filters_json)
@@ -88,7 +93,8 @@ def get_gl_entries(account, to_date):
fields = ['posting_date', 'debit', 'credit'],
filters = [
dict(posting_date = ('<', to_date)),
- dict(account = ('in', child_accounts))
+ dict(account = ('in', child_accounts)),
+ dict(voucher_type = ('!=', 'Period Closing Voucher'))
],
order_by = 'posting_date asc')
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 32485a3469..62a8f05c65 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -174,6 +174,8 @@ def make_gl_entries(doc, credit_account, debit_account, against,
# GL Entry for crediting the amount in the deferred expense
from erpnext.accounts.general_ledger import make_gl_entries
+ if amount == 0: return
+
gl_entries = []
gl_entries.append(
doc.get_gl_dict({
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index af51fc5d8e..522ed4ffa4 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -24,6 +24,11 @@ class AccountingDimension(Document):
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg)
+ exists = frappe.db.get_value("Accounting Dimension", {'document_type': self.document_type}, ['name'])
+
+ if exists and self.is_new():
+ frappe.throw("Document Type already used as a dimension")
+
def after_insert(self):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
@@ -60,7 +65,8 @@ def make_dimension_in_accounting_doctypes(doc):
"label": doc.label,
"fieldtype": "Link",
"options": doc.document_type,
- "insert_after": insert_after_field
+ "insert_after": insert_after_field,
+ "owner": "Administrator"
}
if doctype == "Budget":
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
index 3222aeb085..2473d715d0 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
@@ -15,8 +15,8 @@ class AccountsSettings(Document):
frappe.clear_cache()
def validate(self):
- for f in ["add_taxes_from_item_tax_template"]:
- frappe.db.set_default(f, self.get(f, ""))
+ frappe.db.set_default("add_taxes_from_item_tax_template",
+ self.get("add_taxes_from_item_tax_template", 0))
self.validate_stale_days()
self.enable_payment_schedule_in_print()
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 11d847d821..221e3a7280 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -570,7 +570,7 @@ $.extend(erpnext.journal_entry, {
},
{fieldtype: "Date", fieldname: "posting_date", label: __("Date"), reqd: 1,
default: frm.doc.posting_date},
- {fieldtype: "Small Text", fieldname: "user_remark", label: __("User Remark"), reqd: 1},
+ {fieldtype: "Small Text", fieldname: "user_remark", label: __("User Remark")},
{fieldtype: "Select", fieldname: "naming_series", label: __("Series"), reqd: 1,
options: naming_series_options, default: naming_series_default},
]
diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
index ce8aba75b2..54464e71c4 100644
--- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
+++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py
@@ -32,8 +32,10 @@ class OpeningInvoiceCreationTool(Document):
})
invoices_summary.update({company: _summary})
- paid_amount.append(invoice.paid_amount)
- outstanding_amount.append(invoice.outstanding_amount)
+ if invoice.paid_amount:
+ paid_amount.append(invoice.paid_amount)
+ if invoice.outstanding_amount:
+ outstanding_amount.append(invoice.outstanding_amount)
if paid_amount or outstanding_amount:
max_count.update({
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 1e0b1bcbf1..adf47ed276 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -554,7 +554,7 @@ frappe.ui.form.on('Payment Entry', {
frappe.flags.allocate_payment_amount = true;
frm.events.validate_filters_data(frm, filters);
frm.events.get_outstanding_documents(frm, filters);
- }, __("Filters"), __("Get Outstanding Invoices"));
+ }, __("Filters"), __("Get Outstanding Documents"));
},
validate_filters_data: function(frm, filters) {
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index a85eccd30a..acfc660c4f 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -62,6 +62,7 @@
"dimension_col_break",
"cost_center",
"section_break_12",
+ "status",
"remarks",
"column_break_16",
"letter_head",
@@ -563,10 +564,18 @@
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
+ },
+ {
+ "default": "Draft",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "label": "Status",
+ "options": "\nDraft\nSubmitted\nCancelled",
+ "read_only": 1
}
],
"is_submittable": 1,
- "modified": "2019-05-27 15:53:21.108857",
+ "modified": "2019-11-06 12:59:43.151721",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 89aaffbc2d..bf7e833285 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -61,6 +61,7 @@ class PaymentEntry(AccountsController):
self.validate_duplicate_entry()
self.validate_allocated_amount()
self.ensure_supplier_is_not_blocked()
+ self.set_status()
def on_submit(self):
self.setup_party_account_field()
@@ -70,6 +71,7 @@ class PaymentEntry(AccountsController):
self.update_outstanding_amounts()
self.update_advance_paid()
self.update_expense_claim()
+ self.set_status()
def on_cancel(self):
@@ -79,6 +81,7 @@ class PaymentEntry(AccountsController):
self.update_advance_paid()
self.update_expense_claim()
self.delink_advance_entry_references()
+ self.set_status()
def update_outstanding_amounts(self):
self.set_missing_ref_details(force=True)
@@ -275,6 +278,14 @@ class PaymentEntry(AccountsController):
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
.format(d.reference_name, dr_or_cr))
+ def set_status(self):
+ if self.docstatus == 2:
+ self.status = 'Cancelled'
+ elif self.docstatus == 1:
+ self.status = 'Submitted'
+ else:
+ self.status = 'Draft'
+
def set_amounts(self):
self.set_amounts_in_company_currency()
self.set_total_allocated_amount()
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 4665d75510..d85344e8b7 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -90,7 +90,8 @@ class PaymentReconciliation(Document):
FROM `tab{doc}`, `tabGL Entry`
WHERE
(`tab{doc}`.name = `tabGL Entry`.against_voucher or `tab{doc}`.name = `tabGL Entry`.voucher_no)
- and `tab{doc}`.is_return = 1 and `tabGL Entry`.against_voucher_type = %(voucher_type)s
+ and `tab{doc}`.is_return = 1 and `tab{doc}`.return_against IS NULL
+ and `tabGL Entry`.against_voucher_type = %(voucher_type)s
and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s
and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s
GROUP BY `tab{doc}`.name
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 4ea9b1c6c9..f1c490e2cd 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -18,13 +18,14 @@ from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entri
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
-from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_disabled
+from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
from frappe.model.mapper import get_mapped_doc
from six import iteritems
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\
unlink_inter_company_doc
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
from erpnext.accounts.deferred_revenue import validate_service_stop_date
+from erpnext.stock.doctype.purchase_receipt.purchase_receipt import get_item_account_wise_additional_cost
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -225,6 +226,8 @@ class PurchaseInvoice(BuyingController):
# in case of auto inventory accounting,
# expense account is always "Stock Received But Not Billed" for a stock item
# except epening entry, drop-ship entry and fixed asset items
+ if item.item_code:
+ asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
if auto_accounting_for_stock and item.item_code in stock_items \
and self.is_opening == 'No' and not item.is_fixed_asset \
@@ -235,7 +238,8 @@ class PurchaseInvoice(BuyingController):
item.expense_account = warehouse_account[item.warehouse]["account"]
else:
item.expense_account = stock_not_billed_account
- elif item.is_fixed_asset and is_cwip_accounting_disabled():
+
+ elif item.is_fixed_asset and not is_cwip_accounting_enabled(self.company, asset_category):
if not item.asset:
frappe.throw(_("Row {0}: asset is required for item {1}")
.format(item.idx, item.item_code))
@@ -391,7 +395,8 @@ class PurchaseInvoice(BuyingController):
self.make_supplier_gl_entry(gl_entries)
self.make_item_gl_entries(gl_entries)
- if not is_cwip_accounting_disabled():
+
+ if self.check_asset_cwip_enabled():
self.get_asset_gl_entry(gl_entries)
self.make_tax_gl_entries(gl_entries)
@@ -404,6 +409,15 @@ class PurchaseInvoice(BuyingController):
return gl_entries
+ def check_asset_cwip_enabled(self):
+ # Check if there exists any item with cwip accounting enabled in it's asset category
+ for item in self.get("items"):
+ if item.item_code and item.is_fixed_asset:
+ asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
+ if is_cwip_accounting_enabled(self.company, asset_category):
+ return 1
+ return 0
+
def make_supplier_gl_entry(self, gl_entries):
# Checked both rounding_adjustment and rounded_total
# because rounded_total had value even before introcution of posting GLE based on rounded total
@@ -436,6 +450,8 @@ class PurchaseInvoice(BuyingController):
if self.update_stock and self.auto_accounting_for_stock:
warehouse_account = get_warehouse_account_map(self.company)
+ landed_cost_entries = get_item_account_wise_additional_cost(self.name)
+
voucher_wise_stock_value = {}
if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry',
@@ -445,6 +461,8 @@ class PurchaseInvoice(BuyingController):
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account)
+ if item.item_code:
+ asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items:
# warehouse account
@@ -463,15 +481,16 @@ class PurchaseInvoice(BuyingController):
)
# Amount added through landed-cost-voucher
- if flt(item.landed_cost_voucher_amount):
- gl_entries.append(self.get_gl_dict({
- "account": expenses_included_in_valuation,
- "against": item.expense_account,
- "cost_center": item.cost_center,
- "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "credit": flt(item.landed_cost_voucher_amount),
- "project": item.project
- }, item=item))
+ if landed_cost_entries:
+ for account, amount in iteritems(landed_cost_entries[(item.item_code, item.name)]):
+ gl_entries.append(self.get_gl_dict({
+ "account": account,
+ "against": item.expense_account,
+ "cost_center": item.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+ "credit": flt(amount),
+ "project": item.project
+ }, item=item))
# sub-contracting warehouse
if flt(item.rm_supp_cost):
@@ -486,8 +505,9 @@ class PurchaseInvoice(BuyingController):
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(item.rm_supp_cost)
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
- elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()):
+ elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(self.company,
+ asset_category)):
expense_account = (item.expense_account
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
@@ -528,7 +548,10 @@ class PurchaseInvoice(BuyingController):
def get_asset_gl_entry(self, gl_entries):
for item in self.get("items"):
- if item.is_fixed_asset:
+ if item.item_code and item.is_fixed_asset :
+ asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
+
+ if item.is_fixed_asset and is_cwip_accounting_enabled(self.company, asset_category) :
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js
index 4e76a8d955..800ed921bd 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js
@@ -6,8 +6,8 @@ frappe.listview_settings['Purchase Invoice'] = {
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
"currency", "is_return", "release_date", "on_hold"],
get_indicator: function(doc) {
- if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) {
- return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
+ if( (flt(doc.outstanding_amount) <= 0) && doc.docstatus == 1 && doc.status == 'Debit Note Issued') {
+ return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<=,0"];
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
if(cint(doc.on_hold) && !doc.release_date) {
return [__("On Hold"), "darkgrey"];
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 5766c9a8d1..fefd36a313 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -991,10 +991,8 @@ class SalesInvoice(SellingController):
continue
for serial_no in item.serial_no.split("\n"):
- if serial_no and frappe.db.exists('Serial No', serial_no):
- sno = frappe.get_doc('Serial No', serial_no)
- sno.sales_invoice = invoice
- sno.db_update()
+ if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
+ frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
def validate_serial_numbers(self):
"""
@@ -1040,8 +1038,9 @@ class SalesInvoice(SellingController):
continue
for serial_no in item.serial_no.split("\n"):
- sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice")
- if sales_invoice and self.name != sales_invoice:
+ sales_invoice, item_code = frappe.db.get_value("Serial No", serial_no,
+ ["sales_invoice", "item_code"])
+ if sales_invoice and item_code == item.item_code and self.name != sales_invoice:
sales_invoice_company = frappe.db.get_value("Sales Invoice", sales_invoice, "company")
if sales_invoice_company == self.company:
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
@@ -1230,7 +1229,8 @@ class SalesInvoice(SellingController):
self.status = "Unpaid and Discounted"
elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()):
self.status = "Unpaid"
- elif flt(self.outstanding_amount) < 0 and self.is_return==0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
+ #Check if outstanding amount is 0 due to credit note issued against invoice
+ elif flt(self.outstanding_amount) <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
self.status = "Credit Note Issued"
elif self.is_return == 1:
self.status = "Return"
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.js b/erpnext/accounts/doctype/share_transfer/share_transfer.js
index 364ca6fd28..1cad4dfae3 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.js
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.js
@@ -21,6 +21,8 @@ frappe.ui.form.on('Share Transfer', {
erpnext.share_transfer.make_jv(frm);
});
}
+
+ frm.toggle_reqd("asset_account", frm.doc.transfer_type != "Transfer");
},
no_of_shares: (frm) => {
if (frm.doc.rate != undefined || frm.doc.rate != null){
@@ -56,6 +58,10 @@ frappe.ui.form.on('Share Transfer', {
};
});
}
+ },
+
+ transfer_type: function(frm) {
+ frm.toggle_reqd("asset_account", frm.doc.transfer_type != "Transfer");
}
});
diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.json b/erpnext/accounts/doctype/share_transfer/share_transfer.json
index 24c4569b00..f17bf04caf 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.json
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.json
@@ -1,881 +1,239 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "ACC-SHT-.YYYY.-.#####",
- "beta": 0,
- "creation": "2017-12-25 17:18:03.143726",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "autoname": "ACC-SHT-.YYYY.-.#####",
+ "creation": "2017-12-25 17:18:03.143726",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "transfer_type",
+ "column_break_1",
+ "date",
+ "section_break_1",
+ "from_shareholder",
+ "from_folio_no",
+ "column_break_3",
+ "to_shareholder",
+ "to_folio_no",
+ "section_break_10",
+ "equity_or_liability_account",
+ "column_break_12",
+ "asset_account",
+ "section_break_4",
+ "share_type",
+ "from_no",
+ "rate",
+ "column_break_8",
+ "no_of_shares",
+ "to_no",
+ "amount",
+ "section_break_11",
+ "company",
+ "section_break_6",
+ "remarks",
+ "amended_from"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "transfer_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Transfer Type",
- "length": 0,
- "no_copy": 0,
- "options": "\nIssue\nPurchase\nTransfer",
- "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
- },
+ "fieldname": "transfer_type",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Transfer Type",
+ "options": "\nIssue\nPurchase\nTransfer",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_1",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_1",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "date",
+ "fieldtype": "Date",
+ "label": "Date",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_1",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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
- },
+ "fieldname": "section_break_1",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.transfer_type != 'Issue'",
- "fieldname": "from_shareholder",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "From Shareholder",
- "length": 0,
- "no_copy": 0,
- "options": "Shareholder",
- "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
- },
+ "depends_on": "eval:doc.transfer_type != 'Issue'",
+ "fieldname": "from_shareholder",
+ "fieldtype": "Link",
+ "label": "From Shareholder",
+ "options": "Shareholder"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.transfer_type != 'Issue'",
- "fetch_from": "from_shareholder.folio_no",
- "fieldname": "from_folio_no",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "From Folio No",
- "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": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "eval:doc.transfer_type != 'Issue'",
+ "fetch_from": "from_shareholder.folio_no",
+ "fieldname": "from_folio_no",
+ "fieldtype": "Data",
+ "label": "From Folio No"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.company",
- "fieldname": "equity_or_liability_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Equity/Liability Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "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
- },
+ "depends_on": "eval:doc.company",
+ "fieldname": "equity_or_liability_account",
+ "fieldtype": "Link",
+ "label": "Equity/Liability Account",
+ "options": "Account",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:(doc.transfer_type != 'Transfer') && (doc.company)",
- "fieldname": "asset_account",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Asset Account",
- "length": 0,
- "no_copy": 0,
- "options": "Account",
- "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
- },
+ "depends_on": "eval:(doc.transfer_type != 'Transfer') && (doc.company)",
+ "fieldname": "asset_account",
+ "fieldtype": "Link",
+ "label": "Asset Account",
+ "options": "Account"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.transfer_type != 'Purchase'",
- "fieldname": "to_shareholder",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "To Shareholder",
- "length": 0,
- "no_copy": 0,
- "options": "Shareholder",
- "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
- },
+ "depends_on": "eval:doc.transfer_type != 'Purchase'",
+ "fieldname": "to_shareholder",
+ "fieldtype": "Link",
+ "label": "To Shareholder",
+ "options": "Shareholder"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.transfer_type != 'Purchase'",
- "fetch_from": "to_shareholder.folio_no",
- "fieldname": "to_folio_no",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "To Folio No",
- "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": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "eval:doc.transfer_type != 'Purchase'",
+ "fetch_from": "to_shareholder.folio_no",
+ "fieldname": "to_folio_no",
+ "fieldtype": "Data",
+ "label": "To Folio No"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_4",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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
- },
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "share_type",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Share Type",
- "length": 0,
- "no_copy": 0,
- "options": "Share Type",
- "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
- },
+ "fieldname": "share_type",
+ "fieldtype": "Link",
+ "label": "Share Type",
+ "options": "Share Type",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "(including)",
- "fieldname": "from_no",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "From No",
- "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": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "description": "(including)",
+ "fieldname": "from_no",
+ "fieldtype": "Int",
+ "label": "From No",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "rate",
- "fieldtype": "Currency",
- "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": "Rate",
- "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": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "rate",
+ "fieldtype": "Currency",
+ "label": "Rate",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_8",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "no_of_shares",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "No of Shares",
- "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": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "no_of_shares",
+ "fieldtype": "Int",
+ "label": "No of Shares",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "(including)",
- "fieldname": "to_no",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "To No",
- "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": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "description": "(including)",
+ "fieldname": "to_no",
+ "fieldtype": "Int",
+ "label": "To No",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "amount",
- "fieldtype": "Currency",
- "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": "Amount",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "label": "Amount",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_11",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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
- },
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "company",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_6",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "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
- },
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "remarks",
- "fieldtype": "Long Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Remarks",
- "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
- },
+ "fieldname": "remarks",
+ "fieldtype": "Long Text",
+ "label": "Remarks"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Amended From",
- "length": 0,
- "no_copy": 1,
- "options": "Share Transfer",
- "permlevel": 0,
- "print_hide": 1,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Share Transfer",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "section_break_10",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_12",
+ "fieldtype": "Column Break"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-09-18 14:14:46.233568",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Share Transfer",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "is_submittable": 1,
+ "modified": "2019-11-07 13:31:17.999744",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Share Transfer",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 1,
- "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": 1,
+ "amend": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "share": 1,
+ "submit": 1,
"write": 1
- },
+ },
{
- "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": "Accounts User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1,
"write": 1
- },
+ },
{
- "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": "Accounts Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "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,
- "track_views": 0
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 43d9ad6435..38f283c8d4 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -163,19 +163,32 @@ def validate_account_for_perpetual_inventory(gl_map):
.format(account), StockAccountInvalidTransaction)
elif account_bal != stock_bal:
- frappe.throw(_("Account Balance ({0}) and Stock Value ({1}) is out of sync for account {2} and linked warehouse ({3}). Please create adjustment Journal Entry for amount {4}.")
- .format(account_bal, stock_bal, account, comma_and(warehouse_list), stock_bal - account_bal),
- StockValueAndAccountBalanceOutOfSync)
+ error_reason = _("Account Balance ({0}) and Stock Value ({1}) is out of sync for account {2} and it's linked warehouses.").format(
+ account_bal, stock_bal, frappe.bold(account))
+ error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(stock_bal - account_bal))
+ button_text = _("Make Adjustment Entry")
+
+ frappe.throw("""{0}
{1}
+
+
+
""".format(error_reason, error_resolution, button_text),
+ StockValueAndAccountBalanceOutOfSync, title=_('Account Balance Out Of Sync'))
def validate_cwip_accounts(gl_map):
- if not cint(frappe.db.get_value("Asset Settings", None, "disable_cwip_accounting")) \
- and gl_map[0].voucher_type == "Journal Entry":
+ cwip_enabled = cint(frappe.get_cached_value("Company",
+ gl_map[0].company, "enable_cwip_accounting"))
+
+ if not cwip_enabled:
+ cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
+
+ if cwip_enabled and gl_map[0].voucher_type == "Journal Entry":
cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
where account_type = 'Capital Work in Progress' and is_group=0""")]
for entry in gl_map:
if entry.account in cwip_accounts:
- frappe.throw(_("Account: {0} is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
+ frappe.throw(
+ _("Account: {0} is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
def round_off_debit_credit(gl_map):
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"),
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
index 6eafa0d231..efc76f9158 100644
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
@@ -139,15 +139,11 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
}
make() {
- const me = this;
- frappe.upload.make({
- args: {
- method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
- allow_multiple: 0
- },
- no_socketio: true,
- sample_url: "e.g. http://example.com/somefile.csv",
- callback: function(attachment, r) {
+ const me = this;
+ new frappe.ui.FileUploader({
+ method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement',
+ allow_multiple: 0,
+ on_success: function(attachment, r) {
if (!r.exc && r.message) {
me.data = r.message;
me.setup_transactions_dom();
@@ -533,9 +529,16 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
frappe.db.get_doc(dt, event.value)
.then(doc => {
let displayed_docs = []
+ let payment = []
if (dt === "Payment Entry") {
payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency;
payment.doctype = dt
+ payment.posting_date = doc.posting_date;
+ payment.party = doc.party;
+ payment.reference_no = doc.reference_no;
+ payment.reference_date = doc.reference_date;
+ payment.paid_amount = doc.paid_amount;
+ payment.name = doc.name;
displayed_docs.push(payment);
} else if (dt === "Journal Entry") {
doc.accounts.forEach(payment => {
@@ -568,11 +571,11 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper;
details_wrapper.append(frappe.render_template("linked_payment_header"));
- displayed_docs.forEach(values => {
- details_wrapper.append(frappe.render_template("linked_payment_row", values));
+ displayed_docs.forEach(payment => {
+ details_wrapper.append(frappe.render_template("linked_payment_row", payment));
})
})
}
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 228be18d21..9b4dda2f69 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -79,13 +79,20 @@ frappe.query_reports["Accounts Receivable"] = {
"options": "Customer",
on_change: () => {
var customer = frappe.query_report.get_filter_value('customer');
+ var company = frappe.query_report.get_filter_value('company');
if (customer) {
- frappe.db.get_value('Customer', customer, ["tax_id", "customer_name", "credit_limit", "payment_terms"], function(value) {
+ frappe.db.get_value('Customer', customer, ["tax_id", "customer_name", "payment_terms"], function(value) {
frappe.query_report.set_filter_value('tax_id', value["tax_id"]);
frappe.query_report.set_filter_value('customer_name', value["customer_name"]);
- frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]);
frappe.query_report.set_filter_value('payment_terms', value["payment_terms"]);
});
+
+ frappe.db.get_value('Customer Credit Limit', {'parent': customer, 'company': company},
+ ["credit_limit"], function(value) {
+ if (value) {
+ frappe.query_report.set_filter_value('credit_limit', value["credit_limit"]);
+ }
+ }, "Customer");
} else {
frappe.query_report.set_filter_value('tax_id', "");
frappe.query_report.set_filter_value('customer_name', "");
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index bcbd427186..14906f2c2e 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -188,7 +188,11 @@ class ReceivablePayableReport(object):
self.data.append(row)
def set_invoice_details(self, row):
- row.update(self.invoice_details.get(row.voucher_no, {}))
+ invoice_details = self.invoice_details.get(row.voucher_no, {})
+ if row.due_date:
+ invoice_details.pop("due_date", None)
+ row.update(invoice_details)
+
if row.voucher_type == 'Sales Invoice':
if self.filters.show_delivery_notes:
self.set_delivery_notes(row)
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index b90a7a9501..8955830e09 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -36,6 +36,9 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.filters.report_date) or {}
for party, party_dict in iteritems(self.party_total):
+ if party_dict.outstanding <= 0:
+ continue
+
row = frappe._dict()
row.party = party
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js
index 4bc29da2c7..8c11514aa6 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.js
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js
@@ -2,7 +2,7 @@
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/financial_statements.js", function() {
- frappe.query_reports["Balance Sheet"] = erpnext.financial_statements;
+ frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
frappe.query_reports["Balance Sheet"]["filters"].push({
"fieldname": "accumulated_values",
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 10e977acbf..faeee0f76a 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -76,8 +76,7 @@ def get_data(filters):
accumulate_values_into_parents(accounts, accounts_by_name)
data = prepare_data(accounts, filters, total_row, parent_children_map, company_currency)
- data = filter_out_zero_value_rows(data, parent_children_map,
- show_zero_values=filters.get("show_zero_values"))
+ data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values"))
return data
@@ -187,33 +186,11 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, filters,
d["closing_debit"] = d["opening_debit"] + d["debit"]
d["closing_credit"] = d["opening_credit"] + d["credit"]
- total_row["debit"] += d["debit"]
- total_row["credit"] += d["credit"]
- if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense":
- d["opening_debit"] -= d["opening_credit"]
- d["closing_debit"] -= d["closing_credit"]
+ prepare_opening_closing(d)
- # For opening
- check_opening_closing_has_negative_value(d, "opening_debit", "opening_credit")
-
- # For closing
- check_opening_closing_has_negative_value(d, "closing_debit", "closing_credit")
-
- if d["root_type"] == "Liability" or d["root_type"] == "Income":
- d["opening_credit"] -= d["opening_debit"]
- d["closing_credit"] -= d["closing_debit"]
-
- # For opening
- check_opening_closing_has_negative_value(d, "opening_credit", "opening_debit")
-
- # For closing
- check_opening_closing_has_negative_value(d, "closing_credit", "closing_debit")
-
- total_row["opening_debit"] += d["opening_debit"]
- total_row["closing_debit"] += d["closing_debit"]
- total_row["opening_credit"] += d["opening_credit"]
- total_row["closing_credit"] += d["closing_credit"]
+ for field in value_fields:
+ total_row[field] += d[field]
return total_row
@@ -227,6 +204,10 @@ def prepare_data(accounts, filters, total_row, parent_children_map, company_curr
data = []
for d in accounts:
+ # Prepare opening closing for group account
+ if parent_children_map.get(d.account):
+ prepare_opening_closing(d)
+
has_value = False
row = {
"account": d.name,
@@ -313,11 +294,16 @@ def get_columns():
}
]
-def check_opening_closing_has_negative_value(d, dr_or_cr, switch_to_column):
- # If opening debit has negetive value then move it to opening credit and vice versa.
+def prepare_opening_closing(row):
+ dr_or_cr = "debit" if row["root_type"] in ["Asset", "Equity", "Expense"] else "credit"
+ reverse_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
- if d[dr_or_cr] < 0:
- d[switch_to_column] = abs(d[dr_or_cr])
- d[dr_or_cr] = 0.0
- else:
- d[switch_to_column] = 0.0
+ for col_type in ["opening", "closing"]:
+ valid_col = col_type + "_" + dr_or_cr
+ reverse_col = col_type + "_" + reverse_dr_or_cr
+ row[valid_col] -= row[reverse_col]
+ if row[valid_col] < 0:
+ row[reverse_col] = abs(row[valid_col])
+ row[valid_col] = 0.0
+ else:
+ row[reverse_col] = 0.0
\ No newline at end of file
diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
index bb9045ca81..3e51933df7 100644
--- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
+++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
@@ -51,27 +51,25 @@ class CropCycle(Document):
self.create_task(disease_doc.treatment_task, self.name, start_date)
def create_project(self, period, crop_tasks):
- project = frappe.new_doc("Project")
- project.update({
+ project = frappe.get_doc({
+ "doctype": "Project",
"project_name": self.title,
"expected_start_date": self.start_date,
"expected_end_date": add_days(self.start_date, period - 1)
- })
- project.insert()
+ }).insert()
return project.name
def create_task(self, crop_tasks, project_name, start_date):
for crop_task in crop_tasks:
- task = frappe.new_doc("Task")
- task.update({
+ frappe.get_doc({
+ "doctype": "Task",
"subject": crop_task.get("task_name"),
"priority": crop_task.get("priority"),
"project": project_name,
"exp_start_date": add_days(start_date, crop_task.get("start_day") - 1),
"exp_end_date": add_days(start_date, crop_task.get("end_day") - 1)
- })
- task.insert()
+ }).insert()
def reload_linked_analysis(self):
linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis']
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index c5cad73801..c7390a2ef1 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -203,7 +203,7 @@ frappe.ui.form.on('Asset', {
},
opening_accumulated_depreciation: function(frm) {
- erpnext.asset.set_accululated_depreciation(frm);
+ erpnext.asset.set_accumulated_depreciation(frm);
},
make_schedules_editable: function(frm) {
@@ -282,17 +282,6 @@ frappe.ui.form.on('Asset', {
},
calculate_depreciation: function(frm) {
- frappe.db.get_value("Asset Settings", {'name':"Asset Settings"}, 'schedule_based_on_fiscal_year', (data) => {
- if (data.schedule_based_on_fiscal_year == 1) {
- frm.set_df_property("depreciation_method", "options", "\nStraight Line\nManual");
- frm.toggle_reqd("available_for_use_date", true);
- frm.toggle_display("frequency_of_depreciation", false);
- frappe.db.get_value("Fiscal Year", {'name': frappe.sys_defaults.fiscal_year}, "year_end_date", (data) => {
- frm.set_value("next_depreciation_date", data.year_end_date);
- })
- }
- })
-
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
},
@@ -371,12 +360,12 @@ frappe.ui.form.on('Depreciation Schedule', {
},
depreciation_amount: function(frm, cdt, cdn) {
- erpnext.asset.set_accululated_depreciation(frm);
+ erpnext.asset.set_accumulated_depreciation(frm);
}
})
-erpnext.asset.set_accululated_depreciation = function(frm) {
+erpnext.asset.set_accumulated_depreciation = function(frm) {
if(frm.doc.depreciation_method != "Manual") return;
var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index c60ec5ec3f..6882f6a992 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -1,497 +1,506 @@
{
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "naming_series:",
- "creation": "2016-03-01 17:01:27.920130",
- "doctype": "DocType",
- "document_type": "Document",
- "field_order": [
- "naming_series",
- "asset_name",
- "item_code",
- "item_name",
- "asset_category",
- "asset_owner",
- "asset_owner_company",
- "supplier",
- "customer",
- "image",
- "column_break_3",
- "company",
- "location",
- "custodian",
- "department",
- "purchase_date",
- "disposal_date",
- "journal_entry_for_scrap",
- "accounting_dimensions_section",
- "cost_center",
- "dimension_col_break",
- "section_break_5",
- "gross_purchase_amount",
- "available_for_use_date",
- "column_break_18",
- "calculate_depreciation",
- "is_existing_asset",
- "opening_accumulated_depreciation",
- "number_of_depreciations_booked",
- "section_break_23",
- "finance_books",
- "section_break_33",
- "depreciation_method",
- "value_after_depreciation",
- "total_number_of_depreciations",
- "column_break_24",
- "frequency_of_depreciation",
- "next_depreciation_date",
- "section_break_14",
- "schedules",
- "insurance_details",
- "policy_number",
- "insurer",
- "insured_value",
- "column_break_48",
- "insurance_start_date",
- "insurance_end_date",
- "comprehensive_insurance",
- "section_break_31",
- "maintenance_required",
- "other_details",
- "status",
- "booked_fixed_asset",
- "column_break_51",
- "purchase_receipt",
- "purchase_receipt_amount",
- "purchase_invoice",
- "default_finance_book",
- "amended_from"
- ],
- "fields": [
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Naming Series",
- "options": "ACC-ASS-.YYYY.-"
- },
- {
- "fieldname": "asset_name",
- "fieldtype": "Data",
- "in_list_view": 1,
- "label": "Asset Name",
- "reqd": 1
- },
- {
- "fieldname": "item_code",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "Item Code",
- "options": "Item",
- "reqd": 1
- },
- {
- "fetch_from": "item_code.item_name",
- "fieldname": "item_name",
- "fieldtype": "Read Only",
- "label": "Item Name"
- },
- {
- "fetch_from": "item_code.asset_category",
- "fieldname": "asset_category",
- "fieldtype": "Link",
- "in_global_search": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Asset Category",
- "options": "Asset Category",
- "read_only": 1
- },
- {
- "fieldname": "asset_owner",
- "fieldtype": "Select",
- "label": "Asset Owner",
- "options": "\nCompany\nSupplier\nCustomer"
- },
- {
- "depends_on": "eval:doc.asset_owner == \"Company\"",
- "fieldname": "asset_owner_company",
- "fieldtype": "Link",
- "label": "Asset Owner Company",
- "options": "Company"
- },
- {
- "depends_on": "eval:doc.asset_owner == \"Supplier\"",
- "fieldname": "supplier",
- "fieldtype": "Link",
- "label": "Supplier",
- "options": "Supplier"
- },
- {
- "depends_on": "eval:doc.asset_owner == \"Customer\"",
- "fieldname": "customer",
- "fieldtype": "Link",
- "label": "Customer",
- "options": "Customer"
- },
- {
- "allow_on_submit": 1,
- "fieldname": "image",
- "fieldtype": "Attach Image",
- "hidden": 1,
- "label": "Image",
- "no_copy": 1,
- "print_hide": 1
- },
- {
- "fieldname": "column_break_3",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "options": "Company",
- "remember_last_selected_value": 1,
- "reqd": 1
- },
- {
- "fieldname": "location",
- "fieldtype": "Link",
- "in_list_view": 1,
- "label": "Location",
- "options": "Location",
- "reqd": 1
- },
- {
- "fieldname": "custodian",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Custodian",
- "options": "Employee"
- },
- {
- "fieldname": "cost_center",
- "fieldtype": "Link",
- "label": "Cost Center",
- "options": "Cost Center"
- },
- {
- "fieldname": "department",
- "fieldtype": "Link",
- "label": "Department",
- "options": "Department"
- },
- {
- "fieldname": "purchase_date",
- "fieldtype": "Date",
- "label": "Purchase Date",
- "reqd": 1
- },
- {
- "fieldname": "disposal_date",
- "fieldtype": "Date",
- "label": "Disposal Date",
- "read_only": 1
- },
- {
- "fieldname": "journal_entry_for_scrap",
- "fieldtype": "Link",
- "label": "Journal Entry for Scrap",
- "no_copy": 1,
- "options": "Journal Entry",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "section_break_5",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "gross_purchase_amount",
- "fieldtype": "Currency",
- "label": "Gross Purchase Amount",
- "options": "Company:company:default_currency",
- "reqd": 1
- },
- {
- "fieldname": "available_for_use_date",
- "fieldtype": "Date",
- "label": "Available-for-use Date"
- },
- {
- "fieldname": "column_break_18",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "fieldname": "calculate_depreciation",
- "fieldtype": "Check",
- "label": "Calculate Depreciation"
- },
- {
- "default": "0",
- "fieldname": "is_existing_asset",
- "fieldtype": "Check",
- "label": "Is Existing Asset"
- },
- {
- "depends_on": "is_existing_asset",
- "fieldname": "opening_accumulated_depreciation",
- "fieldtype": "Currency",
- "label": "Opening Accumulated Depreciation",
- "no_copy": 1,
- "options": "Company:company:default_currency"
- },
- {
- "depends_on": "eval:(doc.is_existing_asset && doc.opening_accumulated_depreciation)",
- "fieldname": "number_of_depreciations_booked",
- "fieldtype": "Int",
- "label": "Number of Depreciations Booked",
- "no_copy": 1
- },
- {
- "depends_on": "calculate_depreciation",
- "fieldname": "section_break_23",
- "fieldtype": "Section Break",
- "label": "Depreciation"
- },
- {
- "fieldname": "finance_books",
- "fieldtype": "Table",
- "label": "Finance Books",
- "options": "Asset Finance Book"
- },
- {
- "fieldname": "section_break_33",
- "fieldtype": "Section Break",
- "hidden": 1
- },
- {
- "fieldname": "depreciation_method",
- "fieldtype": "Select",
- "label": "Depreciation Method",
- "options": "\nStraight Line\nDouble Declining Balance\nManual"
- },
- {
- "fieldname": "value_after_depreciation",
- "fieldtype": "Currency",
- "hidden": 1,
- "label": "Value After Depreciation",
- "options": "Company:company:default_currency",
- "read_only": 1
- },
- {
- "fieldname": "total_number_of_depreciations",
- "fieldtype": "Int",
- "label": "Total Number of Depreciations"
- },
- {
- "fieldname": "column_break_24",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "frequency_of_depreciation",
- "fieldtype": "Int",
- "label": "Frequency of Depreciation (Months)"
- },
- {
- "fieldname": "next_depreciation_date",
- "fieldtype": "Date",
- "label": "Next Depreciation Date",
- "no_copy": 1
- },
- {
- "depends_on": "calculate_depreciation",
- "fieldname": "section_break_14",
- "fieldtype": "Section Break",
- "label": "Depreciation Schedule"
- },
- {
- "fieldname": "schedules",
- "fieldtype": "Table",
- "label": "Depreciation Schedules",
- "no_copy": 1,
- "options": "Depreciation Schedule"
- },
- {
- "collapsible": 1,
- "fieldname": "insurance_details",
- "fieldtype": "Section Break",
- "label": "Insurance details"
- },
- {
- "fieldname": "policy_number",
- "fieldtype": "Data",
- "label": "Policy number"
- },
- {
- "fieldname": "insurer",
- "fieldtype": "Data",
- "label": "Insurer"
- },
- {
- "fieldname": "insured_value",
- "fieldtype": "Data",
- "label": "Insured value"
- },
- {
- "fieldname": "column_break_48",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "insurance_start_date",
- "fieldtype": "Date",
- "label": "Insurance Start Date"
- },
- {
- "fieldname": "insurance_end_date",
- "fieldtype": "Date",
- "label": "Insurance End Date"
- },
- {
- "fieldname": "comprehensive_insurance",
- "fieldtype": "Data",
- "label": "Comprehensive Insurance"
- },
- {
- "fieldname": "section_break_31",
- "fieldtype": "Section Break",
- "label": "Maintenance"
- },
- {
- "allow_on_submit": 1,
- "default": "0",
- "description": "Check if Asset requires Preventive Maintenance or Calibration",
- "fieldname": "maintenance_required",
- "fieldtype": "Check",
- "label": "Maintenance Required"
- },
- {
- "collapsible": 1,
- "fieldname": "other_details",
- "fieldtype": "Section Break",
- "label": "Other Details"
- },
- {
- "allow_on_submit": 1,
- "default": "Draft",
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Status",
- "no_copy": 1,
- "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt",
- "read_only": 1
- },
- {
- "default": "0",
- "fieldname": "booked_fixed_asset",
- "fieldtype": "Check",
- "label": "Booked Fixed Asset",
- "no_copy": 1,
- "read_only": 1
- },
- {
- "fieldname": "column_break_51",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "purchase_receipt",
- "fieldtype": "Link",
- "label": "Purchase Receipt",
- "no_copy": 1,
- "options": "Purchase Receipt",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "purchase_receipt_amount",
- "fieldtype": "Currency",
- "hidden": 1,
- "label": "Purchase Receipt Amount",
- "no_copy": 1,
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "purchase_invoice",
- "fieldtype": "Link",
- "label": "Purchase Invoice",
- "no_copy": 1,
- "options": "Purchase Invoice",
- "read_only": 1
- },
- {
- "fetch_from": "company.default_finance_book",
- "fieldname": "default_finance_book",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Default Finance Book",
- "options": "Finance Book",
- "read_only": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "label": "Amended From",
- "no_copy": 1,
- "options": "Asset",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "collapsible": 1,
- "fieldname": "accounting_dimensions_section",
- "fieldtype": "Section Break",
- "label": "Accounting Dimensions"
- },
- {
- "fieldname": "dimension_col_break",
- "fieldtype": "Column Break"
- }
- ],
- "idx": 72,
- "image_field": "image",
- "is_submittable": 1,
- "modified": "2019-05-25 22:26:19.786201",
- "modified_by": "Administrator",
- "module": "Assets",
- "name": "Asset",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "import": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts User",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Quality Manager",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "asset_name"
- }
\ No newline at end of file
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "naming_series:",
+ "creation": "2016-03-01 17:01:27.920130",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+ "naming_series",
+ "asset_name",
+ "item_code",
+ "item_name",
+ "asset_category",
+ "asset_owner",
+ "asset_owner_company",
+ "supplier",
+ "customer",
+ "image",
+ "column_break_3",
+ "company",
+ "location",
+ "custodian",
+ "department",
+ "purchase_date",
+ "disposal_date",
+ "journal_entry_for_scrap",
+ "accounting_dimensions_section",
+ "cost_center",
+ "dimension_col_break",
+ "section_break_5",
+ "gross_purchase_amount",
+ "available_for_use_date",
+ "column_break_18",
+ "calculate_depreciation",
+ "allow_monthly_depreciation",
+ "is_existing_asset",
+ "opening_accumulated_depreciation",
+ "number_of_depreciations_booked",
+ "section_break_23",
+ "finance_books",
+ "section_break_33",
+ "depreciation_method",
+ "value_after_depreciation",
+ "total_number_of_depreciations",
+ "column_break_24",
+ "frequency_of_depreciation",
+ "next_depreciation_date",
+ "section_break_14",
+ "schedules",
+ "insurance_details",
+ "policy_number",
+ "insurer",
+ "insured_value",
+ "column_break_48",
+ "insurance_start_date",
+ "insurance_end_date",
+ "comprehensive_insurance",
+ "section_break_31",
+ "maintenance_required",
+ "other_details",
+ "status",
+ "booked_fixed_asset",
+ "column_break_51",
+ "purchase_receipt",
+ "purchase_receipt_amount",
+ "purchase_invoice",
+ "default_finance_book",
+ "amended_from"
+ ],
+ "fields": [
+ {
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Naming Series",
+ "options": "ACC-ASS-.YYYY.-"
+ },
+ {
+ "fieldname": "asset_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Asset Name",
+ "reqd": 1
+ },
+ {
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "in_standard_filter": 1,
+ "label": "Item Code",
+ "options": "Item",
+ "reqd": 1
+ },
+ {
+ "fetch_from": "item_code.item_name",
+ "fieldname": "item_name",
+ "fieldtype": "Read Only",
+ "label": "Item Name"
+ },
+ {
+ "fetch_from": "item_code.asset_category",
+ "fieldname": "asset_category",
+ "fieldtype": "Link",
+ "in_global_search": 1,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Asset Category",
+ "options": "Asset Category",
+ "read_only": 1
+ },
+ {
+ "fieldname": "asset_owner",
+ "fieldtype": "Select",
+ "label": "Asset Owner",
+ "options": "\nCompany\nSupplier\nCustomer"
+ },
+ {
+ "depends_on": "eval:doc.asset_owner == \"Company\"",
+ "fieldname": "asset_owner_company",
+ "fieldtype": "Link",
+ "label": "Asset Owner Company",
+ "options": "Company"
+ },
+ {
+ "depends_on": "eval:doc.asset_owner == \"Supplier\"",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "label": "Supplier",
+ "options": "Supplier"
+ },
+ {
+ "depends_on": "eval:doc.asset_owner == \"Customer\"",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "label": "Customer",
+ "options": "Customer"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "image",
+ "fieldtype": "Attach Image",
+ "hidden": 1,
+ "label": "Image",
+ "no_copy": 1,
+ "print_hide": 1
+ },
+ {
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "remember_last_selected_value": 1,
+ "reqd": 1
+ },
+ {
+ "fieldname": "location",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Location",
+ "options": "Location",
+ "reqd": 1
+ },
+ {
+ "fieldname": "custodian",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Custodian",
+ "options": "Employee"
+ },
+ {
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "department",
+ "fieldtype": "Link",
+ "label": "Department",
+ "options": "Department"
+ },
+ {
+ "fieldname": "purchase_date",
+ "fieldtype": "Date",
+ "label": "Purchase Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "disposal_date",
+ "fieldtype": "Date",
+ "label": "Disposal Date",
+ "read_only": 1
+ },
+ {
+ "fieldname": "journal_entry_for_scrap",
+ "fieldtype": "Link",
+ "label": "Journal Entry for Scrap",
+ "no_copy": 1,
+ "options": "Journal Entry",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "section_break_5",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "gross_purchase_amount",
+ "fieldtype": "Currency",
+ "label": "Gross Purchase Amount",
+ "options": "Company:company:default_currency",
+ "reqd": 1
+ },
+ {
+ "fieldname": "available_for_use_date",
+ "fieldtype": "Date",
+ "label": "Available-for-use Date"
+ },
+ {
+ "fieldname": "column_break_18",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "calculate_depreciation",
+ "fieldtype": "Check",
+ "label": "Calculate Depreciation"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_existing_asset",
+ "fieldtype": "Check",
+ "label": "Is Existing Asset"
+ },
+ {
+ "depends_on": "is_existing_asset",
+ "fieldname": "opening_accumulated_depreciation",
+ "fieldtype": "Currency",
+ "label": "Opening Accumulated Depreciation",
+ "no_copy": 1,
+ "options": "Company:company:default_currency"
+ },
+ {
+ "depends_on": "eval:(doc.is_existing_asset && doc.opening_accumulated_depreciation)",
+ "fieldname": "number_of_depreciations_booked",
+ "fieldtype": "Int",
+ "label": "Number of Depreciations Booked",
+ "no_copy": 1
+ },
+ {
+ "depends_on": "calculate_depreciation",
+ "fieldname": "section_break_23",
+ "fieldtype": "Section Break",
+ "label": "Depreciation"
+ },
+ {
+ "fieldname": "finance_books",
+ "fieldtype": "Table",
+ "label": "Finance Books",
+ "options": "Asset Finance Book"
+ },
+ {
+ "fieldname": "section_break_33",
+ "fieldtype": "Section Break",
+ "hidden": 1
+ },
+ {
+ "fieldname": "depreciation_method",
+ "fieldtype": "Select",
+ "label": "Depreciation Method",
+ "options": "\nStraight Line\nDouble Declining Balance\nManual"
+ },
+ {
+ "fieldname": "value_after_depreciation",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Value After Depreciation",
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "total_number_of_depreciations",
+ "fieldtype": "Int",
+ "label": "Total Number of Depreciations"
+ },
+ {
+ "fieldname": "column_break_24",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "frequency_of_depreciation",
+ "fieldtype": "Int",
+ "label": "Frequency of Depreciation (Months)"
+ },
+ {
+ "fieldname": "next_depreciation_date",
+ "fieldtype": "Date",
+ "label": "Next Depreciation Date",
+ "no_copy": 1
+ },
+ {
+ "depends_on": "calculate_depreciation",
+ "fieldname": "section_break_14",
+ "fieldtype": "Section Break",
+ "label": "Depreciation Schedule"
+ },
+ {
+ "fieldname": "schedules",
+ "fieldtype": "Table",
+ "label": "Depreciation Schedules",
+ "no_copy": 1,
+ "options": "Depreciation Schedule"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "insurance_details",
+ "fieldtype": "Section Break",
+ "label": "Insurance details"
+ },
+ {
+ "fieldname": "policy_number",
+ "fieldtype": "Data",
+ "label": "Policy number"
+ },
+ {
+ "fieldname": "insurer",
+ "fieldtype": "Data",
+ "label": "Insurer"
+ },
+ {
+ "fieldname": "insured_value",
+ "fieldtype": "Data",
+ "label": "Insured value"
+ },
+ {
+ "fieldname": "column_break_48",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "insurance_start_date",
+ "fieldtype": "Date",
+ "label": "Insurance Start Date"
+ },
+ {
+ "fieldname": "insurance_end_date",
+ "fieldtype": "Date",
+ "label": "Insurance End Date"
+ },
+ {
+ "fieldname": "comprehensive_insurance",
+ "fieldtype": "Data",
+ "label": "Comprehensive Insurance"
+ },
+ {
+ "fieldname": "section_break_31",
+ "fieldtype": "Section Break",
+ "label": "Maintenance"
+ },
+ {
+ "allow_on_submit": 1,
+ "default": "0",
+ "description": "Check if Asset requires Preventive Maintenance or Calibration",
+ "fieldname": "maintenance_required",
+ "fieldtype": "Check",
+ "label": "Maintenance Required"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "other_details",
+ "fieldtype": "Section Break",
+ "label": "Other Details"
+ },
+ {
+ "allow_on_submit": 1,
+ "default": "Draft",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Status",
+ "no_copy": 1,
+ "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt",
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "booked_fixed_asset",
+ "fieldtype": "Check",
+ "label": "Booked Fixed Asset",
+ "no_copy": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_51",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "purchase_receipt",
+ "fieldtype": "Link",
+ "label": "Purchase Receipt",
+ "no_copy": 1,
+ "options": "Purchase Receipt",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "purchase_receipt_amount",
+ "fieldtype": "Currency",
+ "hidden": 1,
+ "label": "Purchase Receipt Amount",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "purchase_invoice",
+ "fieldtype": "Link",
+ "label": "Purchase Invoice",
+ "no_copy": 1,
+ "options": "Purchase Invoice",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "company.default_finance_book",
+ "fieldname": "default_finance_book",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Default Finance Book",
+ "options": "Finance Book",
+ "read_only": 1
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "label": "Amended From",
+ "no_copy": 1,
+ "options": "Asset",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "depends_on": "calculate_depreciation",
+ "fieldname": "allow_monthly_depreciation",
+ "fieldtype": "Check",
+ "label": "Allow Monthly Depreciation"
+ }
+ ],
+ "idx": 72,
+ "image_field": "image",
+ "is_submittable": 1,
+ "modified": "2019-10-22 15:47:36.050828",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "import": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Quality Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "asset_name"
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 6e2bbc1626..d1f8c1a8d3 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe, erpnext, math, json
from frappe import _
from six import string_types
-from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, add_days
+from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days
from frappe.model.document import Document
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.assets.doctype.asset.depreciation \
@@ -30,7 +30,8 @@ class Asset(AccountsController):
self.validate_in_use_date()
self.set_status()
self.update_stock_movement()
- if not self.booked_fixed_asset and not is_cwip_accounting_disabled():
+ if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.company,
+ self.asset_category):
self.make_gl_entries()
def on_cancel(self):
@@ -76,10 +77,13 @@ class Asset(AccountsController):
self.set('finance_books', finance_books)
def validate_asset_values(self):
+ if not self.asset_category:
+ self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
+
if not flt(self.gross_purchase_amount):
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
- if not is_cwip_accounting_disabled():
+ if is_cwip_accounting_enabled(self.company, self.asset_category):
if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice):
frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}").
format(self.item_code))
@@ -145,19 +149,31 @@ class Asset(AccountsController):
schedule_date = add_months(d.depreciation_start_date,
n * cint(d.frequency_of_depreciation))
+ # schedule date will be a year later from start date
+ # so monthly schedule date is calculated by removing 11 months from it
+ monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1)
+
# For first row
if has_pro_rata and n==0:
- depreciation_amount, days = get_pro_rata_amt(d, depreciation_amount,
+ depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount,
self.available_for_use_date, d.depreciation_start_date)
+
+ # For first depr schedule date will be the start date
+ # so monthly schedule date is calculated by removing month difference between use date and start date
+ monthly_schedule_date = add_months(d.depreciation_start_date, - months + 1)
+
# For last row
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
to_date = add_months(self.available_for_use_date,
n * cint(d.frequency_of_depreciation))
- depreciation_amount, days = get_pro_rata_amt(d,
+ depreciation_amount, days, months = get_pro_rata_amt(d,
depreciation_amount, schedule_date, to_date)
+ monthly_schedule_date = add_months(schedule_date, 1)
+
schedule_date = add_days(schedule_date, days)
+ last_schedule_date = schedule_date
if not depreciation_amount: continue
value_after_depreciation -= flt(depreciation_amount,
@@ -171,13 +187,50 @@ class Asset(AccountsController):
skip_row = True
if depreciation_amount > 0:
- self.append("schedules", {
- "schedule_date": schedule_date,
- "depreciation_amount": depreciation_amount,
- "depreciation_method": d.depreciation_method,
- "finance_book": d.finance_book,
- "finance_book_id": d.idx
- })
+ # With monthly depreciation, each depreciation is divided by months remaining until next date
+ if self.allow_monthly_depreciation:
+ # month range is 1 to 12
+ # In pro rata case, for first and last depreciation, month range would be different
+ month_range = months \
+ if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \
+ else d.frequency_of_depreciation
+
+ for r in range(month_range):
+ if (has_pro_rata and n == 0):
+ # For first entry of monthly depr
+ if r == 0:
+ days_until_first_depr = date_diff(monthly_schedule_date, self.available_for_use_date)
+ per_day_amt = depreciation_amount / days
+ depreciation_amount_for_current_month = per_day_amt * days_until_first_depr
+ depreciation_amount -= depreciation_amount_for_current_month
+ date = monthly_schedule_date
+ amount = depreciation_amount_for_current_month
+ else:
+ date = add_months(monthly_schedule_date, r)
+ amount = depreciation_amount / (month_range - 1)
+ elif (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) and r == cint(month_range) - 1:
+ # For last entry of monthly depr
+ date = last_schedule_date
+ amount = depreciation_amount / month_range
+ else:
+ date = add_months(monthly_schedule_date, r)
+ amount = depreciation_amount / month_range
+
+ self.append("schedules", {
+ "schedule_date": date,
+ "depreciation_amount": amount,
+ "depreciation_method": d.depreciation_method,
+ "finance_book": d.finance_book,
+ "finance_book_id": d.idx
+ })
+ else:
+ self.append("schedules", {
+ "schedule_date": schedule_date,
+ "depreciation_amount": depreciation_amount,
+ "depreciation_method": d.depreciation_method,
+ "finance_book": d.finance_book,
+ "finance_book_id": d.idx
+ })
def check_is_pro_rata(self, row):
has_pro_rata = False
@@ -424,7 +477,7 @@ def update_maintenance_status():
asset.set_status('Out of Order')
def make_post_gl_entry():
- if is_cwip_accounting_disabled():
+ if not is_cwip_accounting_enabled(self.company, self.asset_category):
return
assets = frappe.db.sql_list(""" select name from `tabAsset`
@@ -574,17 +627,23 @@ def make_journal_entry(asset_name):
return je
-def is_cwip_accounting_disabled():
- return cint(frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting"))
+def is_cwip_accounting_enabled(company, asset_category=None):
+ enable_cwip_in_company = cint(frappe.db.get_value("Company", company, "enable_cwip_accounting"))
+
+ if enable_cwip_in_company or not asset_category:
+ return enable_cwip_in_company
+
+ return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting"))
def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
days = date_diff(to_date, from_date)
+ months = month_diff(to_date, from_date)
total_days = get_total_days(to_date, row.frequency_of_depreciation)
- return (depreciation_amount * flt(days)) / flt(total_days), days
+ return (depreciation_amount * flt(days)) / flt(total_days), days, months
def get_total_days(date, frequency):
period_start_date = add_months(date,
cint(frequency) * -1)
- return date_diff(date, period_start_date)
\ No newline at end of file
+ return date_diff(date, period_start_date)
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index c09b94fa8e..7085b31e05 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -14,7 +14,6 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchas
class TestAsset(unittest.TestCase):
def setUp(self):
set_depreciation_settings_in_company()
- remove_prorated_depreciation_schedule()
create_asset_data()
frappe.db.sql("delete from `tabTax Rule`")
@@ -70,11 +69,13 @@ class TestAsset(unittest.TestCase):
{"voucher_type": "Purchase Invoice", "voucher_no": pi.name}))
def test_is_fixed_asset_set(self):
+ asset = create_asset(is_existing_asset = 1)
doc = frappe.new_doc('Purchase Invoice')
doc.supplier = '_Test Supplier'
doc.append('items', {
'item_code': 'Macbook Pro',
- 'qty': 1
+ 'qty': 1,
+ 'asset': asset.name
})
doc.set_missing_values()
@@ -200,7 +201,6 @@ class TestAsset(unittest.TestCase):
self.assertEqual(schedules, expected_schedules)
def test_schedule_for_prorated_straight_line_method(self):
- set_prorated_depreciation_schedule()
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=100000.0, location="Test Location")
@@ -233,8 +233,6 @@ class TestAsset(unittest.TestCase):
self.assertEqual(schedules, expected_schedules)
- remove_prorated_depreciation_schedule()
-
def test_depreciation(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=100000.0, location="Test Location")
@@ -487,6 +485,8 @@ class TestAsset(unittest.TestCase):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_invoice as make_purchase_invoice_from_pr)
+ #frappe.db.set_value("Asset Category","Computers","enable_cwip_accounting", 1)
+
pr = make_purchase_receipt(item_code="Macbook Pro",
qty=1, rate=5000, do_not_submit=True, location="Test Location")
@@ -565,6 +565,7 @@ class TestAsset(unittest.TestCase):
where voucher_type='Asset' and voucher_no = %s
order by account""", asset_doc.name)
+
self.assertEqual(gle, expected_gle)
def test_expense_head(self):
@@ -575,7 +576,6 @@ class TestAsset(unittest.TestCase):
self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
-
def create_asset_data():
if not frappe.db.exists("Asset Category", "Computers"):
create_asset_category()
@@ -596,15 +596,15 @@ def create_asset(**args):
asset = frappe.get_doc({
"doctype": "Asset",
- "asset_name": "Macbook Pro 1",
+ "asset_name": args.asset_name or "Macbook Pro 1",
"asset_category": "Computers",
- "item_code": "Macbook Pro",
- "company": "_Test Company",
+ "item_code": args.item_code or "Macbook Pro",
+ "company": args.company or"_Test Company",
"purchase_date": "2015-01-01",
"calculate_depreciation": 0,
"gross_purchase_amount": 100000,
"expected_value_after_useful_life": 10000,
- "warehouse": "_Test Warehouse - _TC",
+ "warehouse": args.warehouse or "_Test Warehouse - _TC",
"available_for_use_date": "2020-06-06",
"location": "Test Location",
"asset_owner": "Company",
@@ -616,6 +616,9 @@ def create_asset(**args):
except frappe.DuplicateEntryError:
pass
+ if args.submit:
+ asset.submit()
+
return asset
def create_asset_category():
@@ -623,6 +626,7 @@ def create_asset_category():
asset_category.asset_category_name = "Computers"
asset_category.total_number_of_depreciations = 3
asset_category.frequency_of_depreciation = 3
+ asset_category.enable_cwip_accounting = 1
asset_category.append("accounts", {
"company_name": "_Test Company",
"fixed_asset_account": "_Test Fixed Asset - _TC",
@@ -656,19 +660,4 @@ def set_depreciation_settings_in_company():
company.save()
# Enable booking asset depreciation entry automatically
- frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
-
-def remove_prorated_depreciation_schedule():
- asset_settings = frappe.get_doc("Asset Settings", "Asset Settings")
- asset_settings.schedule_based_on_fiscal_year = 0
- asset_settings.save()
-
- frappe.db.commit()
-
-def set_prorated_depreciation_schedule():
- asset_settings = frappe.get_doc("Asset Settings", "Asset Settings")
- asset_settings.schedule_based_on_fiscal_year = 1
- asset_settings.number_of_days_in_fiscal_year = 360
- asset_settings.save()
-
- frappe.db.commit()
+ frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_category/asset_category.json b/erpnext/assets/doctype/asset_category/asset_category.json
index 882cbe2eaa..7483b41d4d 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.json
+++ b/erpnext/assets/doctype/asset_category/asset_category.json
@@ -1,284 +1,115 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:asset_category_name",
- "beta": 0,
- "creation": "2016-03-01 17:41:39.778765",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:asset_category_name",
+ "creation": "2016-03-01 17:41:39.778765",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+ "asset_category_name",
+ "column_break_3",
+ "depreciation_options",
+ "enable_cwip_accounting",
+ "finance_book_detail",
+ "finance_books",
+ "section_break_2",
+ "accounts"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "asset_category_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": "Asset Category Name",
- "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": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "asset_category_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Asset Category Name",
+ "reqd": 1,
+ "unique": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "column_break_3",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "finance_book_detail",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Finance Book Detail",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "finance_book_detail",
+ "fieldtype": "Section Break",
+ "label": "Finance Book Detail"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "finance_books",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Finance Books",
- "length": 0,
- "no_copy": 0,
- "options": "Asset Finance Book",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "finance_books",
+ "fieldtype": "Table",
+ "label": "Finance Books",
+ "options": "Asset Finance Book"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_2",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Accounts",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "section_break_2",
+ "fieldtype": "Section Break",
+ "label": "Accounts"
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "accounts",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Accounts",
- "length": 0,
- "no_copy": 0,
- "options": "Asset Category Account",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "accounts",
+ "fieldtype": "Table",
+ "label": "Accounts",
+ "options": "Asset Category Account",
+ "reqd": 1
+ },
+ {
+ "fieldname": "depreciation_options",
+ "fieldtype": "Section Break",
+ "label": "Depreciation Options"
+ },
+ {
+ "default": "0",
+ "fieldname": "enable_cwip_accounting",
+ "fieldtype": "Check",
+ "label": "Enable Capital Work in Progress Accounting"
}
- ],
- "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-05-12 14:56:04.116425",
- "modified_by": "Administrator",
- "module": "Assets",
- "name": "Asset Category",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "modified": "2019-10-11 12:19:59.759136",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Category",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 1,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Accounts User",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "import": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "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": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "import": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "share": 1,
"write": 1
- },
+ },
{
- "amend": 0,
- "apply_user_permissions": 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": "Quality Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Quality Manager",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0
+ ],
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py
index bbdc6ec2cf..5cb634abcd 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.py
+++ b/erpnext/assets/doctype/asset_category/asset_category.py
@@ -10,11 +10,24 @@ from frappe.model.document import Document
class AssetCategory(Document):
def validate(self):
+ self.validate_finance_books()
+ self.validate_enable_cwip_accounting()
+
+ def validate_finance_books(self):
for d in self.finance_books:
for field in ("Total Number of Depreciations", "Frequency of Depreciation"):
if cint(d.get(frappe.scrub(field)))<1:
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
+ def validate_enable_cwip_accounting(self):
+ if self.enable_cwip_accounting :
+ for d in self.accounts:
+ cwip = frappe.db.get_value("Company",d.company_name,"enable_cwip_accounting")
+ if cwip:
+ frappe.throw(_
+ ("CWIP is enabled globally in Company {1}. To enable it in Asset Category, first disable it in {1} ").format(
+ frappe.bold(d.idx), frappe.bold(d.company_name)))
+
@frappe.whitelist()
def get_asset_category_account(asset, fieldname, account=None, asset_category = None, company = None):
if not asset_category and company:
diff --git a/erpnext/assets/doctype/asset_settings/asset_settings.js b/erpnext/assets/doctype/asset_settings/asset_settings.js
deleted file mode 100644
index 3b421486c3..0000000000
--- a/erpnext/assets/doctype/asset_settings/asset_settings.js
+++ /dev/null
@@ -1,5 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.ui.form.on('Asset Settings', {
-});
diff --git a/erpnext/assets/doctype/asset_settings/asset_settings.json b/erpnext/assets/doctype/asset_settings/asset_settings.json
deleted file mode 100644
index edc5ce169c..0000000000
--- a/erpnext/assets/doctype/asset_settings/asset_settings.json
+++ /dev/null
@@ -1,148 +0,0 @@
-{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2018-01-03 10:30:32.983381",
- "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,
- "fetch_if_empty": 0,
- "fieldname": "depreciation_options",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Depreciation Options",
- "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,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "disable_cwip_accounting",
- "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": "Disable CWIP Accounting",
- "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": 1,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-05-26 18:31:19.930563",
- "modified_by": "Administrator",
- "module": "Assets",
- "name": "Asset Settings",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 0,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 1
- },
- {
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 0,
- "role": "Accounts 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,
- "track_views": 0
-}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_settings/asset_settings.py b/erpnext/assets/doctype/asset_settings/asset_settings.py
deleted file mode 100644
index e303ebd23f..0000000000
--- a/erpnext/assets/doctype/asset_settings/asset_settings.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 AssetSettings(Document):
- pass
diff --git a/erpnext/assets/doctype/asset_settings/test_asset_settings.js b/erpnext/assets/doctype/asset_settings/test_asset_settings.js
deleted file mode 100644
index eac2c928f3..0000000000
--- a/erpnext/assets/doctype/asset_settings/test_asset_settings.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: Asset Settings", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Asset Settings
- () => frappe.tests.make('Asset Settings', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/assets/doctype/asset_settings/test_asset_settings.py b/erpnext/assets/doctype/asset_settings/test_asset_settings.py
deleted file mode 100644
index 75f146a27e..0000000000
--- a/erpnext/assets/doctype/asset_settings/test_asset_settings.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 TestAssetSettings(unittest.TestCase):
- pass
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 9ad06f9b7e..2f0cfa64fc 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -134,7 +134,7 @@ frappe.ui.form.on("Request for Quotation",{
if (args.search_type === "Tag" && args.tag) {
return frappe.call({
type: "GET",
- method: "frappe.desk.tags.get_tagged_docs",
+ method: "frappe.desk.doctype.tag.tag.get_tagged_docs",
args: {
"doctype": "Supplier",
"tag": args.tag
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index a10ce46e33..95db33b0f8 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -344,13 +344,9 @@ def get_item_from_material_requests_based_on_supplier(source_name, target_doc =
@frappe.whitelist()
def get_supplier_tag():
- data = frappe.db.sql("select _user_tags from `tabSupplier`")
-
- tags = []
- for tag in data:
- tags += filter(bool, tag[0].split(","))
-
- tags = list(set(tags))
-
- return tags
+ if not frappe.cache().hget("Supplier", "Tags"):
+ filters = {"document_type": "Supplier"}
+ tags = list(set([tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag]))
+ frappe.cache().hset("Supplier", "Tags", tags)
+ return frappe.cache().hget("Supplier", "Tags")
diff --git a/erpnext/config/assets.py b/erpnext/config/assets.py
index 3c9452f5a4..4cf7cf0806 100644
--- a/erpnext/config/assets.py
+++ b/erpnext/config/assets.py
@@ -21,10 +21,6 @@ def get_data():
"name": "Asset Category",
"onboard": 1,
},
- {
- "type": "doctype",
- "name": "Asset Settings",
- },
{
"type": "doctype",
"name": "Asset Movement",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 67f453d2b3..9415228467 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1193,8 +1193,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
frappe.throw(_("Cannot set quantity less than received quantity"))
child_item.qty = flt(d.get("qty"))
+ precision = child_item.precision("rate") or 2
- if flt(child_item.billed_amt) > (flt(d.get("rate")) * flt(d.get("qty"))):
+ if flt(child_item.billed_amt, precision) > flt(flt(d.get("rate")) * flt(d.get("qty")), precision):
frappe.throw(_("Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.")
.format(child_item.idx, child_item.item_code))
else:
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 9d1389c977..2b2c27b052 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -49,7 +49,8 @@ status_map = {
["Submitted", "eval:self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
- ["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"],
+ ["Debit Note Issued",
+ "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
@@ -118,7 +119,6 @@ class StatusUpdater(Document):
if self.doctype in status_map:
_status = self.status
-
if status and update:
self.db_set("status", status)
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.json b/erpnext/crm/doctype/email_campaign/email_campaign.json
index 3259136275..736a9d6173 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.json
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.json
@@ -52,7 +52,8 @@
"fieldtype": "Select",
"in_list_view": 1,
"label": "Email Campaign For ",
- "options": "\nLead\nContact"
+ "options": "\nLead\nContact",
+ "reqd": 1
},
{
"fieldname": "recipient",
@@ -69,7 +70,7 @@
"options": "User"
}
],
- "modified": "2019-07-12 13:47:37.261213",
+ "modified": "2019-11-11 17:18:47.342839",
"modified_by": "Administrator",
"module": "CRM",
"name": "Email Campaign",
diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py
index 98e4927beb..3050d05a7c 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -73,13 +73,13 @@ def send_mail(entry, email_campaign):
email_template = frappe.get_doc("Email Template", entry.get("email_template"))
sender = frappe.db.get_value("User", email_campaign.get("sender"), 'email')
-
+ context = {"doc": frappe.get_doc(email_campaign.email_campaign_for, email_campaign.recipient)}
# send mail and link communication to document
comm = make(
doctype = "Email Campaign",
name = email_campaign.name,
subject = email_template.get("subject"),
- content = email_template.get("response"),
+ content = frappe.render_template(email_template.get("response"), context),
sender = sender,
recipients = recipient,
communication_medium = "Email",
diff --git a/erpnext/education/doctype/education_settings/education_settings.json b/erpnext/education/doctype/education_settings/education_settings.json
index 32b5fb8198..967a030fd2 100644
--- a/erpnext/education/doctype/education_settings/education_settings.json
+++ b/erpnext/education/doctype/education_settings/education_settings.json
@@ -11,6 +11,7 @@
"validate_batch",
"validate_course",
"academic_term_reqd",
+ "user_creation_skip",
"section_break_7",
"instructor_created_by",
"web_academy_settings_section",
@@ -91,6 +92,13 @@
"fieldname": "enable_lms",
"fieldtype": "Check",
"label": "Enable LMS"
+ },
+ {
+ "default": "0",
+ "description": "By default, a new User is created for every new Student. If enabled, no new User will be created when a new Student is created.",
+ "fieldname": "user_creation_skip",
+ "fieldtype": "Check",
+ "label": "Skip User creation for new Student"
}
],
"issingle": 1,
@@ -133,4 +141,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index 705c6e4e98..9af5e22913 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -40,7 +40,8 @@ class Student(Document):
frappe.throw(_("Student {0} exist against student applicant {1}").format(student[0][0], self.student_applicant))
def after_insert(self):
- self.create_student_user()
+ if not frappe.get_single('Education Settings').user_creation_skip:
+ self.create_student_user()
def create_student_user(self):
"""Create a website user for student creation if not already exists"""
diff --git a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
index 0b6ea8cc7c..28c2ab9e54 100644
--- a/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
+++ b/erpnext/erpnext_integrations/connectors/woocommerce_connection.py
@@ -1,10 +1,8 @@
from __future__ import unicode_literals
import frappe, base64, hashlib, hmac, json
-import datetime
from frappe import _
-
def verify_request():
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
sig = base64.b64encode(
@@ -30,191 +28,149 @@ def order(*args, **kwargs):
frappe.log_error(error_message, "WooCommerce Error")
raise
-
def _order(*args, **kwargs):
woocommerce_settings = frappe.get_doc("Woocommerce Settings")
if frappe.flags.woocomm_test_order_data:
- fd = frappe.flags.woocomm_test_order_data
+ order = frappe.flags.woocomm_test_order_data
event = "created"
elif frappe.request and frappe.request.data:
verify_request()
- fd = json.loads(frappe.request.data)
+ try:
+ order = json.loads(frappe.request.data)
+ except ValueError:
+ #woocommerce returns 'webhook_id=value' for the first request which is not JSON
+ order = frappe.request.data
event = frappe.get_request_header("X-Wc-Webhook-Event")
else:
return "success"
if event == "created":
- raw_billing_data = fd.get("billing")
- customer_woo_com_email = raw_billing_data.get("email")
-
- if frappe.get_value("Customer",{"woocommerce_email": customer_woo_com_email}):
- # Edit
- link_customer_and_address(raw_billing_data,1)
- else:
- # Create
- link_customer_and_address(raw_billing_data,0)
-
-
- items_list = fd.get("line_items")
- for item in items_list:
-
- item_woo_com_id = item.get("product_id")
-
- if frappe.get_value("Item",{"woocommerce_id": item_woo_com_id}):
- #Edit
- link_item(item,1)
- else:
- link_item(item,0)
-
-
+ raw_billing_data = order.get("billing")
customer_name = raw_billing_data.get("first_name") + " " + raw_billing_data.get("last_name")
+ link_customer_and_address(raw_billing_data, customer_name)
+ link_items(order.get("line_items"), woocommerce_settings)
+ create_sales_order(order, woocommerce_settings, customer_name)
- new_sales_order = frappe.new_doc("Sales Order")
- new_sales_order.customer = customer_name
-
- created_date = fd.get("date_created").split("T")
- new_sales_order.transaction_date = created_date[0]
-
- new_sales_order.po_no = fd.get("id")
- new_sales_order.woocommerce_id = fd.get("id")
- new_sales_order.naming_series = woocommerce_settings.sales_order_series or "SO-WOO-"
-
- placed_order_date = created_date[0]
- raw_date = datetime.datetime.strptime(placed_order_date, "%Y-%m-%d")
- raw_delivery_date = frappe.utils.add_to_date(raw_date,days = 7)
- order_delivery_date_str = raw_delivery_date.strftime('%Y-%m-%d')
- order_delivery_date = str(order_delivery_date_str)
-
- new_sales_order.delivery_date = order_delivery_date
- default_set_company = frappe.get_doc("Global Defaults")
- company = raw_billing_data.get("company") or default_set_company.default_company
- found_company = frappe.get_doc("Company",{"name":company})
- company_abbr = found_company.abbr
-
- new_sales_order.company = company
-
- for item in items_list:
- woocomm_item_id = item.get("product_id")
- found_item = frappe.get_doc("Item",{"woocommerce_id": woocomm_item_id})
-
- ordered_items_tax = item.get("total_tax")
-
- new_sales_order.append("items",{
- "item_code": found_item.item_code,
- "item_name": found_item.item_name,
- "description": found_item.item_name,
- "delivery_date":order_delivery_date,
- "uom": woocommerce_settings.uom or _("Nos"),
- "qty": item.get("quantity"),
- "rate": item.get("price"),
- "warehouse": woocommerce_settings.warehouse or "Stores" + " - " + company_abbr
- })
-
- add_tax_details(new_sales_order,ordered_items_tax,"Ordered Item tax",0)
-
- # shipping_details = fd.get("shipping_lines") # used for detailed order
- shipping_total = fd.get("shipping_total")
- shipping_tax = fd.get("shipping_tax")
-
- add_tax_details(new_sales_order,shipping_tax,"Shipping Tax",1)
- add_tax_details(new_sales_order,shipping_total,"Shipping Total",1)
-
- new_sales_order.submit()
-
- frappe.db.commit()
-
-def link_customer_and_address(raw_billing_data,customer_status):
-
- if customer_status == 0:
- # create
+def link_customer_and_address(raw_billing_data, customer_name):
+ customer_woo_com_email = raw_billing_data.get("email")
+ customer_exists = frappe.get_value("Customer", {"woocommerce_email": customer_woo_com_email})
+ if not customer_exists:
+ # Create Customer
customer = frappe.new_doc("Customer")
- address = frappe.new_doc("Address")
-
- if customer_status == 1:
- # Edit
- customer_woo_com_email = raw_billing_data.get("email")
- customer = frappe.get_doc("Customer",{"woocommerce_email": customer_woo_com_email})
+ else:
+ # Edit Customer
+ customer = frappe.get_doc("Customer", {"woocommerce_email": customer_woo_com_email})
old_name = customer.customer_name
- full_name = str(raw_billing_data.get("first_name"))+ " "+str(raw_billing_data.get("last_name"))
- customer.customer_name = full_name
- customer.woocommerce_email = str(raw_billing_data.get("email"))
- customer.save()
- frappe.db.commit()
+ customer.customer_name = customer_name
+ customer.woocommerce_email = customer_woo_com_email
+ customer.flags.ignore_mandatory = True
+ customer.save()
- if customer_status == 1:
- frappe.rename_doc("Customer", old_name, full_name)
- address = frappe.get_doc("Address",{"woocommerce_email":customer_woo_com_email})
- customer = frappe.get_doc("Customer",{"woocommerce_email": customer_woo_com_email})
+ if customer_exists:
+ frappe.rename_doc("Customer", old_name, customer_name)
+ address = frappe.get_doc("Address", {"woocommerce_email": customer_woo_com_email})
+ else:
+ address = frappe.new_doc("Address")
address.address_line1 = raw_billing_data.get("address_1", "Not Provided")
address.address_line2 = raw_billing_data.get("address_2", "Not Provided")
address.city = raw_billing_data.get("city", "Not Provided")
- address.woocommerce_email = str(raw_billing_data.get("email"))
- address.address_type = "Shipping"
- address.country = frappe.get_value("Country", filters={"code":raw_billing_data.get("country", "IN").lower()})
- address.state = raw_billing_data.get("state")
- address.pincode = str(raw_billing_data.get("postcode"))
- address.phone = str(raw_billing_data.get("phone"))
- address.email_id = str(raw_billing_data.get("email"))
-
+ address.woocommerce_email = customer_woo_com_email
+ address.address_type = "Billing"
+ address.country = frappe.get_value("Country", {"code": raw_billing_data.get("country", "IN").lower()})
+ address.state = raw_billing_data.get("state")
+ address.pincode = raw_billing_data.get("postcode")
+ address.phone = raw_billing_data.get("phone")
+ address.email_id = customer_woo_com_email
address.append("links", {
"link_doctype": "Customer",
"link_name": customer.customer_name
})
+ address.flags.ignore_mandatory = True
+ address = address.save()
- address.save()
- frappe.db.commit()
-
- if customer_status == 1:
-
- address = frappe.get_doc("Address",{"woocommerce_email":customer_woo_com_email})
+ if customer_exists:
old_address_title = address.name
- new_address_title = customer.customer_name+"-billing"
+ new_address_title = customer.customer_name + "-billing"
address.address_title = customer.customer_name
address.save()
- frappe.rename_doc("Address",old_address_title,new_address_title)
+ frappe.rename_doc("Address", old_address_title, new_address_title)
- frappe.db.commit()
-
-def link_item(item_data,item_status):
- woocommerce_settings = frappe.get_doc("Woocommerce Settings")
-
- if item_status == 0:
- #Create Item
- item = frappe.new_doc("Item")
-
- if item_status == 1:
- #Edit Item
+def link_items(items_list, woocommerce_settings):
+ for item_data in items_list:
item_woo_com_id = item_data.get("product_id")
- item = frappe.get_doc("Item",{"woocommerce_id": item_woo_com_id})
- item.item_name = str(item_data.get("name"))
- item.item_code = "woocommerce - " + str(item_data.get("product_id"))
- item.woocommerce_id = str(item_data.get("product_id"))
- item.item_group = _("WooCommerce Products")
- item.stock_uom = woocommerce_settings.uom or _("Nos")
- item.save()
+ if frappe.get_value("Item", {"woocommerce_id": item_woo_com_id}):
+ #Edit Item
+ item = frappe.get_doc("Item", {"woocommerce_id": item_woo_com_id})
+ else:
+ #Create Item
+ item = frappe.new_doc("Item")
+
+ item.item_name = item_data.get("name")
+ item.item_code = _("woocommerce - {0}").format(item_data.get("product_id"))
+ item.woocommerce_id = item_data.get("product_id")
+ item.item_group = _("WooCommerce Products")
+ item.stock_uom = woocommerce_settings.uom or _("Nos")
+ item.flags.ignore_mandatory = True
+ item.save()
+
+def create_sales_order(order, woocommerce_settings, customer_name):
+ new_sales_order = frappe.new_doc("Sales Order")
+ new_sales_order.customer = customer_name
+
+ new_sales_order.po_no = new_sales_order.woocommerce_id = order.get("id")
+ new_sales_order.naming_series = woocommerce_settings.sales_order_series or "SO-WOO-"
+
+ created_date = order.get("date_created").split("T")
+ new_sales_order.transaction_date = created_date[0]
+ delivery_after = woocommerce_settings.delivery_after_days or 7
+ new_sales_order.delivery_date = frappe.utils.add_days(created_date[0], delivery_after)
+
+ new_sales_order.company = woocommerce_settings.company
+
+ set_items_in_sales_order(new_sales_order, woocommerce_settings, order)
+ new_sales_order.flags.ignore_mandatory = True
+ new_sales_order.insert()
+ new_sales_order.submit()
+
frappe.db.commit()
-def add_tax_details(sales_order,price,desc,status):
+def set_items_in_sales_order(new_sales_order, woocommerce_settings, order):
+ company_abbr = frappe.db.get_value('Company', woocommerce_settings.company, 'abbr')
- woocommerce_settings = frappe.get_doc("Woocommerce Settings")
+ for item in order.get("line_items"):
+ woocomm_item_id = item.get("product_id")
+ found_item = frappe.get_doc("Item", {"woocommerce_id": woocomm_item_id})
- if status == 0:
- # Product taxes
- account_head_type = woocommerce_settings.tax_account
+ ordered_items_tax = item.get("total_tax")
- if status == 1:
- # Shipping taxes
- account_head_type = woocommerce_settings.f_n_f_account
+ new_sales_order.append("items",{
+ "item_code": found_item.item_code,
+ "item_name": found_item.item_name,
+ "description": found_item.item_name,
+ "delivery_date": new_sales_order.delivery_date,
+ "uom": woocommerce_settings.uom or _("Nos"),
+ "qty": item.get("quantity"),
+ "rate": item.get("price"),
+ "warehouse": woocommerce_settings.warehouse or _("Stores - {0}").format(company_abbr)
+ })
- sales_order.append("taxes",{
- "charge_type":"Actual",
- "account_head": account_head_type,
- "tax_amount": price,
- "description": desc
- })
+ add_tax_details(new_sales_order, ordered_items_tax, "Ordered Item tax", woocommerce_settings.tax_account)
+
+ # shipping_details = order.get("shipping_lines") # used for detailed order
+
+ add_tax_details(new_sales_order, order.get("shipping_tax"), "Shipping Tax", woocommerce_settings.f_n_f_account)
+ add_tax_details(new_sales_order, order.get("shipping_total"), "Shipping Total", woocommerce_settings.f_n_f_account)
+
+def add_tax_details(sales_order, price, desc, tax_account_head):
+ sales_order.append("taxes", {
+ "charge_type":"Actual",
+ "account_head": tax_account_head,
+ "tax_amount": price,
+ "description": desc
+ })
diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
index 0cad0cca72..64c3b2d273 100644
--- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
+++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py
@@ -50,7 +50,7 @@ class ShopifySettings(Document):
deleted_webhooks = []
for d in self.webhooks:
- url = get_shopify_url('admin/api/2019-04/webhooks.json'.format(d.webhook_id), self)
+ url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self)
try:
res = session.delete(url, headers=get_header(self))
res.raise_for_status()
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.json b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.json
index dd3c24dce5..956ae09cbd 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.json
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.json
@@ -1,694 +1,175 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
"creation": "2018-02-12 15:10:05.495713",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "enable_sync",
+ "sb_00",
+ "woocommerce_server_url",
+ "secret",
+ "cb_00",
+ "api_consumer_key",
+ "api_consumer_secret",
+ "sb_accounting_details",
+ "tax_account",
+ "column_break_10",
+ "f_n_f_account",
+ "defaults_section",
+ "creation_user",
+ "warehouse",
+ "sales_order_series",
+ "column_break_14",
+ "company",
+ "delivery_after_days",
+ "uom",
+ "endpoints",
+ "endpoint"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
+ "default": "0",
"fieldname": "enable_sync",
"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": "Enable Sync",
- "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
+ "label": "Enable Sync"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "sb_00",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "woocommerce_server_url",
"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": "Woocommerce Server URL",
- "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
+ "label": "Woocommerce Server URL"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "secret",
"fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Secret",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "cb_00",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "api_consumer_key",
"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": "API consumer key",
- "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
+ "label": "API consumer key"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "api_consumer_secret",
"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": "API consumer secret",
- "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
+ "label": "API consumer secret"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "sb_accounting_details",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Accounting Details",
- "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
+ "label": "Accounting Details"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "tax_account",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Tax Account",
- "length": 0,
- "no_copy": 0,
"options": "Account",
- "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
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "column_break_10",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "f_n_f_account",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Freight and Forwarding Account",
- "length": 0,
- "no_copy": 0,
"options": "Account",
- "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
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "defaults_section",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Defaults",
- "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
+ "label": "Defaults"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "The user that will be used to create Customers, Items and Sales Orders. This user should have the relevant permissions.",
- "fetch_if_empty": 0,
"fieldname": "creation_user",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Creation User",
- "length": 0,
- "no_copy": 0,
"options": "User",
- "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
+ "reqd": 1
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "This warehouse will be used to create Sale Orders. The fallback warehouse is \"Stores\".",
- "fetch_if_empty": 0,
+ "description": "This warehouse will be used to create Sales Orders. The fallback warehouse is \"Stores\".",
"fieldname": "warehouse",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Warehouse",
- "length": 0,
- "no_copy": 0,
- "options": "Warehouse",
- "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
+ "options": "Warehouse"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "column_break_14",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "The fallback series is \"SO-WOO-\".",
- "fetch_if_empty": 0,
"fieldname": "sales_order_series",
"fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Sales Order Series",
- "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
+ "label": "Sales Order Series"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "This is the default UOM used for items and Sales orders. The fallback UOM is \"Nos\".",
- "fetch_if_empty": 0,
"fieldname": "uom",
"fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "UOM",
- "length": 0,
- "no_copy": 0,
- "options": "UOM",
- "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
+ "options": "UOM"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "endpoints",
"fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Endpoints",
- "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
+ "label": "Endpoints"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
"fieldname": "endpoint",
"fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
"label": "Endpoint",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "read_only": 1
+ },
+ {
+ "description": "This company will be used to create Sales Orders.",
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "reqd": 1
+ },
+ {
+ "description": "This is the default offset (days) for the Delivery Date in Sales Orders. The fallback offset is 7 days from the order placement date.",
+ "fieldname": "delivery_after_days",
+ "fieldtype": "Int",
+ "label": "Delivery After (Days)"
}
],
- "has_web_view": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "in_create": 0,
- "is_submittable": 0,
"issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "menu_index": 0,
- "modified": "2019-04-08 17:04:16.720696",
+ "modified": "2019-11-04 00:45:21.232096",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "Woocommerce Settings",
- "name_case": "",
"owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
"create": 1,
- "delete": 0,
"email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
"print": 1,
"read": 1,
- "report": 0,
"role": "System Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"write": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
index 055684d445..bd072f40a1 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
@@ -8,6 +8,7 @@ from frappe import _
from frappe.utils.nestedset import get_root_of
from frappe.model.document import Document
from six.moves.urllib.parse import urlparse
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
class WoocommerceSettings(Document):
def validate(self):
@@ -17,75 +18,21 @@ class WoocommerceSettings(Document):
def create_delete_custom_fields(self):
if self.enable_sync:
+ custom_fields = {}
# create
- create_custom_field_id_and_check_status = False
- create_custom_field_email_check = False
- names = ["Customer-woocommerce_id","Sales Order-woocommerce_id","Item-woocommerce_id","Address-woocommerce_id"]
- names_check_box = ["Customer-woocommerce_check","Sales Order-woocommerce_check","Item-woocommerce_check","Address-woocommerce_check"]
- email_names = ["Customer-woocommerce_email","Address-woocommerce_email"]
+ for doctype in ["Customer", "Sales Order", "Item", "Address"]:
+ df = dict(fieldname='woocommerce_id', label='Woocommerce ID', fieldtype='Data', read_only=1, print_hide=1)
+ create_custom_field(doctype, df)
- for i in zip(names,names_check_box):
-
- if not frappe.get_value("Custom Field",{"name":i[0]}) or not frappe.get_value("Custom Field",{"name":i[1]}):
- create_custom_field_id_and_check_status = True
- break
-
-
- if create_custom_field_id_and_check_status:
- names = ["Customer","Sales Order","Item","Address"]
- for name in names:
- custom = frappe.new_doc("Custom Field")
- custom.dt = name
- custom.label = "woocommerce_id"
- custom.read_only = 1
- custom.save()
-
- custom = frappe.new_doc("Custom Field")
- custom.dt = name
- custom.label = "woocommerce_check"
- custom.fieldtype = "Check"
- custom.read_only = 1
- custom.save()
-
- for i in email_names:
-
- if not frappe.get_value("Custom Field",{"name":i}):
- create_custom_field_email_check = True
- break;
-
- if create_custom_field_email_check:
- names = ["Customer","Address"]
- for name in names:
- custom = frappe.new_doc("Custom Field")
- custom.dt = name
- custom.label = "woocommerce_email"
- custom.read_only = 1
- custom.save()
-
- if not frappe.get_value("Item Group",{"name": _("WooCommerce Products")}):
+ for doctype in ["Customer", "Address"]:
+ df = dict(fieldname='woocommerce_email', label='Woocommerce Email', fieldtype='Data', read_only=1, print_hide=1)
+ create_custom_field(doctype, df)
+
+ if not frappe.get_value("Item Group", {"name": _("WooCommerce Products")}):
item_group = frappe.new_doc("Item Group")
item_group.item_group_name = _("WooCommerce Products")
item_group.parent_item_group = get_root_of("Item Group")
- item_group.save()
-
-
- elif not self.enable_sync:
- # delete
- names = ["Customer-woocommerce_id","Sales Order-woocommerce_id","Item-woocommerce_id","Address-woocommerce_id"]
- names_check_box = ["Customer-woocommerce_check","Sales Order-woocommerce_check","Item-woocommerce_check","Address-woocommerce_check"]
- email_names = ["Customer-woocommerce_email","Address-woocommerce_email"]
- for name in names:
- frappe.delete_doc("Custom Field",name)
-
- for name in names_check_box:
- frappe.delete_doc("Custom Field",name)
-
- for name in email_names:
- frappe.delete_doc("Custom Field",name)
-
- frappe.delete_doc("Item Group", _("WooCommerce Products"))
-
- frappe.db.commit()
+ item_group.insert()
def validate_settings(self):
if self.enable_sync:
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index b1855ec9bb..9e74bfd290 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -235,17 +235,16 @@ doc_events = {
("Sales Taxes and Charges Template", 'Price List'): {
"on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings"
},
-
"Website Settings": {
"validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products"
},
"Sales Invoice": {
- "on_submit": ["erpnext.regional.france.utils.create_transaction_log", "erpnext.regional.italy.utils.sales_invoice_on_submit"],
+ "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.regional.italy.utils.sales_invoice_on_submit"],
"on_cancel": "erpnext.regional.italy.utils.sales_invoice_on_cancel",
"on_trash": "erpnext.regional.check_deletion_permission"
},
"Payment Entry": {
- "on_submit": ["erpnext.regional.france.utils.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.make_status_as_paid"],
+ "on_submit": ["erpnext.regional.create_transaction_log", "erpnext.accounts.doctype.payment_request.payment_request.make_status_as_paid"],
"on_trash": "erpnext.regional.check_deletion_permission"
},
'Address': {
diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py
index 9f2f2013a7..d6b66da081 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -19,14 +19,20 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
approvers = []
department_details = {}
department_list = []
- employee_department = filters.get("department") or frappe.get_value("Employee", filters.get("employee"), "department")
+ employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver"], as_dict=True)
+ if employee.leave_approver:
+ approver = frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name'])
+ approvers.append(approver)
+ return approvers
+
+ employee_department = filters.get("department") or employee.department
if employee_department:
department_details = frappe.db.get_value("Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True)
if department_details:
department_list = frappe.db.sql("""select name from `tabDepartment` where lft <= %s
and rgt >= %s
and disabled=0
- order by lft desc""", (department_details.lft, department_details.rgt), as_list = True)
+ order by lft desc""", (department_details.lft, department_details.rgt), as_list=True)
if filters.get("doctype") == "Leave Application":
parentfield = "leave_approvers"
@@ -41,4 +47,4 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
and approver.parentfield = %s
and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True)
- return approvers
\ No newline at end of file
+ return approvers
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 3fc330e2d2..703ec06f83 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -167,10 +167,11 @@ class Employee(NestedSet):
def validate_status(self):
if self.status == 'Left':
reports_to = frappe.db.get_all('Employee',
- filters={'reports_to': self.name}
+ filters={'reports_to': self.name, 'status': "Active"},
+ fields=['name','employee_name']
)
if reports_to:
- link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name) for employee in reports_to]
+ link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name, label=employee.employee_name) for employee in reports_to]
throw(_("Employee status cannot be set to 'Left' as following employees are currently reporting to this employee: ")
+ ', '.join(link_to_employees), EmployeeLeftValidationError)
if not self.relieving_date:
diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py
index 162b697ac8..11ad83ba37 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -21,7 +21,7 @@ def get_data():
},
{
'label': _('Expense'),
- 'items': ['Expense Claim', 'Travel Request']
+ 'items': ['Expense Claim', 'Travel Request', 'Employee Advance']
},
{
'label': _('Benefit'),
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json
index 4e2778f48d..5c2f490171 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.json
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.json
@@ -1,435 +1,436 @@
{
- "allow_import": 1,
- "autoname": "naming_series:",
- "creation": "2013-01-10 16:34:14",
- "doctype": "DocType",
- "document_type": "Setup",
- "engine": "InnoDB",
- "field_order": [
- "naming_series",
- "employee",
- "employee_name",
- "department",
- "column_break_5",
- "expense_approver",
- "approval_status",
- "is_paid",
- "expense_details",
- "expenses",
- "sb1",
- "taxes",
- "transactions_section",
- "total_sanctioned_amount",
- "total_taxes_and_charges",
- "total_advance_amount",
- "column_break_17",
- "grand_total",
- "total_claimed_amount",
- "total_amount_reimbursed",
- "section_break_16",
- "posting_date",
- "vehicle_log",
- "task",
- "cb1",
- "remark",
- "title",
- "email_id",
- "accounting_details",
- "company",
- "mode_of_payment",
- "clearance_date",
- "column_break_24",
- "payable_account",
- "accounting_dimensions_section",
- "project",
- "dimension_col_break",
- "cost_center",
- "more_details",
- "status",
- "amended_from",
- "advance_payments",
- "advances"
- ],
- "fields": [
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "no_copy": 1,
- "options": "HR-EXP-.YYYY.-",
- "print_hide": 1,
- "reqd": 1,
- "set_only_once": 1
- },
- {
- "fieldname": "employee",
- "fieldtype": "Link",
- "in_global_search": 1,
- "label": "From Employee",
- "oldfieldname": "employee",
- "oldfieldtype": "Link",
- "options": "Employee",
- "reqd": 1,
- "search_index": 1
- },
- {
- "fetch_from": "employee.employee_name",
- "fieldname": "employee_name",
- "fieldtype": "Data",
- "in_global_search": 1,
- "label": "Employee Name",
- "oldfieldname": "employee_name",
- "oldfieldtype": "Data",
- "read_only": 1,
- "width": "150px"
- },
- {
- "fetch_from": "employee.department",
- "fieldname": "department",
- "fieldtype": "Link",
- "label": "Department",
- "options": "Department",
- "read_only": 1
- },
- {
- "fieldname": "column_break_5",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "expense_approver",
- "fieldtype": "Link",
- "label": "Expense Approver",
- "options": "User"
- },
- {
- "default": "Draft",
- "fieldname": "approval_status",
- "fieldtype": "Select",
- "label": "Approval Status",
- "no_copy": 1,
- "options": "Draft\nApproved\nRejected",
- "search_index": 1
- },
- {
- "fieldname": "total_claimed_amount",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Total Claimed Amount",
- "no_copy": 1,
- "oldfieldname": "total_claimed_amount",
- "oldfieldtype": "Currency",
- "options": "Company:company:default_currency",
- "read_only": 1,
- "width": "160px"
- },
- {
- "fieldname": "total_sanctioned_amount",
- "fieldtype": "Currency",
- "label": "Total Sanctioned Amount",
- "no_copy": 1,
- "oldfieldname": "total_sanctioned_amount",
- "oldfieldtype": "Currency",
- "options": "Company:company:default_currency",
- "read_only": 1,
- "width": "160px"
- },
- {
- "default": "0",
- "depends_on": "eval:(doc.docstatus==0 || doc.is_paid)",
- "fieldname": "is_paid",
- "fieldtype": "Check",
- "label": "Is Paid"
- },
- {
- "fieldname": "expense_details",
- "fieldtype": "Section Break",
- "oldfieldtype": "Section Break"
- },
- {
- "fieldname": "expenses",
- "fieldtype": "Table",
- "label": "Expenses",
- "oldfieldname": "expense_voucher_details",
- "oldfieldtype": "Table",
- "options": "Expense Claim Detail",
- "reqd": 1
- },
- {
- "fieldname": "sb1",
- "fieldtype": "Section Break",
- "options": "Simple"
- },
- {
- "default": "Today",
- "fieldname": "posting_date",
- "fieldtype": "Date",
- "label": "Posting Date",
- "oldfieldname": "posting_date",
- "oldfieldtype": "Date",
- "reqd": 1
- },
- {
- "fieldname": "vehicle_log",
- "fieldtype": "Link",
- "label": "Vehicle Log",
- "options": "Vehicle Log",
- "read_only": 1
- },
- {
- "fieldname": "project",
- "fieldtype": "Link",
- "label": "Project",
- "options": "Project"
- },
- {
- "fieldname": "task",
- "fieldtype": "Link",
- "label": "Task",
- "options": "Task",
- "remember_last_selected_value": 1
- },
- {
- "fieldname": "cb1",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "total_amount_reimbursed",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Total Amount Reimbursed",
- "no_copy": 1,
- "options": "Company:company:default_currency",
- "read_only": 1
- },
- {
- "fieldname": "remark",
- "fieldtype": "Small Text",
- "label": "Remark",
- "no_copy": 1,
- "oldfieldname": "remark",
- "oldfieldtype": "Small Text"
- },
- {
- "allow_on_submit": 1,
- "default": "{employee_name}",
- "fieldname": "title",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Title",
- "no_copy": 1
- },
- {
- "fieldname": "email_id",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Employees Email Id",
- "oldfieldname": "email_id",
- "oldfieldtype": "Data",
- "print_hide": 1
- },
- {
- "fieldname": "accounting_details",
- "fieldtype": "Section Break",
- "label": "Accounting Details"
- },
- {
- "fieldname": "company",
- "fieldtype": "Link",
- "label": "Company",
- "oldfieldname": "company",
- "oldfieldtype": "Link",
- "options": "Company",
- "remember_last_selected_value": 1,
- "reqd": 1
- },
- {
- "depends_on": "is_paid",
- "fieldname": "mode_of_payment",
- "fieldtype": "Link",
- "label": "Mode of Payment",
- "options": "Mode of Payment"
- },
- {
- "fieldname": "clearance_date",
- "fieldtype": "Date",
- "label": "Clearance Date"
- },
- {
- "fieldname": "column_break_24",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "payable_account",
- "fieldtype": "Link",
- "label": "Payable Account",
- "options": "Account"
- },
- {
- "fieldname": "cost_center",
- "fieldtype": "Link",
- "label": "Cost Center",
- "options": "Cost Center"
- },
- {
- "collapsible": 1,
- "fieldname": "more_details",
- "fieldtype": "Section Break",
- "label": "More Details"
- },
- {
- "default": "Draft",
- "fieldname": "status",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Status",
- "no_copy": 1,
- "options": "Draft\nPaid\nUnpaid\nRejected\nSubmitted\nCancelled",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "fieldname": "amended_from",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Amended From",
- "no_copy": 1,
- "oldfieldname": "amended_from",
- "oldfieldtype": "Data",
- "options": "Expense Claim",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1,
- "width": "160px"
- },
- {
- "fieldname": "advance_payments",
- "fieldtype": "Section Break",
- "label": "Advance Payments"
- },
- {
- "fieldname": "advances",
- "fieldtype": "Table",
- "label": "Advances",
- "options": "Expense Claim Advance"
- },
- {
- "fieldname": "total_advance_amount",
- "fieldtype": "Currency",
- "label": "Total Advance Amount",
- "options": "Company:company:default_currency",
- "read_only": 1
- },
- {
- "fieldname": "accounting_dimensions_section",
- "fieldtype": "Section Break",
- "label": "Accounting Dimensions"
- },
- {
- "fieldname": "dimension_col_break",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "taxes",
- "fieldtype": "Table",
- "label": "Expense Taxes and Charges",
- "options": "Expense Taxes and Charges"
- },
- {
- "fieldname": "section_break_16",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "transactions_section",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "grand_total",
- "fieldtype": "Currency",
- "in_list_view": 1,
- "label": "Grand Total",
- "options": "Company:company:default_currency",
- "read_only": 1
- },
- {
- "fieldname": "column_break_17",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "total_taxes_and_charges",
- "fieldtype": "Currency",
- "label": "Total Taxes and Charges",
- "options": "Company:company:default_currency",
- "read_only": 1
- }
- ],
- "icon": "fa fa-money",
- "idx": 1,
- "is_submittable": 1,
- "modified": "2019-06-26 18:05:52.530462",
- "modified_by": "Administrator",
- "module": "HR",
- "name": "Expense Claim",
- "name_case": "Title Case",
- "owner": "harshada@webnotestech.com",
- "permissions": [
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "HR Manager",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "create": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Employee",
- "share": 1,
- "write": 1
- },
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Expense Approver",
- "share": 1,
- "submit": 1,
- "write": 1
- },
- {
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "HR User",
- "share": 1,
- "submit": 1,
- "write": 1
- }
- ],
- "search_fields": "employee,employee_name",
- "show_name_in_global_search": 1,
- "sort_field": "modified",
- "sort_order": "DESC",
- "timeline_field": "employee",
- "title_field": "title"
- }
\ No newline at end of file
+ "allow_import": 1,
+ "autoname": "naming_series:",
+ "creation": "2013-01-10 16:34:14",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "engine": "InnoDB",
+ "field_order": [
+ "naming_series",
+ "employee",
+ "employee_name",
+ "department",
+ "column_break_5",
+ "expense_approver",
+ "approval_status",
+ "is_paid",
+ "expense_details",
+ "expenses",
+ "sb1",
+ "taxes",
+ "transactions_section",
+ "total_sanctioned_amount",
+ "total_taxes_and_charges",
+ "total_advance_amount",
+ "column_break_17",
+ "grand_total",
+ "total_claimed_amount",
+ "total_amount_reimbursed",
+ "section_break_16",
+ "posting_date",
+ "vehicle_log",
+ "task",
+ "cb1",
+ "remark",
+ "title",
+ "email_id",
+ "accounting_details",
+ "company",
+ "mode_of_payment",
+ "clearance_date",
+ "column_break_24",
+ "payable_account",
+ "accounting_dimensions_section",
+ "project",
+ "dimension_col_break",
+ "cost_center",
+ "more_details",
+ "status",
+ "amended_from",
+ "advance_payments",
+ "advances"
+ ],
+ "fields": [
+ {
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "no_copy": 1,
+ "options": "HR-EXP-.YYYY.-",
+ "print_hide": 1,
+ "reqd": 1,
+ "set_only_once": 1
+ },
+ {
+ "fieldname": "employee",
+ "fieldtype": "Link",
+ "in_global_search": 1,
+ "label": "From Employee",
+ "oldfieldname": "employee",
+ "oldfieldtype": "Link",
+ "options": "Employee",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "fetch_from": "employee.employee_name",
+ "fieldname": "employee_name",
+ "fieldtype": "Data",
+ "in_global_search": 1,
+ "label": "Employee Name",
+ "oldfieldname": "employee_name",
+ "oldfieldtype": "Data",
+ "read_only": 1,
+ "width": "150px"
+ },
+ {
+ "fetch_from": "employee.department",
+ "fieldname": "department",
+ "fieldtype": "Link",
+ "label": "Department",
+ "options": "Department",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_5",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "expense_approver",
+ "fieldtype": "Link",
+ "label": "Expense Approver",
+ "options": "User"
+ },
+ {
+ "default": "Draft",
+ "fieldname": "approval_status",
+ "fieldtype": "Select",
+ "label": "Approval Status",
+ "no_copy": 1,
+ "options": "Draft\nApproved\nRejected",
+ "search_index": 1
+ },
+ {
+ "fieldname": "total_claimed_amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Total Claimed Amount",
+ "no_copy": 1,
+ "oldfieldname": "total_claimed_amount",
+ "oldfieldtype": "Currency",
+ "options": "Company:company:default_currency",
+ "read_only": 1,
+ "width": "160px"
+ },
+ {
+ "fieldname": "total_sanctioned_amount",
+ "fieldtype": "Currency",
+ "label": "Total Sanctioned Amount",
+ "no_copy": 1,
+ "oldfieldname": "total_sanctioned_amount",
+ "oldfieldtype": "Currency",
+ "options": "Company:company:default_currency",
+ "read_only": 1,
+ "width": "160px"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:(doc.docstatus==0 || doc.is_paid)",
+ "fieldname": "is_paid",
+ "fieldtype": "Check",
+ "label": "Is Paid"
+ },
+ {
+ "fieldname": "expense_details",
+ "fieldtype": "Section Break",
+ "oldfieldtype": "Section Break"
+ },
+ {
+ "fieldname": "expenses",
+ "fieldtype": "Table",
+ "label": "Expenses",
+ "oldfieldname": "expense_voucher_details",
+ "oldfieldtype": "Table",
+ "options": "Expense Claim Detail",
+ "reqd": 1
+ },
+ {
+ "fieldname": "sb1",
+ "fieldtype": "Section Break",
+ "options": "Simple"
+ },
+ {
+ "default": "Today",
+ "fieldname": "posting_date",
+ "fieldtype": "Date",
+ "label": "Posting Date",
+ "oldfieldname": "posting_date",
+ "oldfieldtype": "Date",
+ "reqd": 1
+ },
+ {
+ "fieldname": "vehicle_log",
+ "fieldtype": "Link",
+ "label": "Vehicle Log",
+ "options": "Vehicle Log",
+ "read_only": 1
+ },
+ {
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "label": "Project",
+ "options": "Project"
+ },
+ {
+ "fieldname": "task",
+ "fieldtype": "Link",
+ "label": "Task",
+ "options": "Task",
+ "remember_last_selected_value": 1
+ },
+ {
+ "fieldname": "cb1",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "total_amount_reimbursed",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Total Amount Reimbursed",
+ "no_copy": 1,
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "remark",
+ "fieldtype": "Small Text",
+ "label": "Remark",
+ "no_copy": 1,
+ "oldfieldname": "remark",
+ "oldfieldtype": "Small Text"
+ },
+ {
+ "allow_on_submit": 1,
+ "default": "{employee_name}",
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Title",
+ "no_copy": 1
+ },
+ {
+ "fieldname": "email_id",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Employees Email Id",
+ "oldfieldname": "email_id",
+ "oldfieldtype": "Data",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "accounting_details",
+ "fieldtype": "Section Break",
+ "label": "Accounting Details"
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "oldfieldname": "company",
+ "oldfieldtype": "Link",
+ "options": "Company",
+ "remember_last_selected_value": 1,
+ "reqd": 1
+ },
+ {
+ "depends_on": "is_paid",
+ "fieldname": "mode_of_payment",
+ "fieldtype": "Link",
+ "label": "Mode of Payment",
+ "options": "Mode of Payment"
+ },
+ {
+ "fieldname": "clearance_date",
+ "fieldtype": "Date",
+ "label": "Clearance Date"
+ },
+ {
+ "fieldname": "column_break_24",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "payable_account",
+ "fieldtype": "Link",
+ "label": "Payable Account",
+ "options": "Account",
+ "reqd": 1
+ },
+ {
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "more_details",
+ "fieldtype": "Section Break",
+ "label": "More Details"
+ },
+ {
+ "default": "Draft",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "no_copy": 1,
+ "options": "Draft\nPaid\nUnpaid\nRejected\nSubmitted\nCancelled",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "amended_from",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Amended From",
+ "no_copy": 1,
+ "oldfieldname": "amended_from",
+ "oldfieldtype": "Data",
+ "options": "Expense Claim",
+ "print_hide": 1,
+ "read_only": 1,
+ "report_hide": 1,
+ "width": "160px"
+ },
+ {
+ "fieldname": "advance_payments",
+ "fieldtype": "Section Break",
+ "label": "Advance Payments"
+ },
+ {
+ "fieldname": "advances",
+ "fieldtype": "Table",
+ "label": "Advances",
+ "options": "Expense Claim Advance"
+ },
+ {
+ "fieldname": "total_advance_amount",
+ "fieldtype": "Currency",
+ "label": "Total Advance Amount",
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "taxes",
+ "fieldtype": "Table",
+ "label": "Expense Taxes and Charges",
+ "options": "Expense Taxes and Charges"
+ },
+ {
+ "fieldname": "section_break_16",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "transactions_section",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "grand_total",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Grand Total",
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_17",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "total_taxes_and_charges",
+ "fieldtype": "Currency",
+ "label": "Total Taxes and Charges",
+ "options": "Company:company:default_currency",
+ "read_only": 1
+ }
+ ],
+ "icon": "fa fa-money",
+ "idx": 1,
+ "is_submittable": 1,
+ "modified": "2019-11-08 14:13:08.964547",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Expense Claim",
+ "name_case": "Title Case",
+ "owner": "harshada@webnotestech.com",
+ "permissions": [
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Employee",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Expense Approver",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "HR User",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ }
+ ],
+ "search_fields": "employee,employee_name",
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "timeline_field": "employee",
+ "title_field": "title"
+}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index caeb2dd946..f0036277c8 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -144,6 +144,33 @@ class ExpenseClaim(AccountsController):
"against_voucher": self.name
})
)
+
+ gl_entry.append(
+ self.get_gl_dict({
+ "account": data.advance_account,
+ "debit": data.allocated_amount,
+ "debit_in_account_currency": data.allocated_amount,
+ "against": self.payable_account,
+ "party_type": "Employee",
+ "party": self.employee,
+ "against_voucher_type": self.doctype,
+ "against_voucher": self.name
+ })
+ )
+
+ gl_entry.append(
+ self.get_gl_dict({
+ "account": self.payable_account,
+ "credit": data.allocated_amount,
+ "credit_in_account_currency": data.allocated_amount,
+ "against": data.advance_account,
+ "party_type": "Employee",
+ "party": self.employee,
+ "against_voucher_type": "Employee Advance",
+ "against_voucher": data.employee_advance
+ })
+ )
+
self.add_tax_gl_entries(gl_entry)
if self.is_paid and self.grand_total:
@@ -192,9 +219,6 @@ class ExpenseClaim(AccountsController):
if not self.cost_center:
frappe.throw(_("Cost center is required to book an expense claim"))
- if not self.payable_account:
- frappe.throw(_("Please set default payable account for the company {0}").format(getlink("Company",self.company)))
-
if self.is_paid:
if not self.mode_of_payment:
frappe.throw(_("Mode of payment is required to make a payment").format(self.employee))
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index e1e5e8001d..0e6630541c 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -55,11 +55,11 @@ class LeaveApplication(Document):
self.reload()
def on_cancel(self):
+ self.create_leave_ledger_entry(submit=False)
self.status = "Cancelled"
# notify leave applier about cancellation
self.notify_employee()
self.cancel_attendance()
- self.create_leave_ledger_entry(submit=False)
def validate_applicable_after(self):
if self.leave_type:
@@ -351,6 +351,9 @@ class LeaveApplication(Document):
pass
def create_leave_ledger_entry(self, submit=True):
+ if self.status != 'Approved':
+ return
+
expiry_date = get_allocation_expiry(self.employee, self.leave_type,
self.to_date, self.from_date)
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js
index d56320a073..dd34ef2ae2 100755
--- a/erpnext/hr/doctype/salary_structure/salary_structure.js
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.js
@@ -46,10 +46,12 @@ frappe.ui.form.on('Salary Structure', {
frm.trigger("toggle_fields");
frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false);
frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false);
-
- frm.add_custom_button(__("Preview Salary Slip"), function() {
- frm.trigger('preview_salary_slip');
- });
+
+ if(frm.doc.docstatus === 1) {
+ frm.add_custom_button(__("Preview Salary Slip"), function() {
+ frm.trigger('preview_salary_slip');
+ });
+ }
if(frm.doc.docstatus==1) {
frm.add_custom_button(__("Assign Salary Structure"), function() {
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py
index f7d712d3f1..0e1a74f370 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.py
@@ -169,5 +169,10 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
@frappe.whitelist()
def get_employees(salary_structure):
employees = frappe.get_list('Salary Structure Assignment',
- filters={'salary_structure': salary_structure}, fields=['employee'])
+ filters={'salary_structure': salary_structure, 'docstatus': 1}, fields=['employee'])
+
+ if not employees:
+ frappe.throw(_("There's no Employee with Salary Structure: {0}. \
+ Assign {1} to an Employee to preview Salary Slip").format(salary_structure, salary_structure))
+
return list(set([d.employee for d in employees]))
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index e6afbcc220..595bcaa8d4 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -7,6 +7,7 @@ import frappe
from frappe.model.document import Document
from frappe import _
from frappe.utils import getdate, nowdate, cint, flt
+from frappe.utils.nestedset import get_descendants_of
class SubsidiaryCompanyError(frappe.ValidationError): pass
class ParentCompanyError(frappe.ValidationError): pass
@@ -131,7 +132,8 @@ def get_designation_counts(designation, company):
return False
employee_counts = {}
- company_set = get_company_set(company)
+ company_set = get_descendants_of('Company', company)
+ company_set.append(company)
employee_counts["employee_count"] = frappe.db.get_value("Employee",
filters={
@@ -167,14 +169,4 @@ def get_active_staffing_plan_details(company, designation, from_date=getdate(now
designation, from_date, to_date)
# Only a single staffing plan can be active for a designation on given date
- return staffing_plan if staffing_plan else None
-
-def get_company_set(company):
- return frappe.db.sql_list("""
- SELECT
- name
- FROM `tabCompany`
- WHERE
- parent_company=%(company)s
- OR name=%(company)s
- """, (dict(company=company)))
\ No newline at end of file
+ return staffing_plan if staffing_plan else None
\ No newline at end of file
diff --git a/erpnext/hr/report/department_analytics/department_analytics.js b/erpnext/hr/report/department_analytics/department_analytics.js
index a0b6fc7641..29fedcd735 100644
--- a/erpnext/hr/report/department_analytics/department_analytics.js
+++ b/erpnext/hr/report/department_analytics/department_analytics.js
@@ -2,4 +2,14 @@
// For license information, please see license.txt
frappe.query_reports["Department Analytics"] = {
+ "filters": [
+ {
+ "fieldname":"company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "default": frappe.defaults.get_user_default("Company"),
+ "reqd": 1
+ },
+ ]
};
\ No newline at end of file
diff --git a/erpnext/hr/report/department_analytics/department_analytics.py b/erpnext/hr/report/department_analytics/department_analytics.py
index c4a9030c59..b28eac43f8 100644
--- a/erpnext/hr/report/department_analytics/department_analytics.py
+++ b/erpnext/hr/report/department_analytics/department_analytics.py
@@ -7,6 +7,10 @@ from frappe import _
def execute(filters=None):
if not filters: filters = {}
+
+ if not filters["company"]:
+ frappe.throw(_('{0} is mandatory').format(_('Company')))
+
columns = get_columns()
employees = get_employees(filters)
departments_result = get_department(filters)
@@ -28,6 +32,9 @@ def get_conditions(filters):
conditions = ""
if filters.get("department"): conditions += " and department = '%s'" % \
filters["department"].replace("'", "\\'")
+
+ if filters.get("company"): conditions += " and company = '%s'" % \
+ filters["company"].replace("'", "\\'")
return conditions
def get_employees(filters):
@@ -37,7 +44,7 @@ def get_employees(filters):
gender, company from `tabEmployee` where status = 'Active' %s""" % conditions, as_list=1)
def get_department(filters):
- return frappe.db.sql("""select name from `tabDepartment`""" , as_list=1)
+ return frappe.db.sql("""select name from `tabDepartment` where company = %s""", (filters["company"]), as_list=1)
def get_chart_data(departments,employees):
if not departments:
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index c849f5b7f2..db79d7feda 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -420,8 +420,12 @@ class BOM(WebsiteGenerator):
def traverse_tree(self, bom_list=None):
def _get_children(bom_no):
- return frappe.db.sql_list("""select bom_no from `tabBOM Item`
- where parent = %s and ifnull(bom_no, '') != '' and parenttype='BOM'""", bom_no)
+ children = frappe.cache().hget('bom_children', bom_no)
+ if children is None:
+ children = frappe.db.sql_list("""SELECT `bom_no` FROM `tabBOM Item`
+ WHERE `parent`=%s AND `bom_no`!='' AND `parenttype`='BOM'""", bom_no)
+ frappe.cache().hset('bom_children', bom_no, children)
+ return children
count = 0
if not bom_list:
@@ -534,12 +538,24 @@ class BOM(WebsiteGenerator):
def get_child_exploded_items(self, bom_no, stock_qty):
""" Add all items from Flat BOM of child BOM"""
# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss
- child_fb_items = frappe.db.sql("""select bom_item.item_code, bom_item.item_name,
- bom_item.description, bom_item.source_warehouse, bom_item.operation,
- bom_item.stock_uom, bom_item.stock_qty, bom_item.rate, bom_item.include_item_in_manufacturing,
- bom_item.stock_qty / ifnull(bom.quantity, 1) as qty_consumed_per_unit
- from `tabBOM Explosion Item` bom_item, tabBOM bom
- where bom_item.parent = bom.name and bom.name = %s and bom.docstatus = 1""", bom_no, as_dict = 1)
+ child_fb_items = frappe.db.sql("""
+ SELECT
+ bom_item.item_code,
+ bom_item.item_name,
+ bom_item.description,
+ bom_item.source_warehouse,
+ bom_item.operation,
+ bom_item.stock_uom,
+ bom_item.stock_qty,
+ bom_item.rate,
+ bom_item.include_item_in_manufacturing,
+ bom_item.stock_qty / ifnull(bom.quantity, 1) AS qty_consumed_per_unit
+ FROM `tabBOM Explosion Item` bom_item, tabBOM bom
+ WHERE
+ bom_item.parent = bom.name
+ AND bom.name = %s
+ AND bom.docstatus = 1
+ """, bom_no, as_dict = 1)
for d in child_fb_items:
self.add_to_cur_exploded_items(frappe._dict({
@@ -760,6 +776,8 @@ def add_additional_cost(stock_entry, work_order):
# Add non stock items cost in the additional cost
bom = frappe.get_doc('BOM', work_order.bom_no)
table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items'
+ expenses_included_in_valuation = frappe.get_cached_value("Company", work_order.company,
+ "expenses_included_in_valuation")
items = {}
for d in bom.get(table):
@@ -770,6 +788,7 @@ def add_additional_cost(stock_entry, work_order):
for name in non_stock_items:
stock_entry.append('additional_costs', {
+ 'expense_account': expenses_included_in_valuation,
'description': name[0],
'amount': items.get(name[0])
})
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 87b8f67e53..2ca4d16a07 100644
--- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
+++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
@@ -14,23 +14,23 @@ class BOMUpdateTool(Document):
def replace_bom(self):
self.validate_bom()
self.update_new_bom()
+ frappe.cache().delete_key('bom_children')
bom_list = self.get_parent_boms(self.new_bom)
updated_bom = []
for bom in bom_list:
try:
- bom_obj = frappe.get_doc("BOM", bom)
- bom_obj.load_doc_before_save()
- updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
+ bom_obj = frappe.get_cached_doc('BOM', bom)
+ # this is only used for versioning and we do not want
+ # to make separate db calls by using load_doc_before_save
+ # which proves to be expensive while doing bulk replace
+ bom_obj._doc_before_save = bom_obj.as_dict()
bom_obj.calculate_cost()
bom_obj.update_parent_cost()
bom_obj.db_update()
- if (getattr(bom_obj.meta, 'track_changes', False) and not bom_obj.flags.ignore_version):
+ if bom_obj.meta.get('track_changes') and not bom_obj.flags.ignore_version:
bom_obj.save_version()
-
- frappe.db.commit()
except Exception:
- frappe.db.rollback()
frappe.log_error(frappe.get_traceback())
def validate_bom(self):
@@ -42,22 +42,22 @@ class BOMUpdateTool(Document):
frappe.throw(_("The selected BOMs are not for the same item"))
def update_new_bom(self):
- new_bom_unitcost = frappe.db.sql("""select total_cost/quantity
- from `tabBOM` where name = %s""", self.new_bom)
+ new_bom_unitcost = frappe.db.sql("""SELECT `total_cost`/`quantity`
+ FROM `tabBOM` WHERE name = %s""", self.new_bom)
new_bom_unitcost = flt(new_bom_unitcost[0][0]) if new_bom_unitcost else 0
frappe.db.sql("""update `tabBOM Item` set bom_no=%s,
rate=%s, amount=stock_qty*%s where bom_no = %s and docstatus < 2 and parenttype='BOM'""",
(self.new_bom, new_bom_unitcost, new_bom_unitcost, self.current_bom))
- def get_parent_boms(self, bom, bom_list=None):
- if not bom_list:
- bom_list = []
-
- data = frappe.db.sql(""" select distinct parent from `tabBOM Item`
- where bom_no = %s and docstatus < 2 and parenttype='BOM'""", bom)
+ def get_parent_boms(self, bom, bom_list=[]):
+ data = frappe.db.sql("""SELECT DISTINCT parent FROM `tabBOM Item`
+ WHERE bom_no = %s AND docstatus < 2 AND parenttype='BOM'""", bom)
for d in data:
+ if self.new_bom == d[0]:
+ frappe.throw(_("BOM recursion: {0} cannot be child of {1}").format(bom, self.new_bom))
+
bom_list.append(d[0])
self.get_parent_boms(d[0], bom_list)
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index 39d59f006b..f27197d09f 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -1,443 +1,135 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2017-12-01 12:12:55.048691",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "creation": "2017-12-01 12:12:55.048691",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "item_code",
+ "item_name",
+ "warehouse",
+ "material_request_type",
+ "column_break_4",
+ "quantity",
+ "uom",
+ "projected_qty",
+ "actual_qty",
+ "item_details",
+ "description",
+ "min_order_qty",
+ "section_break_8",
+ "sales_order",
+ "requested_qty"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "item_code",
- "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": "Item Code",
- "length": 0,
- "no_copy": 0,
- "options": "Item",
- "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
- },
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Item Code",
+ "options": "Item",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "item_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Item 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": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "item_name",
+ "fieldtype": "Data",
+ "label": "Item Name"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "warehouse",
- "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": 1,
- "label": "Warehouse",
- "length": 0,
- "no_copy": 0,
- "options": "Warehouse",
- "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
- },
+ "fieldname": "warehouse",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Warehouse",
+ "options": "Warehouse",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "material_request_type",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Material Request Type",
- "length": 0,
- "no_copy": 0,
- "options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "material_request_type",
+ "fieldtype": "Select",
+ "label": "Material Request Type",
+ "options": "\nPurchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "column_break_4",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "quantity",
- "fieldtype": "Float",
- "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": "Required Quantity",
- "length": 0,
- "no_copy": 1,
- "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
- },
+ "fieldname": "quantity",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Required Quantity",
+ "no_copy": 1,
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "projected_qty",
- "fieldtype": "Float",
- "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": "Projected Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "projected_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Projected Qty",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "collapsible_depends_on": "",
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
- "fieldname": "actual_qty",
- "fieldtype": "Float",
- "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": "Actual Qty",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "actual_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Actual Qty",
+ "no_copy": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "min_order_qty",
- "fieldtype": "Float",
- "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": "Minimum Order Quantity",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "min_order_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Minimum Order Quantity",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 1,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "section_break_8",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Reference",
- "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
- },
+ "collapsible": 1,
+ "fieldname": "section_break_8",
+ "fieldtype": "Section Break",
+ "label": "Reference"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "sales_order",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Sales Order",
- "length": 0,
- "no_copy": 0,
- "options": "Sales Order",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "sales_order",
+ "fieldtype": "Link",
+ "label": "Sales Order",
+ "options": "Sales Order",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "",
- "fetch_if_empty": 0,
- "fieldname": "requested_qty",
- "fieldtype": "Float",
- "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": "Requested Qty",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "requested_qty",
+ "fieldtype": "Float",
+ "label": "Requested Qty",
+ "read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "item_details",
+ "fieldtype": "Section Break",
+ "label": "Item Description"
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Text Editor",
+ "label": "Description"
+ },
+ {
+ "fieldname": "uom",
+ "fieldtype": "Link",
+ "label": "UOM",
+ "options": "UOM",
+ "read_only": 1
}
- ],
- "has_web_view": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 1,
- "max_attachments": 0,
- "modified": "2019-04-08 18:15:26.849602",
- "modified_by": "Administrator",
- "module": "Manufacturing",
- "name": "Material Request Plan Item",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 1,
- "read_only": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "istable": 1,
+ "modified": "2019-11-08 15:15:43.979360",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Material Request Plan Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 4b654b47e6..3b24d0fa0f 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -3,6 +3,11 @@
frappe.ui.form.on('Production Plan', {
setup: function(frm) {
+ frm.custom_make_buttons = {
+ 'Work Order': 'Work Order',
+ 'Material Request': 'Material Request',
+ };
+
frm.fields_dict['po_items'].grid.get_field('warehouse').get_query = function(doc) {
return {
filters: {
@@ -182,8 +187,8 @@ frappe.ui.form.on('Production Plan', {
},
get_items_for_mr: function(frm) {
- const set_fields = ['actual_qty', 'item_code',
- 'item_name', 'min_order_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'material_request_type'];
+ const set_fields = ['actual_qty', 'item_code','item_name', 'description', 'uom',
+ 'min_order_qty', 'quantity', 'sales_order', 'warehouse', 'projected_qty', 'material_request_type'];
frappe.call({
method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_items_for_material_requests",
freeze: true,
@@ -233,7 +238,7 @@ frappe.ui.form.on('Production Plan', {
if (item_wise_qty) {
for (var key in item_wise_qty) {
- title += __('Item {0}: {1} qty produced, ', [key, item_wise_qty[key]]);
+ title += __('Item {0}: {1} qty produced. ', [key, item_wise_qty[key]]);
}
}
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 10d9a474e0..5d2696933b 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -99,7 +99,7 @@ class ProductionPlan(Document):
self.get_mr_items()
def get_so_items(self):
- so_list = [d.sales_order for d in self.get("sales_orders", []) if d.sales_order]
+ so_list = [d.sales_order for d in self.sales_orders if d.sales_order]
if not so_list:
msgprint(_("Please enter Sales Orders in the above table"))
return []
@@ -109,7 +109,7 @@ class ProductionPlan(Document):
item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code))
items = frappe.db.sql("""select distinct parent, item_code, warehouse,
- (qty - work_order_qty) * conversion_factor as pending_qty, name
+ (qty - work_order_qty) * conversion_factor as pending_qty, description, name
from `tabSales Order Item` so_item
where parent in (%s) and docstatus = 1 and qty > work_order_qty
and exists (select name from `tabBOM` bom where bom.item=so_item.item_code
@@ -121,7 +121,7 @@ class ProductionPlan(Document):
packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse,
(((so_item.qty - so_item.work_order_qty) * pi.qty) / so_item.qty)
- as pending_qty, pi.parent_item, so_item.name
+ as pending_qty, pi.parent_item, pi.description, so_item.name
from `tabSales Order Item` so_item, `tabPacked Item` pi
where so_item.parent = pi.parent and so_item.docstatus = 1
and pi.parent_item = so_item.item_code
@@ -134,7 +134,7 @@ class ProductionPlan(Document):
self.calculate_total_planned_qty()
def get_mr_items(self):
- mr_list = [d.material_request for d in self.get("material_requests", []) if d.material_request]
+ mr_list = [d.material_request for d in self.material_requests if d.material_request]
if not mr_list:
msgprint(_("Please enter Material Requests in the above table"))
return []
@@ -143,7 +143,7 @@ class ProductionPlan(Document):
if self.item_code:
item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code))
- items = frappe.db.sql("""select distinct parent, name, item_code, warehouse,
+ items = frappe.db.sql("""select distinct parent, name, item_code, warehouse, description,
(qty - ordered_qty) as pending_qty
from `tabMaterial Request Item` mr_item
where parent in (%s) and docstatus = 1 and qty > ordered_qty
@@ -162,7 +162,7 @@ class ProductionPlan(Document):
'include_exploded_items': 1,
'warehouse': data.warehouse,
'item_code': data.item_code,
- 'description': item_details and item_details.description or '',
+ 'description': data.description or item_details.description,
'stock_uom': item_details and item_details.stock_uom or '',
'bom_no': item_details and item_details.bom_no or '',
'planned_qty': data.pending_qty,
@@ -174,10 +174,12 @@ class ProductionPlan(Document):
if self.get_items_from == "Sales Order":
pi.sales_order = data.parent
pi.sales_order_item = data.name
+ pi.description = data.description
elif self.get_items_from == "Material Request":
pi.material_request = data.parent
pi.material_request_item = data.name
+ pi.description = data.description
def calculate_total_planned_qty(self):
self.total_planned_qty = 0
@@ -195,7 +197,6 @@ class ProductionPlan(Document):
for data in self.po_items:
if data.name == production_plan_item:
data.produced_qty = produced_qty
- data.pending_qty = data.planned_qty - data.produced_qty
data.db_update()
self.calculate_total_produced_qty()
@@ -302,6 +303,7 @@ class ProductionPlan(Document):
wo_list.extend(work_orders)
frappe.flags.mute_messages = False
+
if wo_list:
wo_list = ["""%s""" % \
(p, p) for p in wo_list]
@@ -309,16 +311,15 @@ class ProductionPlan(Document):
else :
msgprint(_("No Work Orders created"))
-
def make_work_order_for_sub_assembly_items(self, item):
work_orders = []
bom_data = {}
- get_sub_assembly_items(item.get("bom_no"), bom_data, item.get("qty"))
+ get_sub_assembly_items(item.get("bom_no"), bom_data)
for key, data in bom_data.items():
data.update({
- 'qty': data.get("stock_qty"),
+ 'qty': data.get("stock_qty") * item.get("qty"),
'production_plan': self.name,
'company': self.company,
'fg_warehouse': item.get("fg_warehouse"),
@@ -528,6 +529,7 @@ def get_material_request_items(row, sales_order,
required_qty = ceil(required_qty)
if required_qty > 0:
+ print(row)
return {
'item_code': row.item_code,
'item_name': row.item_name,
@@ -540,7 +542,9 @@ def get_material_request_items(row, sales_order,
'projected_qty': bin_dict.get("projected_qty", 0),
'min_order_qty': row['min_order_qty'],
'material_request_type': row.get("default_material_request_type"),
- 'sales_order': sales_order
+ 'sales_order': sales_order,
+ 'description': row.get("description"),
+ 'uom': row.get("purchase_uom") or row.get("stock_uom")
}
def get_sales_orders(self):
@@ -558,7 +562,7 @@ def get_sales_orders(self):
item_filter += " and so_item.item_code = %(item)s"
open_so = frappe.db.sql("""
- select distinct so.name, so.transaction_date, so.customer, so.base_grand_total as grand_total
+ select distinct so.name, so.transaction_date, so.customer, so.base_grand_total
from `tabSales Order` so, `tabSales Order Item` so_item
where so_item.parent = so.name
and so.docstatus = 1 and so.status not in ("Stopped", "Closed")
@@ -622,7 +626,7 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None):
for data in po_items:
planned_qty = data.get('required_qty') or data.get('planned_qty')
ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty
- warehouse = warehouse or data.get("warehouse")
+ warehouse = data.get("warehouse") or warehouse
item_details = {}
if data.get("bom") or data.get("bom_no"):
@@ -705,11 +709,11 @@ def get_item_data(item_code):
return {
"bom_no": item_details.get("bom_no"),
- "stock_uom": item_details.get("stock_uom"),
- "description": item_details.get("description")
+ "stock_uom": item_details.get("stock_uom")
+# "description": item_details.get("description")
}
-def get_sub_assembly_items(bom_no, bom_data, qty):
+def get_sub_assembly_items(bom_no, bom_data):
data = get_children('BOM', parent = bom_no)
for d in data:
if d.expandable:
@@ -726,6 +730,6 @@ def get_sub_assembly_items(bom_no, bom_data, qty):
})
bom_item = bom_data.get(key)
- bom_item["stock_qty"] += ((d.stock_qty * qty) / d.parent_bom_qty)
+ bom_item["stock_qty"] += d.stock_qty
- get_sub_assembly_items(bom_item.get("bom_no"), bom_data, bom_item["stock_qty"])
+ get_sub_assembly_items(bom_item.get("bom_no"), bom_data)
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 44796417d4..f70c9cc43f 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -11,11 +11,9 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import get_sa
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests
-from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
class TestProductionPlan(unittest.TestCase):
def setUp(self):
- set_perpetual_inventory(0)
for item in ['Test Production Item 1', 'Subassembly Item 1',
'Raw Material Item 1', 'Raw Material Item 2']:
create_item(item, valuation_rate=100)
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index cdbce33e1f..107c79b89b 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -395,6 +395,11 @@ frappe.ui.form.on("Work Order", {
}
});
}
+ },
+
+ additional_operating_cost: function(frm) {
+ erpnext.work_order.calculate_cost(frm.doc);
+ erpnext.work_order.calculate_total_cost(frm);
}
});
@@ -534,8 +539,7 @@ erpnext.work_order = {
},
calculate_total_cost: function(frm) {
- var variable_cost = frm.doc.actual_operating_cost ?
- flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost);
+ let variable_cost = flt(frm.doc.actual_operating_cost) || flt(frm.doc.planned_operating_cost);
frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost));
},
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index b57548e960..6ea3dc83ed 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -223,7 +223,15 @@ class WorkOrder(Document):
def update_production_plan_status(self):
production_plan = frappe.get_doc('Production Plan', self.production_plan)
- production_plan.run_method("update_produced_qty", self.produced_qty, self.production_plan_item)
+ produced_qty = 0
+ if self.production_plan_item:
+ total_qty = frappe.get_all("Work Order", fields = "sum(produced_qty) as produced_qty",
+ filters = {'docstatus': 1, 'production_plan': self.production_plan,
+ 'production_plan_item': self.production_plan_item}, as_list=1)
+
+ produced_qty = total_qty[0][0] if total_qty else 0
+
+ production_plan.run_method("update_produced_qty", produced_qty, self.production_plan_item)
def on_submit(self):
if not self.wip_warehouse:
@@ -645,7 +653,8 @@ def make_stock_entry(work_order_id, purpose, qty=None):
stock_entry.to_warehouse = work_order.fg_warehouse
stock_entry.project = work_order.project
if purpose=="Manufacture":
- additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty)
+ additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty,
+ company=work_order.company)
stock_entry.set("additional_costs", additional_costs)
stock_entry.set_stock_entry_type()
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index ee6bdff661..9e4dc12e65 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -638,6 +638,11 @@ erpnext.patches.v12_0.add_variant_of_in_item_attribute_table
erpnext.patches.v12_0.rename_bank_account_field_in_journal_entry_account
erpnext.patches.v12_0.create_default_energy_point_rules
erpnext.patches.v12_0.set_produced_qty_field_in_sales_order_for_work_order
-erpnext.patches.v12_0.generate_leave_ledger_entries
erpnext.patches.v12_0.set_default_shopify_app_type
+erpnext.patches.v12_0.set_cwip_and_delete_asset_settings
+erpnext.patches.v12_0.set_expense_account_in_landed_cost_voucher_taxes
erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings
+erpnext.patches.v12_0.set_payment_entry_status
+erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields
+erpnext.patches.v12_0.set_default_for_add_taxes_from_item_tax_template
+erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger
\ No newline at end of file
diff --git a/erpnext/patches/v11_0/update_delivery_trip_status.py b/erpnext/patches/v11_0/update_delivery_trip_status.py
index 64b3063bac..42f017e04d 100755
--- a/erpnext/patches/v11_0/update_delivery_trip_status.py
+++ b/erpnext/patches/v11_0/update_delivery_trip_status.py
@@ -1,27 +1,28 @@
-# Copyright (c) 2017, Frappe and Contributors
-# License: GNU General Public License v3. See license.txt
-
-from __future__ import unicode_literals
-import frappe
-
-def execute():
- frappe.reload_doc('stock', 'doctype', 'delivery_trip')
- frappe.reload_doc('stock', 'doctype', 'delivery_stop', force=True)
-
- for trip in frappe.get_all("Delivery Trip"):
- trip_doc = frappe.get_doc("Delivery Trip", trip.name)
-
- status = {
- 0: "Draft",
- 1: "Scheduled",
- 2: "Cancelled"
- }[trip_doc.docstatus]
-
- if trip_doc.docstatus == 1:
- visited_stops = [stop.visited for stop in trip_doc.delivery_stops]
- if all(visited_stops):
- status = "Completed"
- elif any(visited_stops):
- status = "In Transit"
-
- frappe.db.set_value("Delivery Trip", trip.name, "status", status, update_modified=False)
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc('setup', 'doctype', 'global_defaults', force=True)
+ frappe.reload_doc('stock', 'doctype', 'delivery_trip')
+ frappe.reload_doc('stock', 'doctype', 'delivery_stop', force=True)
+
+ for trip in frappe.get_all("Delivery Trip"):
+ trip_doc = frappe.get_doc("Delivery Trip", trip.name)
+
+ status = {
+ 0: "Draft",
+ 1: "Scheduled",
+ 2: "Cancelled"
+ }[trip_doc.docstatus]
+
+ if trip_doc.docstatus == 1:
+ visited_stops = [stop.visited for stop in trip_doc.delivery_stops]
+ if all(visited_stops):
+ status = "Completed"
+ elif any(visited_stops):
+ status = "In Transit"
+
+ frappe.db.set_value("Delivery Trip", trip.name, "status", status, update_modified=False)
diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
index 412f32030a..f25b9eaf52 100644
--- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
+++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py
@@ -1,20 +1,30 @@
import frappe
import json
from six import iteritems
+from frappe.model.naming import make_autoname
def execute():
if "tax_type" not in frappe.db.get_table_columns("Item Tax"):
return
old_item_taxes = {}
item_tax_templates = {}
- rename_template_to_untitled = []
+
+ frappe.reload_doc("accounts", "doctype", "item_tax_template_detail", force=1)
+ frappe.reload_doc("accounts", "doctype", "item_tax_template", force=1)
+ existing_templates = frappe.db.sql("""select template.name, details.tax_type, details.tax_rate
+ from `tabItem Tax Template` template, `tabItem Tax Template Detail` details
+ where details.parent=template.name
+ """, as_dict=1)
+
+ if len(existing_templates):
+ for d in existing_templates:
+ item_tax_templates.setdefault(d.name, {})
+ item_tax_templates[d.name][d.tax_type] = d.tax_rate
for d in frappe.db.sql("""select parent as item_code, tax_type, tax_rate from `tabItem Tax`""", as_dict=1):
old_item_taxes.setdefault(d.item_code, [])
old_item_taxes[d.item_code].append(d)
- frappe.reload_doc("accounts", "doctype", "item_tax_template_detail", force=1)
- frappe.reload_doc("accounts", "doctype", "item_tax_template", force=1)
frappe.reload_doc("stock", "doctype", "item", force=1)
frappe.reload_doc("stock", "doctype", "item_tax", force=1)
frappe.reload_doc("selling", "doctype", "quotation_item", force=1)
@@ -27,6 +37,8 @@ def execute():
frappe.reload_doc("accounts", "doctype", "purchase_invoice_item", force=1)
frappe.reload_doc("accounts", "doctype", "accounts_settings", force=1)
+ frappe.db.auto_commit_on_many_writes = True
+
# for each item that have item tax rates
for item_code in old_item_taxes.keys():
# make current item's tax map
@@ -34,8 +46,7 @@ def execute():
for d in old_item_taxes[item_code]:
item_tax_map[d.tax_type] = d.tax_rate
- item_tax_template_name = get_item_tax_template(item_tax_templates, rename_template_to_untitled,
- item_tax_map, item_code)
+ item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code)
# update the item tax table
item = frappe.get_doc("Item", item_code)
@@ -49,35 +60,33 @@ def execute():
'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice',
'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'
]
+
for dt in doctypes:
for d in frappe.db.sql("""select name, parent, item_code, item_tax_rate from `tab{0} Item`
- where ifnull(item_tax_rate, '') not in ('', '{{}}')""".format(dt), as_dict=1):
+ where ifnull(item_tax_rate, '') not in ('', '{{}}')
+ and item_tax_template is NULL""".format(dt), as_dict=1):
item_tax_map = json.loads(d.item_tax_rate)
- item_tax_template = get_item_tax_template(item_tax_templates, rename_template_to_untitled,
+ item_tax_template_name = get_item_tax_template(item_tax_templates,
item_tax_map, d.item_code, d.parent)
- frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template)
+ frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name)
- idx = 1
- for oldname in rename_template_to_untitled:
- frappe.rename_doc("Item Tax Template", oldname, "Untitled {}".format(idx))
- idx += 1
+ frappe.db.auto_commit_on_many_writes = False
settings = frappe.get_single("Accounts Settings")
settings.add_taxes_from_item_tax_template = 0
settings.determine_address_tax_category_from = "Billing Address"
settings.save()
-def get_item_tax_template(item_tax_templates, rename_template_to_untitled, item_tax_map, item_code, parent=None):
+def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parent=None):
# search for previously created item tax template by comparing tax maps
for template, item_tax_template_map in iteritems(item_tax_templates):
if item_tax_map == item_tax_template_map:
- if not parent:
- rename_template_to_untitled.append(template)
return template
# if no item tax template found, create one
item_tax_template = frappe.new_doc("Item Tax Template")
- item_tax_template.title = "{}--{}".format(parent, item_code) if parent else "Item-{}".format(item_code)
+ item_tax_template.title = make_autoname("Item Tax Template-.####")
+
for tax_type, tax_rate in iteritems(item_tax_map):
if not frappe.db.exists("Account", tax_type):
parts = tax_type.strip().split(" - ")
diff --git a/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py
new file mode 100644
index 0000000000..7859606e5c
--- /dev/null
+++ b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py
@@ -0,0 +1,28 @@
+# Copyright (c) 2018, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import getdate, today
+
+def execute():
+ ''' Delete leave ledger entry created
+ via leave applications with status != Approved '''
+ if not frappe.db.a_row_exists("Leave Ledger Entry"):
+ return
+
+ leave_application_list = get_denied_leave_application_list()
+ if leave_application_list:
+ delete_denied_leaves_from_leave_ledger_entry(leave_application_list)
+
+def get_denied_leave_application_list():
+ return frappe.db.sql_list(''' Select name from `tabLeave Application` where status <> 'Approved' ''')
+
+def delete_denied_leaves_from_leave_ledger_entry(leave_application_list):
+ if leave_application_list:
+ frappe.db.sql(''' Delete
+ FROM `tabLeave Ledger Entry`
+ WHERE
+ transaction_type = 'Leave Application'
+ AND transaction_name in (%s) ''' % (', '.join(['%s'] * len(leave_application_list))), #nosec
+ tuple(leave_application_list))
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py
new file mode 100644
index 0000000000..5842e9edbf
--- /dev/null
+++ b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py
@@ -0,0 +1,21 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import cint
+
+
+def execute():
+ '''Get 'Disable CWIP Accounting value' from Asset Settings, set it in 'Enable Capital Work in Progress Accounting' field
+ in Company, delete Asset Settings '''
+
+ if frappe.db.exists("DocType","Asset Settings"):
+ frappe.reload_doctype("Company")
+ cwip_value = frappe.db.get_single_value("Asset Settings","disable_cwip_accounting")
+
+ companies = [x['name'] for x in frappe.get_all("Company", "name")]
+ for company in companies:
+ enable_cwip_accounting = cint(not cint(cwip_value))
+ frappe.db.set_value("Company", company, "enable_cwip_accounting", enable_cwip_accounting)
+
+ frappe.db.sql(
+ """ DELETE FROM `tabSingles` where doctype = 'Asset Settings' """)
+ frappe.delete_doc_if_exists("DocType","Asset Settings")
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py b/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py
new file mode 100644
index 0000000000..06ee798198
--- /dev/null
+++ b/erpnext/patches/v12_0/set_default_for_add_taxes_from_item_tax_template.py
@@ -0,0 +1,5 @@
+import frappe
+
+def execute():
+ frappe.db.set_value("Accounts Settings", None, "add_taxes_from_item_tax_template", 1)
+ frappe.db.set_default("add_taxes_from_item_tax_template", 1)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py
new file mode 100644
index 0000000000..a996a69b3d
--- /dev/null
+++ b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py
@@ -0,0 +1,33 @@
+from __future__ import unicode_literals
+import frappe
+from six import iteritems
+
+def execute():
+ frappe.reload_doctype('Landed Cost Taxes and Charges')
+
+ company_account_map = frappe._dict(frappe.db.sql("""
+ SELECT name, expenses_included_in_valuation from `tabCompany`
+ """))
+
+ for company, account in iteritems(company_account_map):
+ frappe.db.sql("""
+ UPDATE
+ `tabLanded Cost Taxes and Charges` t, `tabLanded Cost Voucher` l
+ SET
+ t.expense_account = %s
+ WHERE
+ l.docstatus = 1
+ AND l.company = %s
+ AND t.parent = l.name
+ """, (account, company))
+
+ frappe.db.sql("""
+ UPDATE
+ `tabLanded Cost Taxes and Charges` t, `tabStock Entry` s
+ SET
+ t.expense_account = %s
+ WHERE
+ s.docstatus = 1
+ AND s.company = %s
+ AND t.parent = s.name
+ """, (account, company))
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_payment_entry_status.py b/erpnext/patches/v12_0/set_payment_entry_status.py
new file mode 100644
index 0000000000..fafbec6a9a
--- /dev/null
+++ b/erpnext/patches/v12_0/set_payment_entry_status.py
@@ -0,0 +1,9 @@
+import frappe
+
+def execute():
+ frappe.reload_doctype("Payment Entry")
+ frappe.db.sql("""update `tabPayment Entry` set status = CASE
+ WHEN docstatus = 1 THEN 'Submitted'
+ WHEN docstatus = 2 THEN 'Cancelled'
+ ELSE 'Draft'
+ END;""")
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py b/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py
new file mode 100644
index 0000000000..e4dcecd9bd
--- /dev/null
+++ b/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py
@@ -0,0 +1,17 @@
+from __future__ import unicode_literals
+import frappe
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_doctypes_with_dimensions
+
+def execute():
+ accounting_dimensions = frappe.db.sql("""select fieldname from
+ `tabAccounting Dimension`""", as_dict=1)
+
+ doclist = get_doctypes_with_dimensions()
+
+ for dimension in accounting_dimensions:
+ frappe.db.sql("""
+ UPDATE `tabCustom Field`
+ SET owner = 'Administrator'
+ WHERE fieldname = %s
+ AND dt IN (%s)""" % #nosec
+ ('%s', ', '.join(['%s']* len(doclist))), tuple([dimension.fieldname] + doclist))
\ No newline at end of file
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index 192e1bc2a5..25c97d1fb8 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -19,7 +19,7 @@ frappe.ui.form.on("Project", {
frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
});
},
- }
+ };
},
onload: function (frm) {
var so = frappe.meta.get_docfield("Project", "sales_order");
@@ -28,15 +28,15 @@ frappe.ui.form.on("Project", {
return {
"customer": frm.doc.customer,
"project_name": frm.doc.name
- }
- }
+ };
+ };
frm.set_query('customer', 'erpnext.controllers.queries.customer_query');
frm.set_query("user", "users", function () {
return {
query: "erpnext.projects.doctype.project.project.get_users_for_project"
- }
+ };
});
// sales order
@@ -51,9 +51,36 @@ frappe.ui.form.on("Project", {
return {
filters: filters
- }
+ };
});
+ },
+ refresh: function (frm) {
+ if (frm.doc.__islocal) {
+ frm.web_link && frm.web_link.remove();
+ } else {
+ frm.add_web_link("/projects?project=" + encodeURIComponent(frm.doc.name));
+
+ frm.trigger('show_dashboard');
+ }
+ frm.events.set_buttons(frm);
+ },
+
+ set_buttons: function(frm) {
+ if (!frm.is_new()) {
+ frm.add_custom_button(__('Duplicate Project with Tasks'), () => {
+ frm.events.create_duplicate(frm);
+ });
+
+ frm.add_custom_button(__('Completed'), () => {
+ frm.events.set_status(frm, 'Completed');
+ }, __('Set Status'));
+
+ frm.add_custom_button(__('Cancelled'), () => {
+ frm.events.set_status(frm, 'Cancelled');
+ }, __('Set Status'));
+ }
+
if (frappe.model.can_read("Task")) {
frm.add_custom_button(__("Gantt Chart"), function () {
frappe.route_options = {
@@ -72,27 +99,20 @@ frappe.ui.form.on("Project", {
}
},
- refresh: function (frm) {
- if (frm.doc.__islocal) {
- frm.web_link && frm.web_link.remove();
- } else {
- frm.add_web_link("/projects?project=" + encodeURIComponent(frm.doc.name));
-
- frm.trigger('show_dashboard');
- }
- frm.events.set_buttons(frm);
- },
-
- set_buttons: function(frm) {
- if (!frm.is_new()) {
- frm.add_custom_button(__('Completed'), () => {
- frm.events.set_status(frm, 'Completed');
- }, __('Set Status'));
-
- frm.add_custom_button(__('Cancelled'), () => {
- frm.events.set_status(frm, 'Cancelled');
- }, __('Set Status'));
- }
+ create_duplicate: function(frm) {
+ return new Promise(resolve => {
+ frappe.prompt('Project Name', (data) => {
+ frappe.xcall('erpnext.projects.doctype.project.project.create_duplicate_project',
+ {
+ prev_doc: frm.doc,
+ project_name: data.value
+ }).then(() => {
+ frappe.set_route('Form', "Project", data.value);
+ frappe.show_alert(__("Duplicate project has been created"));
+ });
+ resolve();
+ });
+ });
},
set_status: function(frm, status) {
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 783bcf3c38..bf6e21aa4d 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -323,6 +323,37 @@ def allow_to_make_project_update(project, time, frequency):
if get_time(nowtime()) >= get_time(time):
return True
+
+@frappe.whitelist()
+def create_duplicate_project(prev_doc, project_name):
+ ''' Create duplicate project based on the old project '''
+ import json
+ prev_doc = json.loads(prev_doc)
+
+ if project_name == prev_doc.get('name'):
+ frappe.throw(_("Use a name that is different from previous project name"))
+
+ # change the copied doc name to new project name
+ project = frappe.copy_doc(prev_doc)
+ project.name = project_name
+ project.project_template = ''
+ project.project_name = project_name
+ project.insert()
+
+ # fetch all the task linked with the old project
+ task_list = frappe.get_all("Task", filters={
+ 'project': prev_doc.get('name')
+ }, fields=['name'])
+
+ # Create duplicate task for all the task
+ for task in task_list:
+ task = frappe.get_doc('Task', task)
+ new_task = frappe.copy_doc(task)
+ new_task.project = project.name
+ new_task.insert()
+
+ project.db_set('project_template', prev_doc.get('project_template'))
+
def get_projects_for_collect_progress(frequency, fields):
fields.extend(["name"])
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 90e9f05f22..54fce8d6db 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -10,6 +10,7 @@ from frappe import _, throw
from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate
from frappe.utils.nestedset import NestedSet
from frappe.desk.form.assign_to import close_all_assignments, clear
+from frappe.utils import date_diff
class CircularReferenceError(frappe.ValidationError): pass
class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass
@@ -28,16 +29,29 @@ class Task(NestedSet):
def validate(self):
self.validate_dates()
+ self.validate_parent_project_dates()
self.validate_progress()
self.validate_status()
self.update_depends_on()
def validate_dates(self):
if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date):
- frappe.throw(_("'Expected Start Date' can not be greater than 'Expected End Date'"))
+ frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \
+ frappe.bold("Expected End Date")))
if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date):
- frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'"))
+ frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \
+ frappe.bold("Actual End Date")))
+
+ def validate_parent_project_dates(self):
+ if not self.project or frappe.flags.in_test:
+ return
+
+ expected_end_date = getdate(frappe.db.get_value("Project", self.project, "expected_end_date"))
+
+ if expected_end_date:
+ validate_project_dates(expected_end_date, self, "exp_start_date", "exp_end_date", "Expected")
+ validate_project_dates(expected_end_date, self, "act_start_date", "act_end_date", "Actual")
def validate_status(self):
if self.status!=self.get_db_value("status") and self.status == "Completed":
@@ -255,3 +269,10 @@ def add_multiple_tasks(data, parent):
def on_doctype_update():
frappe.db.add_index("Task", ["lft", "rgt"])
+
+def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date):
+ if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0:
+ frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date))
+
+ if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0:
+ frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date))
\ No newline at end of file
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index bc88250c8a..c4481c9aa0 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -188,6 +188,8 @@ class Timesheet(Document):
}, as_dict=True)
# check internal overlap
for time_log in self.time_logs:
+ if not (time_log.from_time or time_log.to_time): continue
+
if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \
args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
(args.to_time > time_log.from_time and args.to_time < time_log.to_time) or
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index ffc5e6ad36..6f43d9ef8c 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -63,12 +63,15 @@ $.extend(erpnext, {
let callback = '';
let on_close = '';
- if (grid_row.doc.serial_no) {
- grid_row.doc.has_serial_no = true;
- }
-
- me.show_serial_batch_selector(grid_row.frm, grid_row.doc,
- callback, on_close, true);
+ frappe.model.get_value('Item', {'name':grid_row.doc.item_code}, 'has_serial_no',
+ (data) => {
+ if(data) {
+ grid_row.doc.has_serial_no = data.has_serial_no;
+ me.show_serial_batch_selector(grid_row.frm, grid_row.doc,
+ callback, on_close, true);
+ }
+ }
+ );
});
},
});
diff --git a/erpnext/regional/__init__.py b/erpnext/regional/__init__.py
index 7388ea0e72..630d5fae2a 100644
--- a/erpnext/regional/__init__.py
+++ b/erpnext/regional/__init__.py
@@ -10,3 +10,21 @@ def check_deletion_permission(doc, method):
region = get_region(doc.company)
if region in ["Nepal", "France"] and doc.docstatus != 0:
frappe.throw(_("Deletion is not permitted for country {0}".format(region)))
+
+def create_transaction_log(doc, method):
+ """
+ Appends the transaction to a chain of hashed logs for legal resons.
+ Called on submit of Sales Invoice and Payment Entry.
+ """
+ region = get_region()
+ if region not in ["France", "Germany"]:
+ return
+
+ data = str(doc.as_dict())
+
+ frappe.get_doc({
+ "doctype": "Transaction Log",
+ "reference_doctype": doc.doctype,
+ "document_name": doc.name,
+ "data": data
+ }).insert(ignore_permissions=True)
diff --git a/erpnext/regional/france/utils.py b/erpnext/regional/france/utils.py
index e4b72f6586..424615dbbc 100644
--- a/erpnext/regional/france/utils.py
+++ b/erpnext/regional/france/utils.py
@@ -3,22 +3,6 @@
from __future__ import unicode_literals
import frappe
-from frappe import _
-from erpnext import get_region
-
-def create_transaction_log(doc, method):
- region = get_region()
- if region not in ["France"]:
- return
- else:
- data = str(doc.as_dict())
-
- frappe.get_doc({
- "doctype": "Transaction Log",
- "reference_doctype": doc.doctype,
- "document_name": doc.name,
- "data": data
- }).insert(ignore_permissions=True)
# don't remove this function it is used in tests
def test_method():
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 0bc277f4af..aae07797a1 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -72,8 +72,8 @@ def validate_gstin_check_digit(gstin, label='GSTIN'):
total += digit
factor = 2 if factor == 1 else 1
if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]:
- frappe.throw(_("Invalid {0}! The check digit validation has failed. " +
- "Please ensure you've typed the {0} correctly.".format(label)))
+ frappe.throw(_("""Invalid {0}! The check digit validation has failed.
+ Please ensure you've typed the {0} correctly.""".format(label)))
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
if frappe.get_meta(item_doctype).has_field('gst_hsn_code'):
diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py
index 922619cc42..090616b077 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -116,7 +116,7 @@ class Gstr1Report(object):
taxable_value = 0
for item_code, net_amount in self.invoice_items.get(invoice).items():
if item_code in items:
- if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code):
+ if self.item_tax_rate.get(invoice) and tax_rate in self.item_tax_rate.get(invoice, {}).get(item_code, []):
taxable_value += abs(net_amount)
elif not self.item_tax_rate.get(invoice):
taxable_value += abs(net_amount)
diff --git a/erpnext/assets/doctype/asset_settings/__init__.py b/erpnext/regional/turkey/__init__.py
similarity index 100%
rename from erpnext/assets/doctype/asset_settings/__init__.py
rename to erpnext/regional/turkey/__init__.py
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js
new file mode 100644
index 0000000000..ee806a78fb
--- /dev/null
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js
@@ -0,0 +1,33 @@
+// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Item-wise Sales History"] = {
+ "filters": [
+ {
+ fieldname:"company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ reqd: 1
+ },
+ {
+ fieldname:"item_group",
+ label: __("Item Group"),
+ fieldtype: "Link",
+ options: "Item Group"
+ },
+ {
+ fieldname:"from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ },
+
+ ]
+};
\ No newline at end of file
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.json b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.json
index 88e6f27395..a6dda289de 100644
--- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.json
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.json
@@ -1,34 +1,34 @@
{
- "add_total_row": 1,
- "creation": "2013-05-23 17:42:24",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 3,
- "is_standard": "Yes",
- "modified": "2019-01-03 22:52:41.519890",
- "modified_by": "Administrator",
- "module": "Selling",
- "name": "Item-wise Sales History",
- "owner": "Administrator",
- "prepared_report": 0,
- "query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n so_item.item_group as \"Item Group:Link/Item Group:120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Data:100\",\n\tso_item.uom as \"UOM:Link/UOM:80\",\n\tso_item.base_rate as \"Rate:Currency:120\",\n\tso_item.base_amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n cu.customer_name as \"Customer Name::150\",\n\tcu.customer_group as \"Customer Group:Link/Customer Group:130\",\n\tso.territory as \"Territory:Link/Territory:130\",\n \tso.project as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Float:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\",\n\tso.company as \"Company:Link/Company:\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item, `tabCustomer` cu\nwhere\n\tso.name = so_item.parent and so.customer=cu.name\n\tand so.docstatus = 1\norder by so.name desc",
- "ref_doctype": "Sales Order",
- "report_name": "Item-wise Sales History",
- "report_type": "Query Report",
+ "add_total_row": 1,
+ "creation": "2013-05-23 17:42:24",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 3,
+ "is_standard": "Yes",
+ "modified": "2019-11-04 16:28:14.608904",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Item-wise Sales History",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Sales Order",
+ "report_name": "Item-wise Sales History",
+ "report_type": "Script Report",
"roles": [
{
"role": "Sales User"
- },
+ },
{
"role": "Sales Manager"
- },
+ },
{
"role": "Maintenance User"
- },
+ },
{
"role": "Accounts User"
- },
+ },
{
"role": "Stock User"
}
diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
new file mode 100644
index 0000000000..226c34f735
--- /dev/null
+++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py
@@ -0,0 +1,214 @@
+# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.utils import flt
+from frappe.utils.nestedset import get_descendants_of
+
+def execute(filters=None):
+ filters = frappe._dict(filters or {})
+ columns = get_columns(filters)
+ data = get_data(filters)
+ return columns, data
+
+def get_columns(filters):
+ return [
+ {
+ "label": _("Item Code"),
+ "fieldtype": "Link",
+ "fieldname": "item_code",
+ "options": "Item",
+ "width": 120
+ },
+ {
+ "label": _("Item Name"),
+ "fieldtype": "Data",
+ "fieldname": "item_name",
+ "width": 140
+ },
+ {
+ "label": _("Item Group"),
+ "fieldtype": "Link",
+ "fieldname": "item_group",
+ "options": "Item Group",
+ "width": 120
+ },
+ {
+ "label": _("Description"),
+ "fieldtype": "Data",
+ "fieldname": "description",
+ "width": 150
+ },
+ {
+ "label": _("Quantity"),
+ "fieldtype": "Float",
+ "fieldname": "quantity",
+ "width": 150
+ },
+ {
+ "label": _("UOM"),
+ "fieldtype": "Link",
+ "fieldname": "uom",
+ "options": "UOM",
+ "width": 100
+ },
+ {
+ "label": _("Rate"),
+ "fieldname": "rate",
+ "options": "Currency",
+ "width": 120
+ },
+ {
+ "label": _("Amount"),
+ "fieldname": "amount",
+ "options": "Currency",
+ "width": 120
+ },
+ {
+ "label": _("Sales Order"),
+ "fieldtype": "Link",
+ "fieldname": "sales_order",
+ "options": "Sales Order",
+ "width": 100
+ },
+ {
+ "label": _("Transaction Date"),
+ "fieldtype": "Date",
+ "fieldname": "transaction_date",
+ "width": 90
+ },
+ {
+ "label": _("Customer"),
+ "fieldtype": "Link",
+ "fieldname": "customer",
+ "options": "Customer",
+ "width": 100
+ },
+ {
+ "label": _("Customer Name"),
+ "fieldtype": "Data",
+ "fieldname": "customer_name",
+ "width": 140
+ },
+ {
+ "label": _("Customer Group"),
+ "fieldtype": "Link",
+ "fieldname": "customer_group",
+ "options": "customer Group",
+ "width": 120
+ },
+ {
+ "label": _("Territory"),
+ "fieldtype": "Link",
+ "fieldname": "territory",
+ "options": "Territory",
+ "width": 100
+ },
+ {
+ "label": _("Project"),
+ "fieldtype": "Link",
+ "fieldname": "project",
+ "options": "Project",
+ "width": 100
+ },
+ {
+ "label": _("Delivered Quantity"),
+ "fieldtype": "Float",
+ "fieldname": "delivered_quantity",
+ "width": 150
+ },
+ {
+ "label": _("Billed Amount"),
+ "fieldname": "rate",
+ "options": "billed_amount",
+ "width": 120
+ },
+ {
+ "label": _("Company"),
+ "fieldtype": "Link",
+ "fieldname": "company",
+ "options": "Company",
+ "width": 100
+ }
+ ]
+
+def get_data(filters):
+
+ data = []
+
+ company_list = get_descendants_of("Company", filters.get("company"))
+ company_list.append(filters.get("company"))
+
+ customer_details = get_customer_details()
+ sales_order_records = get_sales_order_details(company_list, filters)
+
+ for record in sales_order_records:
+ customer_record = customer_details.get(record.customer)
+ row = {
+ "item_code": record.item_code,
+ "item_name": record.item_name,
+ "item_group": record.item_group,
+ "description": record.description,
+ "quantity": record.qty,
+ "uom": record.uom,
+ "rate": record.base_rate,
+ "amount": record.base_amount,
+ "sales_order": record.name,
+ "transaction_date": record.transaction_date,
+ "customer": record.customer,
+ "customer_name": customer_record.customer_name,
+ "customer_group": customer_record.customer_group,
+ "territory": record.territory,
+ "project": record.project,
+ "delivered_quantity": flt(record.delivered_qty),
+ "billed_amount": flt(record.billed_amt),
+ "company": record.company
+ }
+ data.append(row)
+
+ return data
+
+def get_conditions(filters):
+ conditions = ''
+ if filters.get('item_group'):
+ conditions += "AND so_item.item_group = %s" %frappe.db.escape(filters.item_group)
+
+ if filters.get('from_date'):
+ conditions += "AND so.transaction_date >= '%s'" %filters.from_date
+
+ if filters.get('to_date'):
+ conditions += "AND so.transaction_date <= '%s'" %filters.to_date
+
+ return conditions
+
+def get_customer_details():
+ details = frappe.get_all('Customer',
+ fields=['name', 'customer_name', "customer_group"])
+ customer_details = {}
+ for d in details:
+ customer_details.setdefault(d.name, frappe._dict({
+ "customer_name": d.customer_name,
+ "customer_group": d.customer_group
+ }))
+ return customer_details
+
+def get_sales_order_details(company_list, filters):
+ conditions = get_conditions(filters)
+ return frappe.db.sql("""
+ SELECT
+ so_item.item_code, so_item.item_name, so_item.item_group,
+ so_item.description, so_item.qty, so_item.uom,
+ so_item.base_rate, so_item.base_amount, so.name,
+ so.transaction_date, so.customer, so.territory,
+ so.project, so_item.delivered_qty,
+ so_item.billed_amt, so.company
+ FROM
+ `tabSales Order` so, `tabSales Order Item` so_item
+ WHERE
+ so.name = so_item.parent
+ AND so.company in (%s)
+ AND so.docstatus = 1
+ {0}
+ """.format(conditions), company_list, as_dict=1) #nosec
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index bc3418997d..2d181b53ca 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -72,6 +72,7 @@
"stock_received_but_not_billed",
"expenses_included_in_valuation",
"fixed_asset_depreciation_settings",
+ "enable_cwip_accounting",
"accumulated_depreciation_account",
"depreciation_expense_account",
"series_for_depreciation_entry",
@@ -720,12 +721,18 @@
"fieldtype": "Link",
"label": "Default Buying Terms",
"options": "Terms and Conditions"
+ },
+ {
+ "default": "0",
+ "fieldname": "enable_cwip_accounting",
+ "fieldtype": "Check",
+ "label": "Enable Capital Work in Progress Accounting"
}
],
"icon": "fa fa-building",
"idx": 1,
"image_field": "company_logo",
- "modified": "2019-07-04 22:20:45.104307",
+ "modified": "2019-10-09 14:42:04.440974",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
@@ -767,6 +774,18 @@
{
"read": 1,
"role": "Projects User"
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts Manager",
+ "share": 1,
+ "write": 1
}
],
"show_name_in_global_search": 1,
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 8b42b19921..04e8a83e7f 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -207,7 +207,7 @@ class Company(NestedSet):
"default_expense_account": "Cost of Goods Sold"
})
- if self.update_default_account or frappe.flags.in_test:
+ if self.update_default_account:
for default_account in default_accounts:
self._set_default_account(default_account, default_accounts.get(default_account))
diff --git a/erpnext/setup/doctype/currency_exchange/currency_exchange.py b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
index 4effb5ab01..6480f60f59 100644
--- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
@@ -11,10 +11,19 @@ from frappe.utils import get_datetime_str, formatdate, nowdate, cint
class CurrencyExchange(Document):
def autoname(self):
+ purpose = ""
if not self.date:
self.date = nowdate()
- self.name = '{0}-{1}-{2}'.format(formatdate(get_datetime_str(self.date), "yyyy-MM-dd"),
- self.from_currency, self.to_currency)
+
+ # If both selling and buying enabled
+ purpose = "Selling-Buying"
+ if cint(self.for_buying)==0 and cint(self.for_selling)==1:
+ purpose = "Selling"
+ if cint(self.for_buying)==1 and cint(self.for_selling)==0:
+ purpose = "Buying"
+
+ self.name = '{0}-{1}-{2}{3}'.format(formatdate(get_datetime_str(self.date), "yyyy-MM-dd"),
+ self.from_currency, self.to_currency, ("-" + purpose) if purpose else "")
def validate(self):
self.validate_value("exchange_rate", ">", 0)
@@ -23,4 +32,4 @@ class CurrencyExchange(Document):
throw(_("From Currency and To Currency cannot be same"))
if not cint(self.for_buying) and not cint(self.for_selling):
- throw(_("Currency Exchange must be applicable for Buying or for Selling."))
\ No newline at end of file
+ throw(_("Currency Exchange must be applicable for Buying or for Selling."))
diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
index c488b996ff..c5c01c5775 100644
--- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py
@@ -4,15 +4,23 @@ from __future__ import unicode_literals
import frappe, unittest
from frappe.utils import flt
from erpnext.setup.utils import get_exchange_rate
+from frappe.utils import cint
test_records = frappe.get_test_records('Currency Exchange')
def save_new_records(test_records):
for record in test_records:
+ # If both selling and buying enabled
+ purpose = "Selling-Buying"
+
+ if cint(record.get("for_buying"))==0 and cint(record.get("for_selling"))==1:
+ purpose = "Selling"
+ if cint(record.get("for_buying"))==1 and cint(record.get("for_selling"))==0:
+ purpose = "Buying"
kwargs = dict(
doctype=record.get("doctype"),
- docname=record.get("date") + '-' + record.get("from_currency") + '-' + record.get("to_currency"),
+ docname=record.get("date") + '-' + record.get("from_currency") + '-' + record.get("to_currency") + '-' + purpose,
fieldname="exchange_rate",
value=record.get("exchange_rate"),
)
@@ -25,6 +33,8 @@ def save_new_records(test_records):
curr_exchange.from_currency = record["from_currency"]
curr_exchange.to_currency = record["to_currency"]
curr_exchange.exchange_rate = record["exchange_rate"]
+ curr_exchange.for_buying = record["for_buying"]
+ curr_exchange.for_selling = record["for_selling"]
curr_exchange.insert()
@@ -44,18 +54,18 @@ class TestCurrencyExchange(unittest.TestCase):
frappe.db.set_value("Accounts Settings", None, "allow_stale", 1)
# Start with allow_stale is True
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01")
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01", "for_buying")
self.assertEqual(flt(exchange_rate, 3), 60.0)
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
self.assertEqual(exchange_rate, 65.1)
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30")
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling")
self.assertEqual(exchange_rate, 62.9)
# Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
self.clear_cache()
- exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
+ exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_selling")
self.assertFalse(exchange_rate == 60)
self.assertEqual(flt(exchange_rate, 3), 66.894)
@@ -64,35 +74,35 @@ class TestCurrencyExchange(unittest.TestCase):
frappe.db.set_value("Accounts Settings", None, "allow_stale", 0)
frappe.db.set_value("Accounts Settings", None, "stale_days", 1)
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01")
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01", "for_buying")
self.assertEqual(exchange_rate, 60.0)
# Will fetch from fixer.io
self.clear_cache()
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
self.assertEqual(flt(exchange_rate, 3), 67.79)
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30")
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling")
self.assertEqual(exchange_rate, 62.9)
# Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
self.clear_cache()
- exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
+ exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_buying")
self.assertEqual(flt(exchange_rate, 3), 66.894)
- exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-10")
+ exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-10", "for_selling")
self.assertEqual(exchange_rate, 65.1)
# NGN is not available on fixer.io so these should return 0
- exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-09")
+ exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-09", "for_selling")
self.assertEqual(exchange_rate, 0)
- exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-11")
+ exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-11", "for_selling")
self.assertEqual(exchange_rate, 0)
def test_exchange_rate_strict_switched(self):
# Start with allow_stale is True
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
self.assertEqual(exchange_rate, 65.1)
frappe.db.set_value("Accounts Settings", None, "allow_stale", 0)
@@ -100,5 +110,5 @@ class TestCurrencyExchange(unittest.TestCase):
# Will fetch from fixer.io
self.clear_cache()
- exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
- self.assertEqual(flt(exchange_rate, 3), 67.79)
\ No newline at end of file
+ exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying")
+ self.assertEqual(flt(exchange_rate, 3), 67.79)
diff --git a/erpnext/setup/doctype/currency_exchange/test_records.json b/erpnext/setup/doctype/currency_exchange/test_records.json
index 0c9cfbb67c..152060edfc 100644
--- a/erpnext/setup/doctype/currency_exchange/test_records.json
+++ b/erpnext/setup/doctype/currency_exchange/test_records.json
@@ -1,44 +1,56 @@
[
- {
- "doctype": "Currency Exchange",
- "date": "2016-01-01",
- "exchange_rate": 60.0,
- "from_currency": "USD",
- "to_currency": "INR"
- },
- {
- "doctype": "Currency Exchange",
- "date": "2016-01-01",
- "exchange_rate": 0.773,
- "from_currency": "USD",
- "to_currency": "EUR"
- },
- {
- "doctype": "Currency Exchange",
- "date": "2016-01-01",
- "exchange_rate": 0.0167,
- "from_currency": "INR",
- "to_currency": "USD"
- },
- {
- "doctype": "Currency Exchange",
- "date": "2016-01-10",
- "exchange_rate": 65.1,
- "from_currency": "USD",
- "to_currency": "INR"
- },
+ {
+ "doctype": "Currency Exchange",
+ "date": "2016-01-01",
+ "exchange_rate": 60.0,
+ "from_currency": "USD",
+ "to_currency": "INR",
+ "for_buying": 1,
+ "for_selling": 0
+ },
{
- "doctype": "Currency Exchange",
- "date": "2016-01-30",
- "exchange_rate": 62.9,
- "from_currency": "USD",
- "to_currency": "INR"
- },
- {
- "doctype": "Currency Exchange",
- "date": "2016-01-10",
- "exchange_rate": 65.1,
- "from_currency": "INR",
- "to_currency": "NGN"
- }
-]
\ No newline at end of file
+ "doctype": "Currency Exchange",
+ "date": "2016-01-01",
+ "exchange_rate": 0.773,
+ "from_currency": "USD",
+ "to_currency": "EUR",
+ "for_buying": 0,
+ "for_selling": 1
+ },
+ {
+ "doctype": "Currency Exchange",
+ "date": "2016-01-01",
+ "exchange_rate": 0.0167,
+ "from_currency": "INR",
+ "to_currency": "USD",
+ "for_buying": 1,
+ "for_selling": 0
+ },
+ {
+ "doctype": "Currency Exchange",
+ "date": "2016-01-10",
+ "exchange_rate": 65.1,
+ "from_currency": "USD",
+ "to_currency": "INR",
+ "for_buying": 1,
+ "for_selling": 0
+ },
+ {
+ "doctype": "Currency Exchange",
+ "date": "2016-01-30",
+ "exchange_rate": 62.9,
+ "from_currency": "USD",
+ "to_currency": "INR",
+ "for_buying": 1,
+ "for_selling": 1
+ },
+ {
+ "doctype": "Currency Exchange",
+ "date": "2016-01-10",
+ "exchange_rate": 65.1,
+ "from_currency": "INR",
+ "to_currency": "NGN",
+ "for_buying": 1,
+ "for_selling": 1
+ }
+]
diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py
index 1de5ccb142..0bcddc2151 100644
--- a/erpnext/setup/doctype/email_digest/email_digest.py
+++ b/erpnext/setup/doctype/email_digest/email_digest.py
@@ -4,8 +4,8 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import fmt_money, formatdate, format_time, now_datetime, \
- get_url_to_form, get_url_to_list, flt, get_link_to_report
+from frappe.utils import (fmt_money, formatdate, format_time, now_datetime,
+ get_url_to_form, get_url_to_list, flt, get_link_to_report, add_to_date, today)
from datetime import timedelta
from dateutil.relativedelta import relativedelta
from frappe.core.doctype.user.user import STANDARD_USERS
@@ -151,8 +151,9 @@ class EmailDigest(Document):
def get_calendar_events(self):
"""Get calendar events for given user"""
from frappe.desk.doctype.event.event import get_events
- events = get_events(self.future_from_date.strftime("%Y-%m-%d"),
- self.future_to_date.strftime("%Y-%m-%d")) or []
+ from_date, to_date = get_future_date_for_calendaer_event(self.frequency)
+
+ events = get_events(from_date, to_date)
event_count = 0
for i, e in enumerate(events):
@@ -825,4 +826,14 @@ def get_count_for_period(account, fieldname, from_date, to_date):
last_year_closing_count = get_count_on(account, fieldname, fy_start_date - timedelta(days=1))
count = count_on_to_date + (last_year_closing_count - count_before_from_date)
- return count
\ No newline at end of file
+ return count
+
+def get_future_date_for_calendaer_event(frequency):
+ from_date = to_date = today()
+
+ if frequency == "Weekly":
+ to_date = add_to_date(from_date, weeks=1)
+ elif frequency == "Monthly":
+ to_date = add_to_date(from_date, months=1)
+
+ return from_date, to_date
\ No newline at end of file
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index a4c10cfb7d..1236ade45f 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -11,6 +11,7 @@ from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings
from frappe.utils.nestedset import get_root_of
from erpnext.accounts.utils import get_account_name
from erpnext.utilities.product import get_qty_in_stock
+from frappe.contacts.doctype.contact.contact import get_contact_name
class WebsitePriceListMissingError(frappe.ValidationError):
@@ -371,7 +372,7 @@ def get_party(user=None):
if not user:
user = frappe.session.user
- contact_name = frappe.db.get_value("Contact", {"email_id": user})
+ contact_name = get_contact_name(user)
party = None
if contact_name:
@@ -417,7 +418,7 @@ def get_party(user=None):
contact = frappe.new_doc("Contact")
contact.update({
"first_name": fullname,
- "email_id": user
+ "email_ids": [{"email_id": user, "is_primary": 1}]
})
contact.append('links', dict(link_doctype='Customer', link_name=customer.name))
contact.flags.ignore_mandatory = True
@@ -504,7 +505,7 @@ def get_applicable_shipping_rules(party=None, quotation=None):
if shipping_rules:
rule_label_map = frappe.db.get_values("Shipping Rule", shipping_rules, "label")
# we need this in sorted order as per the position of the rule in the settings page
- return [[rule, rule_label_map.get(rule)] for rule in shipping_rules]
+ return [[rule, rule] for rule in shipping_rules]
def get_shipping_rules(quotation=None, cart_settings=None):
if not quotation:
@@ -562,4 +563,4 @@ def apply_coupon_code(applied_code,applied_referral_sales_partner):
frappe.throw(_("Please enter valid coupon code !!"))
else:
frappe.throw(_("Please enter coupon code !!"))
- return quotation
\ No newline at end of file
+ return quotation
diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py
index f609a0be7d..3e890b4dd4 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -185,9 +185,17 @@ def get_batches_by_oldest(item_code, warehouse):
def split_batch(batch_no, item_code, warehouse, qty, new_batch_id=None):
"""Split the batch into a new batch"""
batch = frappe.get_doc(dict(doctype='Batch', item=item_code, batch_id=new_batch_id)).insert()
+
+ company = frappe.db.get_value('Stock Ledger Entry', dict(
+ item_code=item_code,
+ batch_no=batch_no,
+ warehouse=warehouse
+ ), ['company'])
+
stock_entry = frappe.get_doc(dict(
doctype='Stock Entry',
purpose='Repack',
+ company=company,
items=[
dict(
item_code=item_code,
diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
index 1aaf73f3ad..0cc243d4cb 100644
--- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
+++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
@@ -1,129 +1,51 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2014-07-11 11:51:00.453717",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "creation": "2014-07-11 11:51:00.453717",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "expense_account",
+ "description",
+ "col_break3",
+ "amount"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "description",
- "fieldtype": "Small Text",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Description",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
- },
+ "fieldname": "description",
+ "fieldtype": "Small Text",
+ "in_list_view": 1,
+ "label": "Description",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "col_break3",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0,
+ "fieldname": "col_break3",
+ "fieldtype": "Column Break",
"width": "50%"
- },
+ },
{
- "allow_bulk_edit": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "amount",
- "fieldtype": "Currency",
- "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": "Amount",
- "length": 0,
- "no_copy": 0,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "unique": 0
+ "fieldname": "amount",
+ "fieldtype": "Currency",
+ "in_list_view": 1,
+ "label": "Amount",
+ "options": "Company:company:default_currency",
+ "reqd": 1
+ },
+ {
+ "fieldname": "expense_account",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Expense Account",
+ "options": "Account",
+ "reqd": 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": 1,
- "max_attachments": 0,
- "modified": "2017-11-15 19:27:59.542487",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Landed Cost Taxes and Charges",
- "name_case": "",
- "owner": "Administrator",
- "permissions": [],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "track_changes": 0,
- "track_seen": 0
+ ],
+ "istable": 1,
+ "modified": "2019-09-30 18:28:32.070655",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Landed Cost Taxes and Charges",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
index edc3444220..c9a3fd976f 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.js
@@ -30,6 +30,16 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
this.frm.add_fetch("receipt_document", "posting_date", "posting_date");
this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total");
+ this.frm.set_query("expense_account", "taxes", function() {
+ return {
+ query: "erpnext.controllers.queries.tax_account_query",
+ filters: {
+ "account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
+ "company": me.frm.doc.company
+ }
+ };
+ });
+
},
refresh: function(frm) {
@@ -38,7 +48,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
-
+
${__("Notes")}:
@@ -96,7 +106,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
var me = this;
if(this.frm.doc.taxes.length) {
-
+
var total_item_cost = 0.0;
var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase();
$.each(this.frm.doc.items || [], function(i, d) {
@@ -105,7 +115,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
var total_charges = 0.0;
$.each(this.frm.doc.items || [], function(i, item) {
- item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
+ item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
total_charges += item.applicable_charges
});
diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
index 4dc0b7b059..fe5d3ed6df 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py
@@ -179,7 +179,7 @@ def submit_landed_cost_voucher(receipt_document_type, receipt_document, charges=
lcv.set("taxes", [{
"description": "Insurance Charges",
- "account": "_Test Account Insurance Charges - _TC",
+ "expense_account": "Expenses Included In Valuation - TCP1",
"amount": charges
}])
diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js
index 3f66743f07..278971125f 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -173,8 +173,10 @@ frappe.ui.form.on('Pick List Item', {
});
function get_item_details(item_code, uom=null) {
- return frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.get_item_details', {
- item_code,
- uom
- });
+ if (item_code) {
+ return frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.get_item_details', {
+ item_code,
+ uom
+ });
+ }
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index fae4de3691..1bfdca50ea 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -14,7 +14,7 @@ from erpnext.accounts.utils import get_account_currency
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.mapper import get_mapped_doc
from erpnext.buying.utils import check_on_hold_or_closed_status
-from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_disabled
+from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from six import iteritems
@@ -195,6 +195,7 @@ class PurchaseReceipt(BuyingController):
from erpnext.accounts.general_ledger import process_gl_map
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
+ landed_cost_entries = get_item_account_wise_additional_cost(self.name)
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
gl_entries = []
@@ -233,15 +234,16 @@ class PurchaseReceipt(BuyingController):
negative_expense_to_be_booked += flt(d.item_tax_amount)
# Amount added through landed-cost-voucher
- if flt(d.landed_cost_voucher_amount):
- gl_entries.append(self.get_gl_dict({
- "account": expenses_included_in_valuation,
- "against": warehouse_account[d.warehouse]["account"],
- "cost_center": d.cost_center,
- "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "credit": flt(d.landed_cost_voucher_amount),
- "project": d.project
- }, item=d))
+ if landed_cost_entries:
+ for account, amount in iteritems(landed_cost_entries[(d.item_code, d.name)]):
+ gl_entries.append(self.get_gl_dict({
+ "account": account,
+ "against": warehouse_account[d.warehouse]["account"],
+ "cost_center": d.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+ "credit": flt(amount),
+ "project": d.project
+ }, item=d))
# sub-contracting warehouse
if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
@@ -336,12 +338,13 @@ class PurchaseReceipt(BuyingController):
def get_asset_gl_entry(self, gl_entries, expenses_included_in_valuation=None):
arbnb_account, cwip_account = None, None
- cwip_disabled = is_cwip_accounting_disabled()
-
if not expenses_included_in_valuation:
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
for d in self.get("items"):
+ asset_category = frappe.get_cached_value("Item", d.item_code, "asset_category")
+ cwip_enabled = is_cwip_accounting_enabled(self.company, asset_category)
+
if d.is_fixed_asset and not (arbnb_account and cwip_account):
arbnb_account = self.get_company_default("asset_received_but_not_billed")
@@ -349,8 +352,7 @@ class PurchaseReceipt(BuyingController):
cwip_account = get_asset_account("capital_work_in_progress_account", d.asset,
company = self.company)
- if d.is_fixed_asset and not cwip_disabled:
-
+ if d.is_fixed_asset and cwip_enabled:
asset_amount = flt(d.net_amount) + flt(d.item_tax_amount/self.conversion_rate)
base_asset_amount = flt(d.base_net_amount + d.item_tax_amount)
@@ -379,7 +381,7 @@ class PurchaseReceipt(BuyingController):
if d.is_fixed_asset and flt(d.landed_cost_voucher_amount):
asset_account = (get_asset_category_account(d.asset, 'fixed_asset_account',
- company = self.company) if cwip_disabled else cwip_account)
+ company = self.company) if not cwip_enabled else cwip_account)
gl_entries.append(self.get_gl_dict({
"account": expenses_included_in_valuation,
@@ -584,3 +586,30 @@ def make_stock_entry(source_name,target_doc=None):
}, target_doc, set_missing_values)
return doclist
+
+def get_item_account_wise_additional_cost(purchase_document):
+ landed_cost_voucher = frappe.get_value("Landed Cost Purchase Receipt",
+ {"receipt_document": purchase_document}, "parent")
+
+ if not landed_cost_voucher:
+ return
+
+ total_item_cost = 0
+ item_account_wise_cost = {}
+ landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", landed_cost_voucher)
+ based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
+
+ for item in landed_cost_voucher_doc.items:
+ if item.receipt_document == purchase_document:
+ total_item_cost += item.get(based_on_field)
+
+ for item in landed_cost_voucher_doc.items:
+ if item.receipt_document == purchase_document:
+ for account in landed_cost_voucher_doc.taxes:
+ item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {})
+ item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, 0.0)
+ item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account] += \
+ account.amount * item.get(based_on_field) / total_item_cost
+
+ return item_account_wise_cost
+
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 0b023024f9..6e78b988f6 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -68,6 +68,16 @@ frappe.ui.form.on('Stock Entry', {
}
});
+ frm.set_query("expense_account", "additional_costs", function() {
+ return {
+ query: "erpnext.controllers.queries.tax_account_query",
+ filters: {
+ "account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
+ "company": frm.doc.company
+ }
+ };
+ });
+
frm.add_fetch("bom_no", "inspection_required", "inspection_required");
},
@@ -727,7 +737,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
return frappe.call({
method: "erpnext.stock.doctype.stock_entry.stock_entry.get_work_order_details",
args: {
- work_order: me.frm.doc.work_order
+ work_order: me.frm.doc.work_order,
+ company: me.frm.doc.company
},
callback: function(r) {
if (!r.exc) {
@@ -743,6 +754,8 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
if (me.frm.doc.purpose == "Manufacture") {
if (!me.frm.doc.to_warehouse) me.frm.set_value("to_warehouse", r.message["fg_warehouse"]);
if (r.message["additional_costs"].length) {
+ me.frm.clear_table("additional_costs");
+
$.each(r.message["additional_costs"], function(i, row) {
me.frm.add_child("additional_costs", row);
})
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 55e02a46ff..26693d208b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -644,28 +644,37 @@ class StockEntry(StockController):
self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
def get_gl_entries(self, warehouse_account):
- expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
-
gl_entries = super(StockEntry, self).get_gl_entries(warehouse_account)
- for d in self.get("items"):
- additional_cost = flt(d.additional_cost, d.precision("additional_cost"))
- if additional_cost:
- gl_entries.append(self.get_gl_dict({
- "account": expenses_included_in_valuation,
- "against": d.expense_account,
- "cost_center": d.cost_center,
- "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "credit": additional_cost
- }, item=d))
+ total_basic_amount = sum([flt(t.basic_amount) for t in self.get("items") if t.t_warehouse])
+ item_account_wise_additional_cost = {}
- gl_entries.append(self.get_gl_dict({
- "account": d.expense_account,
- "against": expenses_included_in_valuation,
- "cost_center": d.cost_center,
- "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "credit": -1 * additional_cost # put it as negative credit instead of debit purposefully
- }, item=d))
+ for t in self.get("additional_costs"):
+ for d in self.get("items"):
+ if d.t_warehouse:
+ item_account_wise_additional_cost.setdefault((d.item_code, d.name), {})
+ item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, 0.0)
+ item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account] += \
+ (t.amount * d.basic_amount) / total_basic_amount
+
+ if item_account_wise_additional_cost:
+ for d in self.get("items"):
+ for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})):
+ gl_entries.append(self.get_gl_dict({
+ "account": account,
+ "against": d.expense_account,
+ "cost_center": d.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+ "credit": amount
+ }, item=d))
+
+ gl_entries.append(self.get_gl_dict({
+ "account": d.expense_account,
+ "against": account,
+ "cost_center": d.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+ "credit": -1 * amount # put it as negative credit instead of debit purposefully
+ }, item=d))
return gl_entries
@@ -1349,7 +1358,7 @@ def make_stock_in_entry(source_name, target_doc=None):
return doclist
@frappe.whitelist()
-def get_work_order_details(work_order):
+def get_work_order_details(work_order, company):
work_order = frappe.get_doc("Work Order", work_order)
pending_qty_to_produce = flt(work_order.qty) - flt(work_order.produced_qty)
@@ -1360,14 +1369,17 @@ def get_work_order_details(work_order):
"wip_warehouse": work_order.wip_warehouse,
"fg_warehouse": work_order.fg_warehouse,
"fg_completed_qty": pending_qty_to_produce,
- "additional_costs": get_additional_costs(work_order, fg_qty=pending_qty_to_produce)
+ "additional_costs": get_additional_costs(work_order, fg_qty=pending_qty_to_produce, company=company)
}
-def get_additional_costs(work_order=None, bom_no=None, fg_qty=None):
+def get_additional_costs(work_order=None, bom_no=None, fg_qty=None, company=None):
additional_costs = []
operating_cost_per_unit = get_operating_cost_per_unit(work_order, bom_no)
+ expenses_included_in_valuation = frappe.get_cached_value("Company", company, "expenses_included_in_valuation")
+
if operating_cost_per_unit:
additional_costs.append({
+ "expense_account": expenses_included_in_valuation,
"description": "Operating Cost as per Work Order / BOM",
"amount": operating_cost_per_unit * flt(fg_qty)
})
@@ -1377,6 +1389,7 @@ def get_additional_costs(work_order=None, bom_no=None, fg_qty=None):
flt(work_order.additional_operating_cost) / flt(work_order.qty)
additional_costs.append({
+ "expense_account": expenses_included_in_valuation,
"description": "Additional Operating Cost",
"amount": additional_operating_cost_per_unit * flt(fg_qty)
})
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 941472bbf9..eddab5d79d 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -259,6 +259,8 @@ class TestStockEntry(unittest.TestCase):
repack.posting_date = nowdate()
repack.posting_time = nowtime()
+ expenses_included_in_valuation = frappe.get_value("Company", company, "expenses_included_in_valuation")
+
items = get_multiple_items()
repack.items = []
for item in items:
@@ -266,11 +268,13 @@ class TestStockEntry(unittest.TestCase):
repack.set("additional_costs", [
{
- "description": "Actual Oerating Cost",
+ "expense_account": expenses_included_in_valuation,
+ "description": "Actual Operating Cost",
"amount": 1000
},
{
- "description": "additional operating costs",
+ "expense_account": expenses_included_in_valuation,
+ "description": "Additional Operating Cost",
"amount": 200
},
])
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 98a8c59483..ca2741ccfb 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -52,9 +52,10 @@ class StockReconciliation(StockController):
def _changed(item):
item_dict = get_stock_balance_for(item.item_code, item.warehouse,
self.posting_date, self.posting_time, batch_no=item.batch_no)
- if (((item.qty is None or item.qty==item_dict.get("qty")) and
- (item.valuation_rate is None or item.valuation_rate==item_dict.get("rate")) and not item.serial_no)
- or (item.serial_no and item.serial_no == item_dict.get("serial_nos"))):
+
+ if ((item.qty is None or item.qty==item_dict.get("qty")) and
+ (item.valuation_rate is None or item.valuation_rate==item_dict.get("rate")) and
+ (not item.serial_no or (item.serial_no == item_dict.get("serial_nos")) )):
return False
else:
# set default as current rates
@@ -182,9 +183,11 @@ class StockReconciliation(StockController):
from erpnext.stock.stock_ledger import get_previous_sle
sl_entries = []
+ has_serial_no = False
for row in self.items:
item = frappe.get_doc("Item", row.item_code)
if item.has_serial_no or item.has_batch_no:
+ has_serial_no = True
self.get_sle_for_serialized_items(row, sl_entries)
else:
previous_sle = get_previous_sle({
@@ -212,8 +215,14 @@ class StockReconciliation(StockController):
sl_entries.append(self.get_sle_for_items(row))
if sl_entries:
+ if has_serial_no:
+ sl_entries = self.merge_similar_item_serial_nos(sl_entries)
+
self.make_sl_entries(sl_entries)
+ if has_serial_no and sl_entries:
+ self.update_valuation_rate_for_serial_no()
+
def get_sle_for_serialized_items(self, row, sl_entries):
from erpnext.stock.stock_ledger import get_previous_sle
@@ -275,8 +284,18 @@ class StockReconciliation(StockController):
# update valuation rate
self.update_valuation_rate_for_serial_nos(row, serial_nos)
+ def update_valuation_rate_for_serial_no(self):
+ for d in self.items:
+ if not d.serial_no: continue
+
+ serial_nos = get_serial_nos(d.serial_no)
+ self.update_valuation_rate_for_serial_nos(d, serial_nos)
+
def update_valuation_rate_for_serial_nos(self, row, serial_nos):
valuation_rate = row.valuation_rate if self.docstatus == 1 else row.current_valuation_rate
+ if valuation_rate is None:
+ return
+
for d in serial_nos:
frappe.db.set_value("Serial No", d, 'purchase_rate', valuation_rate)
@@ -321,11 +340,17 @@ class StockReconciliation(StockController):
where voucher_type=%s and voucher_no=%s""", (self.doctype, self.name))
sl_entries = []
+
+ has_serial_no = False
for row in self.items:
if row.serial_no or row.batch_no or row.current_serial_no:
+ has_serial_no = True
self.get_sle_for_serialized_items(row, sl_entries)
if sl_entries:
+ if has_serial_no:
+ sl_entries = self.merge_similar_item_serial_nos(sl_entries)
+
sl_entries.reverse()
allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
@@ -339,6 +364,35 @@ class StockReconciliation(StockController):
"posting_time": self.posting_time
})
+ def merge_similar_item_serial_nos(self, sl_entries):
+ # If user has put the same item in multiple row with different serial no
+ new_sl_entries = []
+ merge_similar_entries = {}
+
+ for d in sl_entries:
+ if not d.serial_no or d.actual_qty < 0:
+ new_sl_entries.append(d)
+ continue
+
+ key = (d.item_code, d.warehouse)
+ if key not in merge_similar_entries:
+ merge_similar_entries[key] = d
+ elif d.serial_no:
+ data = merge_similar_entries[key]
+ data.actual_qty += d.actual_qty
+ data.qty_after_transaction += d.qty_after_transaction
+
+ data.valuation_rate = (data.valuation_rate + d.valuation_rate) / data.actual_qty
+ data.serial_no += '\n' + d.serial_no
+
+ if data.incoming_rate:
+ data.incoming_rate = (data.incoming_rate + d.incoming_rate) / data.actual_qty
+
+ for key, value in merge_similar_entries.items():
+ new_sl_entries.append(value)
+
+ return new_sl_entries
+
def get_gl_entries(self, warehouse_account=None):
if not self.cost_center:
msgprint(_("Please enter Cost Center"), raise_exception=1)
@@ -456,7 +510,7 @@ def get_qty_rate_for_serial_nos(item_code, warehouse, posting_date, posting_time
}
serial_nos_list = [serial_no.get("name")
- for serial_no in get_available_serial_nos(item_code, warehouse)]
+ for serial_no in get_available_serial_nos(args)]
qty = len(serial_nos_list)
serial_nos = '\n'.join(serial_nos_list)
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 5bdbca23d9..db7f6ad1b9 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -19,10 +19,26 @@ def execute(filters=None):
if opening_row:
data.append(opening_row)
+ actual_qty = stock_value = 0
+
for sle in sl_entries:
item_detail = item_details[sle.item_code]
sle.update(item_detail)
+
+ if filters.get("batch_no"):
+ actual_qty += sle.actual_qty
+ stock_value += sle.stock_value_difference
+
+ if sle.voucher_type == 'Stock Reconciliation':
+ actual_qty = sle.qty_after_transaction
+ stock_value = sle.stock_value
+
+ sle.update({
+ "qty_after_transaction": actual_qty,
+ "stock_value": stock_value
+ })
+
data.append(sle)
if include_uom:
@@ -67,7 +83,7 @@ def get_stock_ledger_entries(filters, items):
return frappe.db.sql("""select concat_ws(" ", posting_date, posting_time) as date,
item_code, warehouse, actual_qty, qty_after_transaction, incoming_rate, valuation_rate,
- stock_value, voucher_type, voucher_no, batch_no, serial_no, company, project
+ stock_value, voucher_type, voucher_no, batch_no, serial_no, company, project, stock_value_difference
from `tabStock Ledger Entry` sle
where company = %(company)s and
posting_date between %(from_date)s and %(to_date)s
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 2ac0bae6da..2c6c95393b 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -271,6 +271,7 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto
'fieldtype': 'Currency' if d.get("convertible") == 'rate' else 'Float'
})
+ update_dict_values = []
for row_idx, row in enumerate(result):
data = row.items() if is_dict_obj else enumerate(row)
for key, value in data:
@@ -286,11 +287,17 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto
row.insert(key+1, new_value)
else:
new_key = "{0}_{1}".format(key, frappe.scrub(include_uom))
- row[new_key] = new_value
+ update_dict_values.append([row, new_key, new_value])
-def get_available_serial_nos(item_code, warehouse):
- return frappe.get_all("Serial No", filters = {'item_code': item_code,
- 'warehouse': warehouse, 'delivery_document_no': ''}) or []
+ for data in update_dict_values:
+ row, key, value = data
+ row[key] = value
+
+def get_available_serial_nos(args):
+ return frappe.db.sql(""" SELECT name from `tabSerial No`
+ WHERE item_code = %(item_code)s and warehouse = %(warehouse)s
+ and timestamp(purchase_date, purchase_time) <= timestamp(%(posting_date)s, %(posting_time)s)
+ """, args, as_dict=1)
def add_additional_uom_columns(columns, result, include_uom, conversion_factors):
if not include_uom or not conversion_factors:
diff --git a/erpnext/support/doctype/issue_priority/issue_priority.py b/erpnext/support/doctype/issue_priority/issue_priority.py
index cecaaaab29..7c8925ebc3 100644
--- a/erpnext/support/doctype/issue_priority/issue_priority.py
+++ b/erpnext/support/doctype/issue_priority/issue_priority.py
@@ -8,7 +8,4 @@ from frappe import _
from frappe.model.document import Document
class IssuePriority(Document):
-
- def validate(self):
- if frappe.db.exists("Issue Priority", {"name": self.name}):
- frappe.throw(_("Issue Priority Already Exists"))
+ pass
\ No newline at end of file
diff --git a/erpnext/tests/test_woocommerce.py b/erpnext/tests/test_woocommerce.py
index fc850d57d7..ce0f47d685 100644
--- a/erpnext/tests/test_woocommerce.py
+++ b/erpnext/tests/test_woocommerce.py
@@ -18,6 +18,7 @@ class TestWoocommerce(unittest.TestCase):
woo_settings.api_consumer_key = "ck_fd43ff5756a6abafd95fadb6677100ce95a758a1"
woo_settings.api_consumer_secret = "cs_94360a1ad7bef7fa420a40cf284f7b3e0788454e"
woo_settings.enable_sync = 1
+ woo_settings.company = "Woocommerce"
woo_settings.tax_account = "Sales Expenses - W"
woo_settings.f_n_f_account = "Expenses - W"
woo_settings.creation_user = "Administrator"
|