Merge pull request #19501 from Mangesh-Khairnar/multiple-pos-profile
feat: multiple company pos profile
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 d098d84..43acded 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 @@
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 @@
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 32485a3..62a8f05 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -174,6 +174,8 @@
# 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 af51fc5..522ed4f 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -24,6 +24,11 @@
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 @@
"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 3222aeb..2473d71 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py
@@ -15,8 +15,8 @@
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 11d847d..221e3a7 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -570,7 +570,7 @@
},
{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 ce8aba7..54464e7 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 @@
})
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 1e0b1bc..adf47ed 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -554,7 +554,7 @@
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 a85eccd..acfc660 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 89aaffb..bf7e833 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -61,6 +61,7 @@
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 @@
self.update_outstanding_amounts()
self.update_advance_paid()
self.update_expense_claim()
+ self.set_status()
def on_cancel(self):
@@ -79,6 +81,7 @@
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 @@
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 4665d75..d85344e 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -90,7 +90,8 @@
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.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index f4b656d..e4e2c7b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -331,15 +331,15 @@
})
},
- asset: function(frm, cdt, cdn) {
+ item_code: function(frm, cdt, cdn) {
var row = locals[cdt][cdn];
- if(row.asset) {
+ if(row.item_code) {
frappe.call({
method: "erpnext.assets.doctype.asset_category.asset_category.get_asset_category_account",
args: {
- "asset": row.asset,
+ "item": row.item_code,
"fieldname": "fixed_asset_account",
- "account": row.expense_account
+ "company": frm.doc.company
},
callback: function(r, rt) {
frappe.model.set_value(cdt, cdn, "expense_account", r.message);
@@ -430,19 +430,7 @@
cur_frm.set_query("expense_account", "items", function(doc) {
return {
query: "erpnext.controllers.queries.get_expense_account",
- filters: {'company': doc.company}
- }
-});
-
-cur_frm.set_query("asset", "items", function(doc, cdt, cdn) {
- var d = locals[cdt][cdn];
- return {
- filters: {
- 'item_code': d.item_code,
- 'docstatus': 1,
- 'company': doc.company,
- 'status': 'Submitted'
- }
+ filters: {'company': doc.company }
}
});
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 4ea9b1c..5c53d26 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.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"
@@ -97,7 +98,6 @@
self.set_against_expense_account()
self.validate_write_off_account()
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
- self.validate_fixed_asset()
self.create_remarks()
self.set_status()
self.validate_purchase_receipt_if_update_stock()
@@ -225,6 +225,8 @@
# 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,12 +237,8 @@
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():
- if not item.asset:
- frappe.throw(_("Row {0}: asset is required for item {1}")
- .format(item.idx, item.item_code))
-
- item.expense_account = get_asset_category_account(item.asset, 'fixed_asset_account',
+ elif item.is_fixed_asset and not is_cwip_accounting_enabled(self.company, asset_category):
+ item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
company = self.company)
elif item.is_fixed_asset and item.pr_detail:
item.expense_account = asset_received_but_not_billed
@@ -359,7 +357,7 @@
return
if not gl_entries:
gl_entries = self.get_gl_entries()
-
+
if gl_entries:
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
@@ -391,7 +389,8 @@
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 +403,15 @@
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 +444,8 @@
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 +455,8 @@
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 +475,16 @@
)
# 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,23 +499,54 @@
"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)
+
+ if not item.is_fixed_asset:
+ amount = flt(item.base_net_amount, item.precision("base_net_amount"))
+ else:
+ amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
- gl_entries.append(
- self.get_gl_dict({
+ gl_entries.append(self.get_gl_dict({
"account": expense_account,
"against": self.supplier,
- "debit": flt(item.base_net_amount, item.precision("base_net_amount")),
- "debit_in_account_currency": (flt(item.base_net_amount,
- item.precision("base_net_amount")) if account_currency==self.company_currency
- else flt(item.net_amount, item.precision("net_amount"))),
+ "debit": amount,
"cost_center": item.cost_center,
"project": item.project
- }, account_currency, item=item)
- )
+ }, account_currency, item=item))
+
+ # If asset is bought through this document and not linked to PR
+ if self.update_stock and item.landed_cost_voucher_amount:
+ expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
+ # Amount added through landed-cost-voucher
+ gl_entries.append(self.get_gl_dict({
+ "account": expenses_included_in_asset_valuation,
+ "against": 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))
+
+ gl_entries.append(self.get_gl_dict({
+ "account": expense_account,
+ "against": expenses_included_in_asset_valuation,
+ "cost_center": item.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+ "debit": flt(item.landed_cost_voucher_amount),
+ "project": item.project
+ }, item=item))
+
+ # update gross amount of asset bought through this document
+ assets = frappe.db.get_all('Asset',
+ filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
+ )
+ for asset in assets:
+ frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
+ frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
if self.auto_accounting_for_stock and self.is_opening == "No" and \
item.item_code in stock_items and item.item_tax_amount:
@@ -527,27 +571,27 @@
item.precision("item_tax_amount"))
def get_asset_gl_entry(self, gl_entries):
+ arbnb_account = self.get_company_default("asset_received_but_not_billed")
+ eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
+
for item in self.get("items"):
if item.is_fixed_asset:
- 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)
base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
- if (not item.expense_account or frappe.db.get_value('Account',
- item.expense_account, 'account_type') not in ['Asset Received But Not Billed', 'Fixed Asset']):
- arbnb_account = self.get_company_default("asset_received_but_not_billed")
+ item_exp_acc_type = frappe.db.get_value('Account', item.expense_account, 'account_type')
+ if (not item.expense_account or item_exp_acc_type not in ['Asset Received But Not Billed', 'Fixed Asset']):
item.expense_account = arbnb_account
if not self.update_stock:
- asset_rbnb_currency = get_account_currency(item.expense_account)
+ arbnb_currency = get_account_currency(item.expense_account)
gl_entries.append(self.get_gl_dict({
"account": item.expense_account,
"against": self.supplier,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"debit": base_asset_amount,
"debit_in_account_currency": (base_asset_amount
- if asset_rbnb_currency == self.company_currency else asset_amount),
+ if arbnb_currency == self.company_currency else asset_amount),
"cost_center": item.cost_center
}, item=item))
@@ -564,8 +608,7 @@
item.item_tax_amount / self.conversion_rate)
}, item=item))
else:
- cwip_account = get_asset_account("capital_work_in_progress_account",
- item.asset, company = self.company)
+ cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company)
cwip_account_currency = get_account_currency(cwip_account)
gl_entries.append(self.get_gl_dict({
@@ -590,6 +633,36 @@
if asset_eiiav_currency == self.company_currency else
item.item_tax_amount / self.conversion_rate)
}, item=item))
+
+ # When update stock is checked
+ # Assets are bought through this document then it will be linked to this document
+ if self.update_stock:
+ if flt(item.landed_cost_voucher_amount):
+ gl_entries.append(self.get_gl_dict({
+ "account": eiiav_account,
+ "against": cwip_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))
+
+ gl_entries.append(self.get_gl_dict({
+ "account": cwip_account,
+ "against": eiiav_account,
+ "cost_center": item.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+ "debit": flt(item.landed_cost_voucher_amount),
+ "project": item.project
+ }, item=item))
+
+ # update gross amount of assets bought through this document
+ assets = frappe.db.get_all('Asset',
+ filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
+ )
+ for asset in assets:
+ frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
+ frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
return gl_entries
@@ -641,14 +714,14 @@
if account_currency==self.company_currency \
else tax.tax_amount_after_discount_amount,
"cost_center": tax.cost_center
- }, account_currency)
+ }, account_currency, item=tax)
)
# accumulate valuation tax
if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
if self.auto_accounting_for_stock and not tax.cost_center:
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
- valuation_tax.setdefault(tax.cost_center, 0)
- valuation_tax[tax.cost_center] += \
+ valuation_tax.setdefault(tax.name, 0)
+ valuation_tax[tax.name] += \
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax:
@@ -658,36 +731,38 @@
total_valuation_amount = sum(valuation_tax.values())
amount_including_divisional_loss = self.negative_expense_to_be_booked
i = 1
- for cost_center, amount in iteritems(valuation_tax):
- if i == len(valuation_tax):
- applicable_amount = amount_including_divisional_loss
- else:
- applicable_amount = self.negative_expense_to_be_booked * (amount / total_valuation_amount)
- amount_including_divisional_loss -= applicable_amount
+ for tax in self.get("taxes"):
+ if valuation_tax.get(tax.name):
+ if i == len(valuation_tax):
+ applicable_amount = amount_including_divisional_loss
+ else:
+ applicable_amount = self.negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount)
+ amount_including_divisional_loss -= applicable_amount
- gl_entries.append(
- self.get_gl_dict({
- "account": self.expenses_included_in_valuation,
- "cost_center": cost_center,
- "against": self.supplier,
- "credit": applicable_amount,
- "remarks": self.remarks or "Accounting Entry for Stock"
- })
- )
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": tax.account_head,
+ "cost_center": tax.cost_center,
+ "against": self.supplier,
+ "credit": applicable_amount,
+ "remarks": self.remarks or _("Accounting Entry for Stock"),
+ }, item=tax)
+ )
- i += 1
+ i += 1
if self.auto_accounting_for_stock and self.update_stock and valuation_tax:
- for cost_center, amount in iteritems(valuation_tax):
- gl_entries.append(
- self.get_gl_dict({
- "account": self.expenses_included_in_valuation,
- "cost_center": cost_center,
- "against": self.supplier,
- "credit": amount,
- "remarks": self.remarks or "Accounting Entry for Stock"
- })
- )
+ for tax in self.get("taxes"):
+ if valuation_tax.get(tax.name):
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": tax.account_head,
+ "cost_center": tax.cost_center,
+ "against": self.supplier,
+ "credit": valuation_tax[tax.name],
+ "remarks": self.remarks or "Accounting Entry for Stock"
+ }, item=tax)
+ )
def make_payment_gl_entries(self, gl_entries):
# Make Cash GL Entries
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js
index 4e76a8d..800ed92 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 @@
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/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index b2ad4f4..85b1166 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -204,19 +204,40 @@
pi.insert()
pi.submit()
- self.check_gle_for_pi(pi.name)
+ self.check_gle_for_pi_against_pr(pi.name)
def check_gle_for_pi(self, pi):
- gl_entries = frappe.db.sql("""select account, debit, credit
+ gl_entries = frappe.db.sql("""select account, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
- order by account asc""", pi, as_dict=1)
+ group by account""", pi, as_dict=1)
+
self.assertTrue(gl_entries)
expected_values = dict((d[0], d) for d in [
["Creditors - TCP1", 0, 720],
["Stock Received But Not Billed - TCP1", 500.0, 0],
- ["_Test Account Shipping Charges - TCP1", 100.0, 0],
+ ["_Test Account Shipping Charges - TCP1", 100.0, 0.0],
+ ["_Test Account VAT - TCP1", 120.0, 0]
+ ])
+
+ for i, gle in enumerate(gl_entries):
+ self.assertEqual(expected_values[gle.account][0], gle.account)
+ self.assertEqual(expected_values[gle.account][1], gle.debit)
+ self.assertEqual(expected_values[gle.account][2], gle.credit)
+
+ def check_gle_for_pi_against_pr(self, pi):
+ gl_entries = frappe.db.sql("""select account, sum(debit) as debit, sum(credit) as credit
+ from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
+ group by account""", pi, as_dict=1)
+
+ self.assertTrue(gl_entries)
+
+ expected_values = dict((d[0], d) for d in [
+ ["Creditors - TCP1", 0, 720],
+ ["Stock Received But Not Billed - TCP1", 750.0, 0],
+ ["_Test Account Shipping Charges - TCP1", 100.0, 100.0],
["_Test Account VAT - TCP1", 120.0, 0],
+ ["_Test Account Customs Duty - TCP1", 0, 150]
])
for i, gle in enumerate(gl_entries):
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 3a19bb1..dc3a1be 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -71,8 +71,8 @@
"expense_account",
"col_break5",
"is_fixed_asset",
- "asset",
"asset_location",
+ "asset_category",
"deferred_expense_section",
"deferred_expense_account",
"service_stop_date",
@@ -116,6 +116,7 @@
"fieldtype": "Column Break"
},
{
+ "fetch_from": "item_code.item_name",
"fieldname": "item_name",
"fieldtype": "Data",
"in_global_search": 1,
@@ -191,6 +192,7 @@
"fieldtype": "Column Break"
},
{
+ "fetch_from": "item_code.stock_uom",
"fieldname": "uom",
"fieldtype": "Link",
"label": "UOM",
@@ -414,6 +416,7 @@
"print_hide": 1
},
{
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "batch_no",
"fieldtype": "Link",
"label": "Batch No",
@@ -425,12 +428,14 @@
"fieldtype": "Column Break"
},
{
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "serial_no",
"fieldtype": "Text",
"label": "Serial No",
"no_copy": 1
},
{
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "rejected_serial_no",
"fieldtype": "Text",
"label": "Rejected Serial No",
@@ -615,6 +620,7 @@
},
{
"default": "0",
+ "fetch_from": "item_code.is_fixed_asset",
"fieldname": "is_fixed_asset",
"fieldtype": "Check",
"hidden": 1,
@@ -625,14 +631,6 @@
},
{
"depends_on": "is_fixed_asset",
- "fieldname": "asset",
- "fieldtype": "Link",
- "label": "Asset",
- "no_copy": 1,
- "options": "Asset"
- },
- {
- "depends_on": "is_fixed_asset",
"fieldname": "asset_location",
"fieldtype": "Link",
"label": "Asset Location",
@@ -676,7 +674,7 @@
"fieldname": "pr_detail",
"fieldtype": "Data",
"hidden": 1,
- "label": "PR Detail",
+ "label": "Purchase Receipt Detail",
"no_copy": 1,
"oldfieldname": "pr_detail",
"oldfieldtype": "Data",
@@ -754,11 +752,21 @@
"fieldtype": "Data",
"label": "Manufacturer Part Number",
"read_only": 1
+ },
+ {
+ "depends_on": "is_fixed_asset",
+ "fetch_from": "item_code.asset_category",
+ "fieldname": "asset_category",
+ "fieldtype": "Data",
+ "in_preview": 1,
+ "label": "Asset Category",
+ "options": "Asset Category",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
- "modified": "2019-09-17 22:32:05.984240",
+ "modified": "2019-11-03 13:43:23.782877",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py
index ed45b2c..ba23784 100755
--- a/erpnext/accounts/doctype/sales_invoice/pos.py
+++ b/erpnext/accounts/doctype/sales_invoice/pos.py
@@ -550,11 +550,15 @@
def make_email_queue(email_queue):
name_list = []
+
for key, data in iteritems(email_queue):
name = frappe.db.get_value('Sales Invoice', {'offline_pos_name': key}, 'name')
+ if not name: continue
+
data = json.loads(data)
sender = frappe.session.user
print_format = "POS Invoice" if not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')) else None
+
attachments = [frappe.attach_print('Sales Invoice', name, print_format=print_format)]
make(subject=data.get('subject'), content=data.get('content'), recipients=data.get('recipients'),
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 5766c9a..3d96d48 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -135,7 +135,17 @@
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
validate_loyalty_points(self, self.loyalty_points)
+
+ def validate_fixed_asset(self):
+ for d in self.get("items"):
+ if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
+ asset = frappe.get_doc("Asset", d.asset)
+ if self.doctype == "Sales Invoice" and self.docstatus == 1:
+ if self.update_stock:
+ frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
+ elif asset.status in ("Scrapped", "Cancelled", "Sold"):
+ frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
def before_save(self):
set_account_for_mode_of_payment(self)
@@ -991,10 +1001,8 @@
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 +1048,9 @@
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 +1239,8 @@
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 364ca6f..1cad4df 100644
--- a/erpnext/accounts/doctype/share_transfer/share_transfer.js
+++ b/erpnext/accounts/doctype/share_transfer/share_transfer.js
@@ -21,6 +21,8 @@
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 @@
};
});
}
+ },
+
+ 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 24c4569..f17bf04 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 43d9ad6..38f283c 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -163,19 +163,32 @@
.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}<br></br>{1}<br></br>
+ <div style="text-align:right;">
+ <button class="btn btn-primary" onclick="frappe.new_doc('Journal Entry')">{2}</button>
+ </div>""".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: <b>{0}</b> is capital Work in progress and can not be updated by Journal Entry").format(entry.account))
+ frappe.throw(
+ _("Account: <b>{0}</b> 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 6eafa0d..efc76f9 100644
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
@@ -139,15 +139,11 @@
}
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 @@
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 @@
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 228be18..9b4dda2 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -79,13 +79,20 @@
"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 bcbd427..14906f2 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -188,7 +188,11 @@
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 b90a7a9..8955830 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 @@
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 4bc29da..8c11514 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 10e977a..faeee0f 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -76,8 +76,7 @@
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 @@
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 @@
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 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 bb9045c..3e51933 100644
--- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
+++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py
@@ -51,27 +51,25 @@
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 c5cad73..f0889bf 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -41,6 +41,21 @@
});
},
+ setup: function(frm) {
+ frm.set_query("purchase_receipt", (doc) => {
+ return {
+ query: "erpnext.controllers.queries.get_purchase_receipts",
+ filters: { item_code: doc.item_code }
+ }
+ });
+ frm.set_query("purchase_invoice", (doc) => {
+ return {
+ query: "erpnext.controllers.queries.get_purchase_invoices",
+ filters: { item_code: doc.item_code }
+ }
+ });
+ },
+
refresh: function(frm) {
frappe.ui.form.trigger("Asset", "is_existing_asset");
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
@@ -78,11 +93,6 @@
});
}
- if (frm.doc.status=='Submitted' && !frm.doc.is_existing_asset && !frm.doc.purchase_invoice) {
- frm.add_custom_button(__("Purchase Invoice"), function() {
- frm.trigger("make_purchase_invoice");
- }, __('Create'));
- }
if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) {
frm.add_custom_button(__("Asset Maintenance"), function() {
frm.trigger("create_asset_maintenance");
@@ -104,11 +114,36 @@
frm.trigger("setup_chart");
}
+ frm.trigger("toggle_reference_doc");
+
if (frm.doc.docstatus == 0) {
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
}
},
+ toggle_reference_doc: function(frm) {
+ if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) {
+ frm.set_df_property('purchase_invoice', 'read_only', 1);
+ frm.set_df_property('purchase_receipt', 'read_only', 1);
+ }
+ else if (frm.doc.purchase_receipt) {
+ // if purchase receipt link is set then set PI disabled
+ frm.toggle_reqd('purchase_invoice', 0);
+ frm.set_df_property('purchase_invoice', 'read_only', 1);
+ }
+ else if (frm.doc.purchase_invoice) {
+ // if purchase invoice link is set then set PR disabled
+ frm.toggle_reqd('purchase_receipt', 0);
+ frm.set_df_property('purchase_receipt', 'read_only', 1);
+ }
+ else {
+ frm.toggle_reqd('purchase_receipt', 1);
+ frm.set_df_property('purchase_receipt', 'read_only', 0);
+ frm.toggle_reqd('purchase_invoice', 1);
+ frm.set_df_property('purchase_invoice', 'read_only', 0);
+ }
+ },
+
make_journal_entry: function(frm) {
frappe.call({
method: "erpnext.assets.doctype.asset.asset.make_journal_entry",
@@ -176,21 +211,25 @@
item_code: function(frm) {
if(frm.doc.item_code) {
- frappe.call({
- method: "erpnext.assets.doctype.asset.asset.get_item_details",
- args: {
- item_code: frm.doc.item_code,
- asset_category: frm.doc.asset_category
- },
- callback: function(r, rt) {
- if(r.message) {
- frm.set_value('finance_books', r.message);
- }
- }
- })
+ frm.trigger('set_finance_book');
}
},
+ set_finance_book: function(frm) {
+ frappe.call({
+ method: "erpnext.assets.doctype.asset.asset.get_item_details",
+ args: {
+ item_code: frm.doc.item_code,
+ asset_category: frm.doc.asset_category
+ },
+ callback: function(r, rt) {
+ if(r.message) {
+ frm.set_value('finance_books', r.message);
+ }
+ }
+ })
+ },
+
available_for_use_date: function(frm) {
$.each(frm.doc.finance_books || [], function(i, d) {
if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date;
@@ -203,33 +242,18 @@
},
opening_accumulated_depreciation: function(frm) {
- erpnext.asset.set_accululated_depreciation(frm);
+ erpnext.asset.set_accumulated_depreciation(frm);
},
make_schedules_editable: function(frm) {
- var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
- ? true : false;
+ if (frm.doc.finance_books) {
+ var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
+ ? true : false;
- frm.toggle_enable("schedules", is_editable);
- frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
- frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable);
- },
-
- make_purchase_invoice: function(frm) {
- frappe.call({
- args: {
- "asset": frm.doc.name,
- "item_code": frm.doc.item_code,
- "gross_purchase_amount": frm.doc.gross_purchase_amount,
- "company": frm.doc.company,
- "posting_date": frm.doc.purchase_date
- },
- method: "erpnext.assets.doctype.asset.asset.make_purchase_invoice",
- callback: function(r) {
- var doclist = frappe.model.sync(r.message);
- frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
- }
- })
+ frm.toggle_enable("schedules", is_editable);
+ frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
+ frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable);
+ }
},
make_sales_invoice: function(frm) {
@@ -282,17 +306,6 @@
},
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);
},
@@ -302,6 +315,65 @@
})
},
+ purchase_receipt: function(frm) {
+ frm.trigger('toggle_reference_doc');
+
+ if (frm.doc.purchase_receipt) {
+ if (frm.doc.item_code) {
+ frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => {
+ frm.set_value('company', pr_doc.company);
+ frm.set_value('purchase_date', pr_doc.posting_date);
+ const item = pr_doc.items.find(item => item.item_code === frm.doc.item_code);
+ if (!item) {
+ frm.set_value('purchase_receipt', '');
+ frappe.msgprint({
+ title: __('Invalid Purchase Receipt'),
+ message: __("The selected Purchase Receipt doesn't contains selected Asset Item."),
+ indicator: 'red'
+ });
+ }
+ frm.set_value('gross_purchase_amount', item.base_net_rate);
+ frm.set_value('location', item.asset_location);
+ });
+ } else {
+ frm.set_value('purchase_receipt', '');
+ frappe.msgprint({
+ title: __('Not Allowed'),
+ message: __("Please select Item Code first")
+ });
+ }
+ }
+ },
+
+ purchase_invoice: function(frm) {
+ frm.trigger('toggle_reference_doc');
+ if (frm.doc.purchase_invoice) {
+ if (frm.doc.item_code) {
+ frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => {
+ frm.set_value('company', pi_doc.company);
+ frm.set_value('purchase_date', pi_doc.posting_date);
+ const item = pi_doc.items.find(item => item.item_code === frm.doc.item_code);
+ if (!item) {
+ frm.set_value('purchase_invoice', '');
+ frappe.msgprint({
+ title: __('Invalid Purchase Invoice'),
+ message: __("The selected Purchase Invoice doesn't contains selected Asset Item."),
+ indicator: 'red'
+ });
+ }
+ frm.set_value('gross_purchase_amount', item.base_net_rate);
+ frm.set_value('location', item.asset_location);
+ });
+ } else {
+ frm.set_value('purchase_invoice', '');
+ frappe.msgprint({
+ title: __('Not Allowed'),
+ message: __("Please select Item Code first")
+ });
+ }
+ }
+ },
+
set_depreciation_rate: function(frm, row) {
if (row.total_number_of_depreciations && row.frequency_of_depreciation
&& row.expected_value_after_useful_life) {
@@ -371,12 +443,12 @@
},
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 c60ec5e..97165a3 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -1,497 +1,505 @@
{
- "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",
+ "purchase_invoice",
+ "column_break_3",
+ "company",
+ "location",
+ "custodian",
+ "department",
+ "purchase_date",
+ "disposal_date",
+ "journal_entry_for_scrap",
+ "purchase_receipt",
+ "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_amount",
+ "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
+ },
+ {
+ "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"
+ },
+ {
+ "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 6e2bbc1..9415eed 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -6,7 +6,7 @@
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 \
@@ -18,6 +18,7 @@
class Asset(AccountsController):
def validate(self):
self.validate_asset_values()
+ self.validate_asset_and_reference()
self.validate_item()
self.set_missing_values()
self.prepare_depreciation_data()
@@ -29,9 +30,13 @@
def on_submit(self):
self.validate_in_use_date()
self.set_status()
- self.update_stock_movement()
- if not self.booked_fixed_asset and not is_cwip_accounting_disabled():
+ self.make_asset_movement()
+ if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.company,
+ self.asset_category):
self.make_gl_entries()
+
+ def before_cancel(self):
+ self.cancel_auto_gen_movement()
def on_cancel(self):
self.validate_cancellation()
@@ -39,6 +44,18 @@
self.set_status()
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
self.db_set('booked_fixed_asset', 0)
+
+ def validate_asset_and_reference(self):
+ if self.purchase_invoice or self.purchase_receipt:
+ reference_doc = 'Purchase Invoice' if self.purchase_invoice else 'Purchase Receipt'
+ reference_name = self.purchase_invoice or self.purchase_receipt
+ reference_doc = frappe.get_doc(reference_doc, reference_name)
+ if reference_doc.get('company') != self.company:
+ frappe.throw(_("Company of asset {0} and purchase document {1} doesn't matches.").format(self.name, reference_doc.get('name')))
+
+
+ if self.is_existing_asset and self.purchase_invoice:
+ frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
def prepare_depreciation_data(self):
if self.calculate_depreciation:
@@ -76,10 +93,13 @@
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))
@@ -105,6 +125,36 @@
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
frappe.throw(_("Available-for-use Date should be after purchase date"))
+ def cancel_auto_gen_movement(self):
+ reference_docname = self.purchase_invoice or self.purchase_receipt
+ movement = frappe.db.get_all('Asset Movement', filters={ 'reference_name': reference_docname, 'docstatus': 1 })
+ if len(movement) > 1:
+ frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \
+ cancelled manually to cancel this asset.'))
+ movement = frappe.get_doc('Asset Movement', movement[0].get('name'))
+ movement.flags.ignore_validate = True
+ movement.cancel()
+
+ def make_asset_movement(self):
+ reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice'
+ reference_docname = self.purchase_receipt or self.purchase_invoice
+ assets = [{
+ 'asset': self.name,
+ 'asset_name': self.asset_name,
+ 'target_location': self.location,
+ 'to_employee': self.custodian
+ }]
+ asset_movement = frappe.get_doc({
+ 'doctype': 'Asset Movement',
+ 'assets': assets,
+ 'purpose': 'Receipt',
+ 'company': self.company,
+ 'transaction_date': getdate(nowdate()),
+ 'reference_doctype': reference_doctype,
+ 'reference_name': reference_docname
+ }).insert()
+ asset_movement.submit()
+
def set_depreciation_rate(self):
for d in self.get("finance_books"):
d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
@@ -145,19 +195,31 @@
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 +233,50 @@
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
@@ -345,22 +444,13 @@
if d.finance_book == self.default_finance_book:
return cint(d.idx) - 1
- def update_stock_movement(self):
- asset_movement = frappe.db.get_value('Asset Movement',
- {'asset': self.name, 'reference_name': self.purchase_receipt, 'docstatus': 0}, 'name')
-
- if asset_movement:
- doc = frappe.get_doc('Asset Movement', asset_movement)
- doc.naming_series = 'ACC-ASM-.YYYY.-'
- doc.submit()
-
def make_gl_entries(self):
gl_entries = []
- if ((self.purchase_receipt or (self.purchase_invoice and
- frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')))
+ if ((self.purchase_receipt \
+ or (self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')))
and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()):
- fixed_aseet_account = get_asset_category_account(self.name, 'fixed_asset_account',
+ fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
asset_category = self.asset_category, company = self.company)
cwip_account = get_asset_account("capital_work_in_progress_account",
@@ -368,7 +458,7 @@
gl_entries.append(self.get_gl_dict({
"account": cwip_account,
- "against": fixed_aseet_account,
+ "against": fixed_asset_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
"credit": self.purchase_receipt_amount,
@@ -377,7 +467,7 @@
}))
gl_entries.append(self.get_gl_dict({
- "account": fixed_aseet_account,
+ "account": fixed_asset_account,
"against": cwip_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
@@ -424,7 +514,7 @@
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`
@@ -439,25 +529,6 @@
return meta.get_field("naming_series").options
@frappe.whitelist()
-def make_purchase_invoice(asset, item_code, gross_purchase_amount, company, posting_date):
- pi = frappe.new_doc("Purchase Invoice")
- pi.company = company
- pi.currency = frappe.get_cached_value('Company', company, "default_currency")
- pi.set_posting_time = 1
- pi.posting_date = posting_date
- pi.append("items", {
- "item_code": item_code,
- "is_fixed_asset": 1,
- "asset": asset,
- "expense_account": get_asset_category_account(asset, 'fixed_asset_account'),
- "qty": 1,
- "price_list_rate": gross_purchase_amount,
- "rate": gross_purchase_amount
- })
- pi.set_missing_values()
- return pi
-
-@frappe.whitelist()
def make_sales_invoice(asset, item_code, company, serial_no=None):
si = frappe.new_doc("Sales Invoice")
si.company = company
@@ -531,7 +602,7 @@
def get_asset_account(account_name, asset=None, asset_category=None, company=None):
account = None
if asset:
- account = get_asset_category_account(asset, account_name,
+ account = get_asset_category_account(account_name, asset=asset,
asset_category = asset_category, company = company)
if not account:
@@ -574,17 +645,61 @@
return je
-def is_cwip_accounting_disabled():
- return cint(frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting"))
+@frappe.whitelist()
+def make_asset_movement(assets):
+ import json
+ from six import string_types
+
+ if isinstance(assets, string_types):
+ assets = json.loads(assets)
+
+ if len(assets) == 0:
+ frappe.throw(_('Atleast one asset has to be selected.'))
+
+ asset_movement = frappe.new_doc("Asset Movement")
+ asset_movement.quantity = len(assets)
+ prev_reference_docname = ''
+
+ for asset in assets:
+ asset = frappe.get_doc('Asset', asset.get('name'))
+ # get PR/PI linked with asset
+ reference_docname = asset.get('purchase_receipt') if asset.get('purchase_receipt') \
+ else asset.get('purchase_invoice')
+ # checks if all the assets are linked with a single PR/PI
+ if prev_reference_docname == '':
+ prev_reference_docname = reference_docname
+ elif prev_reference_docname != reference_docname:
+ frappe.throw(_('Assets selected should belong to same reference document.'))
+
+ asset_movement.company = asset.get('company')
+ asset_movement.reference_doctype = 'Purchase Receipt' if asset.get('purchase_receipt') else 'Purchase Invoice'
+ asset_movement.reference_name = prev_reference_docname
+ asset_movement.append("assets", {
+ 'asset': asset.get('name'),
+ 'source_location': asset.get('location'),
+ 'from_employee': asset.get('custodian')
+ })
+
+ if asset_movement.get('assets'):
+ return asset_movement.as_dict()
+
+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/asset_list.js b/erpnext/assets/doctype/asset/asset_list.js
index 3b95a17..46cde6e 100644
--- a/erpnext/assets/doctype/asset/asset_list.js
+++ b/erpnext/assets/doctype/asset/asset_list.js
@@ -30,8 +30,23 @@
} else if (doc.status === "Draft") {
return [__("Draft"), "red", "status,=,Draft"];
-
}
-
+ },
+ onload: function(me) {
+ me.page.add_action_item('Make Asset Movement', function() {
+ const assets = me.get_checked_items();
+ frappe.call({
+ method: "erpnext.assets.doctype.asset.asset.make_asset_movement",
+ args:{
+ "assets": assets
+ },
+ callback: function (r) {
+ if (r.message) {
+ var doc = frappe.model.sync(r.message)[0];
+ frappe.set_route("Form", doc.doctype, doc.name);
+ }
+ }
+ });
+ });
},
}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index c09b94f..53fd6d3 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -7,14 +7,13 @@
import unittest
from frappe.utils import cstr, nowdate, getdate, flt, get_last_day, add_days, add_months
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
-from erpnext.assets.doctype.asset.asset import make_sales_invoice, make_purchase_invoice
+from erpnext.assets.doctype.asset.asset import make_sales_invoice
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_invoice
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`")
@@ -40,15 +39,15 @@
})
asset.submit()
- pi = make_purchase_invoice(asset.name, asset.item_code, asset.gross_purchase_amount,
- asset.company, asset.purchase_date)
+ pi = make_invoice(pr.name)
pi.supplier = "_Test Supplier"
pi.insert()
pi.submit()
asset.load_from_db()
self.assertEqual(asset.supplier, "_Test Supplier")
self.assertEqual(asset.purchase_date, getdate(purchase_date))
- self.assertEqual(asset.purchase_invoice, pi.name)
+ # Asset won't have reference to PI when purchased through PR
+ self.assertEqual(asset.purchase_receipt, pr.name)
expected_gle = (
("Asset Received But Not Billed - _TC", 100000.0, 0.0),
@@ -61,520 +60,517 @@
self.assertEqual(gle, expected_gle)
pi.cancel()
-
+ asset.cancel()
asset.load_from_db()
- self.assertEqual(asset.supplier, None)
- self.assertEqual(asset.purchase_invoice, None)
+ pr.load_from_db()
+ pr.cancel()
+ self.assertEqual(asset.docstatus, 2)
self.assertFalse(frappe.db.get_value("GL Entry",
{"voucher_type": "Purchase Invoice", "voucher_no": pi.name}))
- def test_is_fixed_asset_set(self):
- doc = frappe.new_doc('Purchase Invoice')
- doc.supplier = '_Test Supplier'
- doc.append('items', {
- 'item_code': 'Macbook Pro',
- 'qty': 1
- })
+ # 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,
+ # 'asset': asset.name
+ # })
- doc.set_missing_values()
- self.assertEquals(doc.items[0].is_fixed_asset, 1)
+ # doc.set_missing_values()
+ # self.assertEquals(doc.items[0].is_fixed_asset, 1)
- def test_schedule_for_straight_line_method(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ # def test_schedule_for_straight_line_method(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=100000.0, location="Test Location")
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.available_for_use_date = '2030-01-01'
+ # asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save()
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 12,
+ # "depreciation_start_date": "2030-12-31"
+ # })
+ # asset.save()
- self.assertEqual(asset.status, "Draft")
- expected_schedules = [
- ["2030-12-31", 30000.00, 30000.00],
- ["2031-12-31", 30000.00, 60000.00],
- ["2032-12-31", 30000.00, 90000.00]
- ]
+ # self.assertEqual(asset.status, "Draft")
+ # expected_schedules = [
+ # ["2030-12-31", 30000.00, 30000.00],
+ # ["2031-12-31", 30000.00, 60000.00],
+ # ["2032-12-31", 30000.00, 90000.00]
+ # ]
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
+ # schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ # for d in asset.get("schedules")]
- self.assertEqual(schedules, expected_schedules)
+ # self.assertEqual(schedules, expected_schedules)
- def test_schedule_for_straight_line_method_for_existing_asset(self):
- create_asset(is_existing_asset=1)
- asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
- asset.calculate_depreciation = 1
- asset.number_of_depreciations_booked = 1
- asset.opening_accumulated_depreciation = 40000
- asset.available_for_use_date = "2030-06-06"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.insert()
- self.assertEqual(asset.status, "Draft")
- asset.save()
- expected_schedules = [
- ["2030-12-31", 14246.58, 54246.58],
- ["2031-12-31", 25000.00, 79246.58],
- ["2032-06-06", 10753.42, 90000.00]
- ]
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
+ # def test_schedule_for_straight_line_method_for_existing_asset(self):
+ # create_asset(is_existing_asset=1)
+ # asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
+ # asset.calculate_depreciation = 1
+ # asset.number_of_depreciations_booked = 1
+ # asset.opening_accumulated_depreciation = 40000
+ # asset.available_for_use_date = "2030-06-06"
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 12,
+ # "depreciation_start_date": "2030-12-31"
+ # })
+ # asset.insert()
+ # self.assertEqual(asset.status, "Draft")
+ # asset.save()
+ # expected_schedules = [
+ # ["2030-12-31", 14246.58, 54246.58],
+ # ["2031-12-31", 25000.00, 79246.58],
+ # ["2032-06-06", 10753.42, 90000.00]
+ # ]
+ # schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
+ # for d in asset.get("schedules")]
- self.assertEqual(schedules, expected_schedules)
+ # self.assertEqual(schedules, expected_schedules)
- def test_schedule_for_double_declining_method(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ # def test_schedule_for_double_declining_method(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=100000.0, location="Test Location")
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Double Declining Balance",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": '2030-12-31'
- })
- asset.insert()
- self.assertEqual(asset.status, "Draft")
- asset.save()
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.available_for_use_date = '2030-01-01'
+ # asset.purchase_date = '2030-01-01'
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Double Declining Balance",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 12,
+ # "depreciation_start_date": '2030-12-31'
+ # })
+ # asset.insert()
+ # self.assertEqual(asset.status, "Draft")
+ # asset.save()
- expected_schedules = [
- ['2030-12-31', 66667.00, 66667.00],
- ['2031-12-31', 22222.11, 88889.11],
- ['2032-12-31', 1110.89, 90000.0]
- ]
+ # expected_schedules = [
+ # ['2030-12-31', 66667.00, 66667.00],
+ # ['2031-12-31', 22222.11, 88889.11],
+ # ['2032-12-31', 1110.89, 90000.0]
+ # ]
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
+ # schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ # for d in asset.get("schedules")]
- self.assertEqual(schedules, expected_schedules)
+ # self.assertEqual(schedules, expected_schedules)
- def test_schedule_for_double_declining_method_for_existing_asset(self):
- create_asset(is_existing_asset = 1)
- asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
- asset.calculate_depreciation = 1
- asset.is_existing_asset = 1
- asset.number_of_depreciations_booked = 1
- asset.opening_accumulated_depreciation = 50000
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2029-11-30'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Double Declining Balance",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.insert()
- self.assertEqual(asset.status, "Draft")
+ # def test_schedule_for_double_declining_method_for_existing_asset(self):
+ # create_asset(is_existing_asset = 1)
+ # asset = frappe.get_doc("Asset", {"asset_name": "Macbook Pro 1"})
+ # asset.calculate_depreciation = 1
+ # asset.is_existing_asset = 1
+ # asset.number_of_depreciations_booked = 1
+ # asset.opening_accumulated_depreciation = 50000
+ # asset.available_for_use_date = '2030-01-01'
+ # asset.purchase_date = '2029-11-30'
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Double Declining Balance",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 12,
+ # "depreciation_start_date": "2030-12-31"
+ # })
+ # asset.insert()
+ # self.assertEqual(asset.status, "Draft")
- expected_schedules = [
- ["2030-12-31", 33333.50, 83333.50],
- ["2031-12-31", 6666.50, 90000.0]
- ]
+ # expected_schedules = [
+ # ["2030-12-31", 33333.50, 83333.50],
+ # ["2031-12-31", 6666.50, 90000.0]
+ # ]
- schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
- for d in asset.get("schedules")]
+ # schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
+ # for d in asset.get("schedules")]
- self.assertEqual(schedules, expected_schedules)
+ # 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")
+ # def test_schedule_for_prorated_straight_line_method(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=100000.0, location="Test Location")
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.purchase_date = '2030-01-30'
- asset.is_existing_asset = 0
- asset.available_for_use_date = "2030-01-30"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.purchase_date = '2030-01-30'
+ # asset.is_existing_asset = 0
+ # asset.available_for_use_date = "2030-01-30"
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 12,
+ # "depreciation_start_date": "2030-12-31"
+ # })
- asset.insert()
- asset.save()
+ # asset.insert()
+ # asset.save()
- expected_schedules = [
- ["2030-12-31", 27534.25, 27534.25],
- ["2031-12-31", 30000.0, 57534.25],
- ["2032-12-31", 30000.0, 87534.25],
- ["2033-01-30", 2465.75, 90000.0]
- ]
+ # expected_schedules = [
+ # ["2030-12-31", 27534.25, 27534.25],
+ # ["2031-12-31", 30000.0, 57534.25],
+ # ["2032-12-31", 30000.0, 87534.25],
+ # ["2033-01-30", 2465.75, 90000.0]
+ # ]
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
+ # schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ # for d in asset.get("schedules")]
- self.assertEqual(schedules, expected_schedules)
+ # 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")
- def test_depreciation(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.purchase_date = '2020-01-30'
+ # asset.available_for_use_date = "2020-01-30"
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 10,
+ # "depreciation_start_date": "2020-12-31"
+ # })
+ # asset.insert()
+ # asset.submit()
+ # asset.load_from_db()
+ # self.assertEqual(asset.status, "Submitted")
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.purchase_date = '2020-01-30'
- asset.available_for_use_date = "2020-01-30"
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.insert()
- asset.submit()
- asset.load_from_db()
- self.assertEqual(asset.status, "Submitted")
+ # frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-")
+ # post_depreciation_entries(date="2021-01-01")
+ # asset.load_from_db()
- frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-")
- post_depreciation_entries(date="2021-01-01")
- asset.load_from_db()
+ # # check depreciation entry series
+ # self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR")
- # check depreciation entry series
- self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR")
+ # expected_gle = (
+ # ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
+ # ("_Test Depreciations - _TC", 30000.0, 0.0)
+ # )
- expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
- ("_Test Depreciations - _TC", 30000.0, 0.0)
- )
+ # gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+ # where against_voucher_type='Asset' and against_voucher = %s
+ # order by account""", asset.name)
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where against_voucher_type='Asset' and against_voucher = %s
- order by account""", asset.name)
+ # self.assertEqual(gle, expected_gle)
+ # self.assertEqual(asset.get("value_after_depreciation"), 0)
- self.assertEqual(gle, expected_gle)
- self.assertEqual(asset.get("value_after_depreciation"), 0)
+ # def test_depreciation_entry_for_wdv_without_pro_rata(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=8000.0, location="Test Location")
- def test_depreciation_entry_for_wdv_without_pro_rata(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=8000.0, location="Test Location")
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.available_for_use_date = '2030-01-01'
+ # asset.purchase_date = '2030-01-01'
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 1000,
+ # "depreciation_method": "Written Down Value",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 12,
+ # "depreciation_start_date": "2030-12-31"
+ # })
+ # asset.save(ignore_permissions=True)
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-01-01'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 1000,
- "depreciation_method": "Written Down Value",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save(ignore_permissions=True)
+ # self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
- self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+ # expected_schedules = [
+ # ["2030-12-31", 4000.00, 4000.00],
+ # ["2031-12-31", 2000.00, 6000.00],
+ # ["2032-12-31", 1000.00, 7000.0],
+ # ]
- expected_schedules = [
- ["2030-12-31", 4000.00, 4000.00],
- ["2031-12-31", 2000.00, 6000.00],
- ["2032-12-31", 1000.00, 7000.0],
- ]
+ # schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ # for d in asset.get("schedules")]
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
+ # self.assertEqual(schedules, expected_schedules)
- self.assertEqual(schedules, expected_schedules)
+ # def test_pro_rata_depreciation_entry_for_wdv(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=8000.0, location="Test Location")
- def test_pro_rata_depreciation_entry_for_wdv(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=8000.0, location="Test Location")
-
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2030-06-06'
- asset.purchase_date = '2030-01-01'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 1000,
- "depreciation_method": "Written Down Value",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 12,
- "depreciation_start_date": "2030-12-31"
- })
- asset.save(ignore_permissions=True)
-
- self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.available_for_use_date = '2030-06-06'
+ # asset.purchase_date = '2030-01-01'
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 1000,
+ # "depreciation_method": "Written Down Value",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 12,
+ # "depreciation_start_date": "2030-12-31"
+ # })
+ # asset.save(ignore_permissions=True)
- expected_schedules = [
- ["2030-12-31", 2279.45, 2279.45],
- ["2031-12-31", 2860.28, 5139.73],
- ["2032-12-31", 1430.14, 6569.87],
- ["2033-06-06", 430.13, 7000.0],
- ]
+ # self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
- schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
- for d in asset.get("schedules")]
+ # expected_schedules = [
+ # ["2030-12-31", 2279.45, 2279.45],
+ # ["2031-12-31", 2860.28, 5139.73],
+ # ["2032-12-31", 1430.14, 6569.87],
+ # ["2033-06-06", 430.13, 7000.0],
+ # ]
- self.assertEqual(schedules, expected_schedules)
+ # schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+ # for d in asset.get("schedules")]
- def test_depreciation_entry_cancellation(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ # self.assertEqual(schedules, expected_schedules)
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.insert()
- asset.submit()
- post_depreciation_entries(date="2021-01-01")
+ # def test_depreciation_entry_cancellation(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=100000.0, location="Test Location")
- asset.load_from_db()
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.available_for_use_date = '2020-06-06'
+ # asset.purchase_date = '2020-06-06'
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 10,
+ # "depreciation_start_date": "2020-12-31"
+ # })
+ # asset.insert()
+ # asset.submit()
+ # post_depreciation_entries(date="2021-01-01")
- # cancel depreciation entry
- depr_entry = asset.get("schedules")[0].journal_entry
- self.assertTrue(depr_entry)
- frappe.get_doc("Journal Entry", depr_entry).cancel()
+ # asset.load_from_db()
- asset.load_from_db()
- depr_entry = asset.get("schedules")[0].journal_entry
- self.assertFalse(depr_entry)
+ # # cancel depreciation entry
+ # depr_entry = asset.get("schedules")[0].journal_entry
+ # self.assertTrue(depr_entry)
+ # frappe.get_doc("Journal Entry", depr_entry).cancel()
- def test_scrap_asset(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ # asset.load_from_db()
+ # depr_entry = asset.get("schedules")[0].journal_entry
+ # self.assertFalse(depr_entry)
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = nowdate()
- asset.purchase_date = nowdate()
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": nowdate()
- })
- asset.insert()
- asset.submit()
+ # def test_scrap_asset(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=100000.0, location="Test Location")
- post_depreciation_entries(date=add_months(nowdate(), 10))
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.available_for_use_date = nowdate()
+ # asset.purchase_date = nowdate()
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 10,
+ # "depreciation_start_date": nowdate()
+ # })
+ # asset.insert()
+ # asset.submit()
- scrap_asset(asset.name)
+ # post_depreciation_entries(date=add_months(nowdate(), 10))
- asset.load_from_db()
- self.assertEqual(asset.status, "Scrapped")
- self.assertTrue(asset.journal_entry_for_scrap)
+ # scrap_asset(asset.name)
- expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 30000.0, 0.0),
- ("_Test Fixed Asset - _TC", 0.0, 100000.0),
- ("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0)
- )
+ # asset.load_from_db()
+ # self.assertEqual(asset.status, "Scrapped")
+ # self.assertTrue(asset.journal_entry_for_scrap)
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where voucher_type='Journal Entry' and voucher_no = %s
- order by account""", asset.journal_entry_for_scrap)
- self.assertEqual(gle, expected_gle)
+ # expected_gle = (
+ # ("_Test Accumulated Depreciations - _TC", 30000.0, 0.0),
+ # ("_Test Fixed Asset - _TC", 0.0, 100000.0),
+ # ("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0)
+ # )
- restore_asset(asset.name)
+ # gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+ # where voucher_type='Journal Entry' and voucher_no = %s
+ # order by account""", asset.journal_entry_for_scrap)
+ # self.assertEqual(gle, expected_gle)
- asset.load_from_db()
- self.assertFalse(asset.journal_entry_for_scrap)
- self.assertEqual(asset.status, "Partially Depreciated")
+ # restore_asset(asset.name)
- def test_asset_sale(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ # asset.load_from_db()
+ # self.assertFalse(asset.journal_entry_for_scrap)
+ # self.assertEqual(asset.status, "Partially Depreciated")
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-12-31"
- })
- asset.insert()
- asset.submit()
- post_depreciation_entries(date="2021-01-01")
+ # def test_asset_sale(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=100000.0, location="Test Location")
- si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
- si.customer = "_Test Customer"
- si.due_date = nowdate()
- si.get("items")[0].rate = 25000
- si.insert()
- si.submit()
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.available_for_use_date = '2020-06-06'
+ # asset.purchase_date = '2020-06-06'
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 10,
+ # "depreciation_start_date": "2020-12-31"
+ # })
+ # asset.insert()
+ # asset.submit()
+ # post_depreciation_entries(date="2021-01-01")
- self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
+ # si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
+ # si.customer = "_Test Customer"
+ # si.due_date = nowdate()
+ # si.get("items")[0].rate = 25000
+ # si.insert()
+ # si.submit()
- expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 20392.16, 0.0),
- ("_Test Fixed Asset - _TC", 0.0, 100000.0),
- ("_Test Gain/Loss on Asset Disposal - _TC", 54607.84, 0.0),
- ("Debtors - _TC", 25000.0, 0.0)
- )
+ # self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where voucher_type='Sales Invoice' and voucher_no = %s
- order by account""", si.name)
+ # expected_gle = (
+ # ("_Test Accumulated Depreciations - _TC", 20392.16, 0.0),
+ # ("_Test Fixed Asset - _TC", 0.0, 100000.0),
+ # ("_Test Gain/Loss on Asset Disposal - _TC", 54607.84, 0.0),
+ # ("Debtors - _TC", 25000.0, 0.0)
+ # )
- self.assertEqual(gle, expected_gle)
+ # gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+ # where voucher_type='Sales Invoice' and voucher_no = %s
+ # order by account""", si.name)
- si.cancel()
- self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
+ # self.assertEqual(gle, expected_gle)
- def test_asset_expected_value_after_useful_life(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=100000.0, location="Test Location")
+ # si.cancel()
+ # self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
- asset = frappe.get_doc('Asset', asset_name)
- asset.calculate_depreciation = 1
- asset.available_for_use_date = '2020-06-06'
- asset.purchase_date = '2020-06-06'
- asset.append("finance_books", {
- "expected_value_after_useful_life": 10000,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": "2020-06-06"
- })
- asset.insert()
- accumulated_depreciation_after_full_schedule = \
- max([d.accumulated_depreciation_amount for d in asset.get("schedules")])
+ # def test_asset_expected_value_after_useful_life(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=100000.0, location="Test Location")
- asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
- flt(accumulated_depreciation_after_full_schedule))
+ # asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+ # asset = frappe.get_doc('Asset', asset_name)
+ # asset.calculate_depreciation = 1
+ # asset.available_for_use_date = '2020-06-06'
+ # asset.purchase_date = '2020-06-06'
+ # asset.append("finance_books", {
+ # "expected_value_after_useful_life": 10000,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 10,
+ # "depreciation_start_date": "2020-06-06"
+ # })
+ # asset.insert()
+ # accumulated_depreciation_after_full_schedule = \
+ # max([d.accumulated_depreciation_amount for d in asset.get("schedules")])
- self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
+ # asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
+ # flt(accumulated_depreciation_after_full_schedule))
- def test_cwip_accounting(self):
- from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
- make_purchase_invoice as make_purchase_invoice_from_pr)
+ # self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule)
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=1, rate=5000, do_not_submit=True, location="Test Location")
+ # def test_cwip_accounting(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=1, rate=5000, do_not_submit=True, location="Test Location")
- pr.set('taxes', [{
- 'category': 'Total',
- 'add_deduct_tax': 'Add',
- 'charge_type': 'On Net Total',
- 'account_head': '_Test Account Service Tax - _TC',
- 'description': '_Test Account Service Tax',
- 'cost_center': 'Main - _TC',
- 'rate': 5.0
- }, {
- 'category': 'Valuation and Total',
- 'add_deduct_tax': 'Add',
- 'charge_type': 'On Net Total',
- 'account_head': '_Test Account Shipping Charges - _TC',
- 'description': '_Test Account Shipping Charges',
- 'cost_center': 'Main - _TC',
- 'rate': 5.0
- }])
+ # pr.set('taxes', [{
+ # 'category': 'Total',
+ # 'add_deduct_tax': 'Add',
+ # 'charge_type': 'On Net Total',
+ # 'account_head': '_Test Account Service Tax - _TC',
+ # 'description': '_Test Account Service Tax',
+ # 'cost_center': 'Main - _TC',
+ # 'rate': 5.0
+ # }, {
+ # 'category': 'Valuation and Total',
+ # 'add_deduct_tax': 'Add',
+ # 'charge_type': 'On Net Total',
+ # 'account_head': '_Test Account Shipping Charges - _TC',
+ # 'description': '_Test Account Shipping Charges',
+ # 'cost_center': 'Main - _TC',
+ # 'rate': 5.0
+ # }])
- pr.submit()
+ # pr.submit()
- expected_gle = (
- ("Asset Received But Not Billed - _TC", 0.0, 5250.0),
- ("CWIP Account - _TC", 5250.0, 0.0)
- )
+ # expected_gle = (
+ # ("Asset Received But Not Billed - _TC", 0.0, 5250.0),
+ # ("CWIP Account - _TC", 5250.0, 0.0)
+ # )
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where voucher_type='Purchase Receipt' and voucher_no = %s
- order by account""", pr.name)
+ # pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+ # where voucher_type='Purchase Receipt' and voucher_no = %s
+ # order by account""", pr.name)
- self.assertEqual(gle, expected_gle)
+ # self.assertEqual(pr_gle, expected_gle)
- pi = make_purchase_invoice_from_pr(pr.name)
- pi.submit()
+ # pi = make_invoice(pr.name)
+ # pi.submit()
- expected_gle = (
- ("_Test Account Service Tax - _TC", 250.0, 0.0),
- ("_Test Account Shipping Charges - _TC", 250.0, 0.0),
- ("Asset Received But Not Billed - _TC", 5250.0, 0.0),
- ("Creditors - _TC", 0.0, 5500.0),
- ("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
- )
+ # expected_gle = (
+ # ("_Test Account Service Tax - _TC", 250.0, 0.0),
+ # ("_Test Account Shipping Charges - _TC", 250.0, 0.0),
+ # ("Asset Received But Not Billed - _TC", 5250.0, 0.0),
+ # ("Creditors - _TC", 0.0, 5500.0),
+ # ("Expenses Included In Asset Valuation - _TC", 0.0, 250.0),
+ # )
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where voucher_type='Purchase Invoice' and voucher_no = %s
- order by account""", pi.name)
+ # pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+ # where voucher_type='Purchase Invoice' and voucher_no = %s
+ # order by account""", pi.name)
- self.assertEqual(gle, expected_gle)
+ # self.assertEqual(pi_gle, expected_gle)
- asset = frappe.db.get_value('Asset',
- {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
+ # asset = frappe.db.get_value('Asset',
+ # {'purchase_receipt': pr.name, 'docstatus': 0}, 'name')
- asset_doc = frappe.get_doc('Asset', asset)
+ # asset_doc = frappe.get_doc('Asset', asset)
- month_end_date = get_last_day(nowdate())
- asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
- self.assertEqual(asset_doc.gross_purchase_amount, 5250.0)
+ # month_end_date = get_last_day(nowdate())
+ # asset_doc.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
+ # self.assertEqual(asset_doc.gross_purchase_amount, 5250.0)
- asset_doc.append("finance_books", {
- "expected_value_after_useful_life": 200,
- "depreciation_method": "Straight Line",
- "total_number_of_depreciations": 3,
- "frequency_of_depreciation": 10,
- "depreciation_start_date": month_end_date
- })
- asset_doc.submit()
+ # asset_doc.append("finance_books", {
+ # "expected_value_after_useful_life": 200,
+ # "depreciation_method": "Straight Line",
+ # "total_number_of_depreciations": 3,
+ # "frequency_of_depreciation": 10,
+ # "depreciation_start_date": month_end_date
+ # })
+ # asset_doc.submit()
- expected_gle = (
- ("_Test Fixed Asset - _TC", 5250.0, 0.0),
- ("CWIP Account - _TC", 0.0, 5250.0)
- )
+ # expected_gle = (
+ # ("_Test Fixed Asset - _TC", 5250.0, 0.0),
+ # ("CWIP Account - _TC", 0.0, 5250.0)
+ # )
- gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
- where voucher_type='Asset' and voucher_no = %s
- order by account""", asset_doc.name)
+ # gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
+ # where voucher_type='Asset' and voucher_no = %s
+ # order by account""", asset_doc.name)
- self.assertEqual(gle, expected_gle)
- def test_expense_head(self):
- pr = make_purchase_receipt(item_code="Macbook Pro",
- qty=2, rate=200000.0, location="Test Location")
+ # self.assertEqual(gle, expected_gle)
- doc = make_invoice(pr.name)
+ # def test_expense_head(self):
+ # pr = make_purchase_receipt(item_code="Macbook Pro",
+ # qty=2, rate=200000.0, location="Test Location")
- self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account)
+ # doc = make_invoice(pr.name)
+ # 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"):
@@ -596,15 +592,15 @@
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 +612,9 @@
except frappe.DuplicateEntryError:
pass
+ if args.submit:
+ asset.submit()
+
return asset
def create_asset_category():
@@ -623,6 +622,7 @@
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",
@@ -632,6 +632,8 @@
asset_category.insert()
def create_fixed_asset_item():
+ meta = frappe.get_meta('Asset')
+ naming_series = meta.get_field("naming_series").options.splitlines()[0] or 'ACC-ASS-.YYYY.-'
try:
frappe.get_doc({
"doctype": "Item",
@@ -642,7 +644,9 @@
"item_group": "All Item Groups",
"stock_uom": "Nos",
"is_stock_item": 0,
- "is_fixed_asset": 1
+ "is_fixed_asset": 1,
+ "auto_create_assets": 1,
+ "asset_naming_series": naming_series
}).insert()
except frappe.DuplicateEntryError:
pass
@@ -656,19 +660,4 @@
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 882cbe2..7483b41 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 bbdc6ec..14f3922 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.py
+++ b/erpnext/assets/doctype/asset_category/asset_category.py
@@ -10,14 +10,30 @@
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:
+def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
+ if item and frappe.db.get_value("Item", item, "is_fixed_asset"):
+ asset_category = frappe.db.get_value("Item", item, ["asset_category"])
+
+ elif not asset_category or not company:
if account:
if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
account=None
diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
index 735302a..6c2fd67 100644
--- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
@@ -73,8 +73,10 @@
'doctype': 'Location',
'location_name': 'Test Location'
}).insert()
-
+
if not frappe.db.exists("Item", "Photocopier"):
+ meta = frappe.get_meta('Asset')
+ naming_series = meta.get_field("naming_series").options
frappe.get_doc({
"doctype": "Item",
"item_code": "Photocopier",
@@ -83,7 +85,9 @@
"company": "_Test Company",
"is_fixed_asset": 1,
"is_stock_item": 0,
- "asset_category": "Equipment"
+ "asset_category": "Equipment",
+ "auto_create_assets": 1,
+ "asset_naming_series": naming_series
}).insert()
def create_maintenance_team():
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js
index 7ef6461..a71212e 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.js
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.js
@@ -2,27 +2,138 @@
// For license information, please see license.txt
frappe.ui.form.on('Asset Movement', {
- select_serial_no: function(frm) {
- if (frm.doc.select_serial_no) {
- let serial_no = frm.doc.serial_no
- ? frm.doc.serial_no + '\n' + frm.doc.select_serial_no : frm.doc.select_serial_no;
- frm.set_value("serial_no", serial_no);
- frm.set_value("quantity", serial_no.split('\n').length);
- }
- },
-
- serial_no: function(frm) {
- const qty = frm.doc.serial_no ? frm.doc.serial_no.split('\n').length : 0;
- frm.set_value("quantity", qty);
- },
-
- setup: function(frm) {
- frm.set_query("select_serial_no", function() {
+ setup: (frm) => {
+ frm.set_query("to_employee", "assets", (doc) => {
return {
filters: {
- "asset": frm.doc.asset
+ company: doc.company
}
};
+ })
+ frm.set_query("from_employee", "assets", (doc) => {
+ return {
+ filters: {
+ company: doc.company
+ }
+ };
+ })
+ frm.set_query("reference_name", (doc) => {
+ return {
+ filters: {
+ company: doc.company,
+ docstatus: 1
+ }
+ };
+ })
+ frm.set_query("reference_doctype", () => {
+ return {
+ filters: {
+ name: ["in", ["Purchase Receipt", "Purchase Invoice"]]
+ }
+ };
+ })
+ },
+
+ onload: (frm) => {
+ frm.trigger('set_required_fields');
+ },
+
+ purpose: (frm) => {
+ frm.trigger('set_required_fields');
+ },
+
+ set_required_fields: (frm, cdt, cdn) => {
+ let fieldnames_to_be_altered;
+ if (frm.doc.purpose === 'Transfer') {
+ fieldnames_to_be_altered = {
+ target_location: { read_only: 0, reqd: 1 },
+ source_location: { read_only: 1, reqd: 1 },
+ from_employee: { read_only: 1, reqd: 0 },
+ to_employee: { read_only: 1, reqd: 0 }
+ };
+ }
+ else if (frm.doc.purpose === 'Receipt') {
+ fieldnames_to_be_altered = {
+ target_location: { read_only: 0, reqd: 1 },
+ source_location: { read_only: 1, reqd: 0 },
+ from_employee: { read_only: 0, reqd: 1 },
+ to_employee: { read_only: 1, reqd: 0 }
+ };
+ }
+ else if (frm.doc.purpose === 'Issue') {
+ fieldnames_to_be_altered = {
+ target_location: { read_only: 1, reqd: 0 },
+ source_location: { read_only: 1, reqd: 1 },
+ from_employee: { read_only: 1, reqd: 0 },
+ to_employee: { read_only: 0, reqd: 1 }
+ };
+ }
+ Object.keys(fieldnames_to_be_altered).forEach(fieldname => {
+ let property_to_be_altered = fieldnames_to_be_altered[fieldname];
+ Object.keys(property_to_be_altered).forEach(property => {
+ let value = property_to_be_altered[property];
+ frm.set_df_property(fieldname, property, value, cdn, 'assets');
+ });
});
+ frm.refresh_field('assets');
+ },
+
+ reference_name: function(frm) {
+ if (frm.doc.reference_name && frm.doc.reference_doctype) {
+ const reference_doctype = frm.doc.reference_doctype === 'Purchase Invoice' ? 'purchase_invoice' : 'purchase_receipt';
+ // On selection of reference name,
+ // sets query to display assets linked to that reference doc
+ frm.set_query('asset', 'assets', function() {
+ return {
+ filters: {
+ [reference_doctype] : frm.doc.reference_name
+ }
+ };
+ });
+
+ // fetches linked asset & adds to the assets table
+ frappe.db.get_list('Asset', {
+ fields: ['name', 'location', 'custodian'],
+ filters: {
+ [reference_doctype] : frm.doc.reference_name
+ }
+ }).then((docs) => {
+ if (docs.length == 0) {
+ frappe.msgprint(frappe._(`Please select ${frm.doc.reference_doctype} which has assets.`));
+ frm.doc.reference_name = '';
+ frm.refresh_field('reference_name');
+ return;
+ }
+ frm.doc.assets = [];
+ docs.forEach(doc => {
+ frm.add_child('assets', {
+ asset: doc.name,
+ source_location: doc.location,
+ from_employee: doc.custodian
+ });
+ frm.refresh_field('assets');
+ })
+ }).catch((err) => {
+ console.log(err); // eslint-disable-line
+ });
+ } else {
+ // if reference is deleted then remove query
+ frm.set_query('asset', 'assets', () => ({ filters: {} }));
+ }
}
});
+
+frappe.ui.form.on('Asset Movement Item', {
+ asset: function(frm, cdt, cdn) {
+ // on manual entry of an asset auto sets their source location / employee
+ const asset_name = locals[cdt][cdn].asset;
+ if (asset_name){
+ frappe.db.get_doc('Asset', asset_name).then((asset_doc) => {
+ if(asset_doc.location) frappe.model.set_value(cdt, cdn, 'source_location', asset_doc.location);
+ if(asset_doc.custodian) frappe.model.set_value(cdt, cdn, 'from_employee', asset_doc.custodian);
+ }).catch((err) => {
+ console.log(err);
+ });
+ }
+ }
+});
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.json b/erpnext/assets/doctype/asset_movement/asset_movement.json
index 68076e1..19af81d 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.json
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.json
@@ -1,27 +1,20 @@
{
"allow_import": 1,
- "autoname": "naming_series:",
+ "autoname": "format:ACC-ASM-{YYYY}-{#####}",
"creation": "2016-04-25 18:00:23.559973",
"doctype": "DocType",
+ "engine": "InnoDB",
"field_order": [
- "naming_series",
"company",
"purpose",
- "asset",
- "transaction_date",
"column_break_4",
- "quantity",
- "select_serial_no",
- "serial_no",
- "section_break_7",
- "source_location",
- "target_location",
- "column_break_10",
- "from_employee",
- "to_employee",
+ "transaction_date",
"reference",
"reference_doctype",
+ "column_break_9",
"reference_name",
+ "section_break_10",
+ "assets",
"amended_from"
],
"fields": [
@@ -36,7 +29,6 @@
"reqd": 1
},
{
- "default": "Transfer",
"fieldname": "purpose",
"fieldtype": "Select",
"label": "Purpose",
@@ -44,16 +36,6 @@
"reqd": 1
},
{
- "fieldname": "asset",
- "fieldtype": "Link",
- "in_global_search": 1,
- "in_list_view": 1,
- "in_standard_filter": 1,
- "label": "Asset",
- "options": "Asset",
- "reqd": 1
- },
- {
"fieldname": "transaction_date",
"fieldtype": "Datetime",
"in_list_view": 1,
@@ -65,56 +47,6 @@
"fieldtype": "Column Break"
},
{
- "fieldname": "quantity",
- "fieldtype": "Float",
- "label": "Quantity"
- },
- {
- "fieldname": "select_serial_no",
- "fieldtype": "Link",
- "label": "Select Serial No",
- "options": "Serial No"
- },
- {
- "fieldname": "serial_no",
- "fieldtype": "Small Text",
- "label": "Serial No"
- },
- {
- "fieldname": "section_break_7",
- "fieldtype": "Section Break"
- },
- {
- "fieldname": "source_location",
- "fieldtype": "Link",
- "label": "Source Location",
- "options": "Location"
- },
- {
- "fieldname": "target_location",
- "fieldtype": "Link",
- "label": "Target Location",
- "options": "Location"
- },
- {
- "fieldname": "column_break_10",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "from_employee",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "From Employee",
- "options": "Employee"
- },
- {
- "fieldname": "to_employee",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "To Employee",
- "options": "Employee"
- },
- {
"fieldname": "reference",
"fieldtype": "Section Break",
"label": "Reference"
@@ -125,7 +57,7 @@
"label": "Reference DocType",
"no_copy": 1,
"options": "DocType",
- "read_only": 1
+ "reqd": 1
},
{
"fieldname": "reference_name",
@@ -133,7 +65,7 @@
"label": "Reference Name",
"no_copy": 1,
"options": "reference_doctype",
- "read_only": 1
+ "reqd": 1
},
{
"fieldname": "amended_from",
@@ -145,16 +77,23 @@
"read_only": 1
},
{
- "default": "ACC-ASM-.YYYY.-",
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "ACC-ASM-.YYYY.-",
+ "fieldname": "section_break_10",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "assets",
+ "fieldtype": "Table",
+ "label": "Assets",
+ "options": "Asset Movement Item",
"reqd": 1
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
}
],
"is_submittable": 1,
- "modified": "2019-09-16 16:27:53.887634",
+ "modified": "2019-11-13 15:37:48.870147",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Movement",
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index a1d3308..714845d 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -5,101 +5,142 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from frappe.model.document import Document
class AssetMovement(Document):
def validate(self):
self.validate_asset()
self.validate_location()
+ self.validate_employee()
def validate_asset(self):
- status, company = frappe.db.get_value("Asset", self.asset, ["status", "company"])
- if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
- frappe.throw(_("{0} asset cannot be transferred").format(status))
+ for d in self.assets:
+ status, company = frappe.db.get_value("Asset", d.asset, ["status", "company"])
+ if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
+ frappe.throw(_("{0} asset cannot be transferred").format(status))
- if company != self.company:
- frappe.throw(_("Asset {0} does not belong to company {1}").format(self.asset, self.company))
+ if company != self.company:
+ frappe.throw(_("Asset {0} does not belong to company {1}").format(d.asset, self.company))
- if self.serial_no and len(get_serial_nos(self.serial_no)) != self.quantity:
- frappe.throw(_("Number of serial nos and quantity must be the same"))
-
- if not(self.source_location or self.target_location or self.from_employee or self.to_employee):
- frappe.throw(_("Either location or employee must be required"))
-
- if (not self.serial_no and
- frappe.db.get_value('Serial No', {'asset': self.asset}, 'name')):
- frappe.throw(_("Serial no is required for the asset {0}").format(self.asset))
+ if not(d.source_location or d.target_location or d.from_employee or d.to_employee):
+ frappe.throw(_("Either location or employee must be required"))
def validate_location(self):
- if self.purpose in ['Transfer', 'Issue']:
- if not self.serial_no and not (self.from_employee or self.to_employee):
- self.source_location = frappe.db.get_value("Asset", self.asset, "location")
+ for d in self.assets:
+ if self.purpose in ['Transfer', 'Issue']:
+ if not d.source_location:
+ d.source_location = frappe.db.get_value("Asset", d.asset, "location")
- if self.purpose == 'Issue' and not (self.source_location or self.from_employee):
- frappe.throw(_("Source Location is required for the asset {0}").format(self.asset))
+ if not d.source_location:
+ frappe.throw(_("Source Location is required for the Asset {0}").format(d.asset))
- if self.serial_no and self.source_location:
- s_nos = get_serial_nos(self.serial_no)
- serial_nos = frappe.db.sql_list(""" select name from `tabSerial No` where location != '%s'
- and name in (%s)""" %(self.source_location, ','.join(['%s'] * len(s_nos))), tuple(s_nos))
+ if d.source_location:
+ current_location = frappe.db.get_value("Asset", d.asset, "location")
- if serial_nos:
- frappe.throw(_("Serial nos {0} does not belongs to the location {1}").
- format(','.join(serial_nos), self.source_location))
+ if current_location != d.source_location:
+ frappe.throw(_("Asset {0} does not belongs to the location {1}").
+ format(d.asset, d.source_location))
+
+ if self.purpose == 'Issue':
+ if d.target_location:
+ frappe.throw(_("Issuing cannot be done to a location. \
+ Please enter employee who has issued Asset {0}").format(d.asset), title="Incorrect Movement Purpose")
+ if not d.to_employee:
+ frappe.throw(_("Employee is required while issuing Asset {0}").format(d.asset))
+
+ if self.purpose == 'Transfer':
+ if d.to_employee:
+ frappe.throw(_("Transferring cannot be done to an Employee. \
+ Please enter location where Asset {0} has to be transferred").format(
+ d.asset), title="Incorrect Movement Purpose")
+ if not d.target_location:
+ frappe.throw(_("Target Location is required while transferring Asset {0}").format(d.asset))
+ if d.source_location == d.target_location:
+ frappe.throw(_("Source and Target Location cannot be same"))
+
+ if self.purpose == 'Receipt':
+ # only when asset is bought and first entry is made
+ if not d.source_location and not (d.target_location or d.to_employee):
+ frappe.throw(_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset))
+ elif d.source_location:
+ # when asset is received from an employee
+ if d.target_location and not d.from_employee:
+ frappe.throw(_("From employee is required while receiving Asset {0} to a target location").format(d.asset))
+ if d.from_employee and not d.target_location:
+ frappe.throw(_("Target Location is required while receiving Asset {0} from an employee").format(d.asset))
+ if d.to_employee and d.target_location:
+ frappe.throw(_("Asset {0} cannot be received at a location and \
+ given to employee in a single movement").format(d.asset))
- if self.source_location and self.source_location == self.target_location and self.purpose == 'Transfer':
- frappe.throw(_("Source and Target Location cannot be same"))
+ def validate_employee(self):
+ for d in self.assets:
+ if d.from_employee:
+ current_custodian = frappe.db.get_value("Asset", d.asset, "custodian")
- if self.purpose == 'Receipt' and not (self.target_location or self.to_employee):
- frappe.throw(_("Target Location is required for the asset {0}").format(self.asset))
+ if current_custodian != d.from_employee:
+ frappe.throw(_("Asset {0} does not belongs to the custodian {1}").
+ format(d.asset, d.from_employee))
+
+ if d.to_employee and frappe.db.get_value("Employee", d.to_employee, "company") != self.company:
+ frappe.throw(_("Employee {0} does not belongs to the company {1}").
+ format(d.to_employee, self.company))
def on_submit(self):
self.set_latest_location_in_asset()
+
+ def before_cancel(self):
+ self.validate_last_movement()
def on_cancel(self):
self.set_latest_location_in_asset()
+
+ def validate_last_movement(self):
+ for d in self.assets:
+ auto_gen_movement_entry = frappe.db.sql(
+ """
+ SELECT asm.name
+ FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm
+ WHERE
+ asm.docstatus=1 and
+ asm_item.parent=asm.name and
+ asm_item.asset=%s and
+ asm.company=%s and
+ asm_item.source_location is NULL and
+ asm.purpose=%s
+ ORDER BY
+ asm.transaction_date asc
+ """, (d.asset, self.company, 'Receipt'), as_dict=1)
+ if auto_gen_movement_entry[0].get('name') == self.name:
+ frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \
+ auto generated for Asset {1}').format(self.name, d.asset))
def set_latest_location_in_asset(self):
- location, employee = '', ''
+ current_location, current_employee = '', ''
cond = "1=1"
- args = {
- 'asset': self.asset,
- 'company': self.company
- }
+ for d in self.assets:
+ args = {
+ 'asset': d.asset,
+ 'company': self.company
+ }
- if self.serial_no:
- cond = "serial_no like %(txt)s"
- args.update({
- 'txt': "%%%s%%" % self.serial_no
- })
+ # latest entry corresponds to current document's location, employee when transaction date > previous dates
+ # In case of cancellation it corresponds to previous latest document's location, employee
+ latest_movement_entry = frappe.db.sql(
+ """
+ SELECT asm_item.target_location, asm_item.to_employee
+ FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm
+ WHERE
+ asm_item.parent=asm.name and
+ asm_item.asset=%(asset)s and
+ asm.company=%(company)s and
+ asm.docstatus=1 and {0}
+ ORDER BY
+ asm.transaction_date desc limit 1
+ """.format(cond), args)
+ if latest_movement_entry:
+ current_location = latest_movement_entry[0][0]
+ current_employee = latest_movement_entry[0][1]
- latest_movement_entry = frappe.db.sql("""select target_location, to_employee from `tabAsset Movement`
- where asset=%(asset)s and docstatus=1 and company=%(company)s and {0}
- order by transaction_date desc limit 1""".format(cond), args)
-
- if latest_movement_entry:
- location = latest_movement_entry[0][0]
- employee = latest_movement_entry[0][1]
- elif self.purpose in ['Transfer', 'Receipt']:
- movement_entry = frappe.db.sql("""select source_location, from_employee from `tabAsset Movement`
- where asset=%(asset)s and docstatus=2 and company=%(company)s and {0}
- order by transaction_date asc limit 1""".format(cond), args)
- if movement_entry:
- location = movement_entry[0][0]
- employee = movement_entry[0][1]
-
- if not self.serial_no:
- frappe.db.set_value("Asset", self.asset, "location", location)
-
- if not employee and self.purpose in ['Receipt', 'Transfer']:
- employee = self.to_employee
-
- if self.serial_no:
- for d in get_serial_nos(self.serial_no):
- if (location or (self.purpose == 'Issue' and self.source_location)):
- frappe.db.set_value('Serial No', d, 'location', location)
-
- if employee or self.docstatus==2 or self.purpose == 'Issue':
- frappe.db.set_value('Serial No', d, 'employee', employee)
+ frappe.db.set_value('Asset', d.asset, 'location', current_location)
+ frappe.db.set_value('Asset', d.asset, 'custodian', current_employee)
diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
index 4d85337..c3755a3 100644
--- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py
@@ -5,6 +5,7 @@
import frappe
import unittest
+import erpnext
from erpnext.stock.doctype.item.test_item import make_item
from frappe.utils import now, nowdate, get_last_day, add_days
from erpnext.assets.doctype.asset.test_asset import create_asset_data
@@ -16,7 +17,6 @@
def setUp(self):
create_asset_data()
make_location()
- make_serialized_item()
def test_movement(self):
pr = make_purchase_receipt(item_code="Macbook Pro",
@@ -38,68 +38,72 @@
if asset.docstatus == 0:
asset.submit()
+
+ # check asset movement is created
if not frappe.db.exists("Location", "Test Location 2"):
frappe.get_doc({
'doctype': 'Location',
'location_name': 'Test Location 2'
}).insert()
- movement1 = create_asset_movement(asset= asset.name, purpose = 'Transfer',
- company=asset.company, source_location="Test Location", target_location="Test Location 2")
+ movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
+ assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
+ reference_doctype = 'Purchase Receipt', reference_name = pr.name)
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
- movement2 = create_asset_movement(asset= asset.name, purpose = 'Transfer',
- company=asset.company, source_location = "Test Location 2", target_location="Test Location")
+ movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company,
+ assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}],
+ reference_doctype = 'Purchase Receipt', reference_name = pr.name)
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
movement1.cancel()
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
- movement2.cancel()
- self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
-
- def test_movement_for_serialized_asset(self):
- asset_item = "Test Serialized Asset Item"
- pr = make_purchase_receipt(item_code=asset_item, rate = 1000, qty=3, location = "Mumbai")
- asset_name = frappe.db.get_value('Asset', {'purchase_receipt': pr.name}, 'name')
-
+ employee = make_employee("testassetmovemp@example.com", company="_Test Company")
+ movement3 = create_asset_movement(purpose = 'Issue', company = asset.company,
+ assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}],
+ reference_doctype = 'Purchase Receipt', reference_name = pr.name)
+
+ # after issuing asset should belong to an employee not at a location
+ self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None)
+ self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee)
+
+ def test_last_movement_cancellation(self):
+ pr = make_purchase_receipt(item_code="Macbook Pro",
+ qty=1, rate=100000.0, location="Test Location")
+
+ asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
asset = frappe.get_doc('Asset', asset_name)
- month_end_date = get_last_day(nowdate())
- asset.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
-
asset.calculate_depreciation = 1
+ asset.available_for_use_date = '2020-06-06'
+ asset.purchase_date = '2020-06-06'
asset.append("finance_books", {
- "expected_value_after_useful_life": 200,
+ "expected_value_after_useful_life": 10000,
+ "next_depreciation_date": "2020-12-31",
"depreciation_method": "Straight Line",
"total_number_of_depreciations": 3,
"frequency_of_depreciation": 10,
- "depreciation_start_date": month_end_date
+ "depreciation_start_date": "2020-06-06"
})
- asset.submit()
- serial_nos = frappe.db.get_value('Asset Movement', {'reference_name': pr.name}, 'serial_no')
+ if asset.docstatus == 0:
+ asset.submit()
+
+ if not frappe.db.exists("Location", "Test Location 2"):
+ frappe.get_doc({
+ 'doctype': 'Location',
+ 'location_name': 'Test Location 2'
+ }).insert()
+
+ movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name })
+ self.assertRaises(frappe.ValidationError, movement.cancel)
- mov1 = create_asset_movement(asset=asset_name, purpose = 'Transfer',
- company=asset.company, source_location = "Mumbai", target_location="Pune", serial_no=serial_nos)
- self.assertEqual(mov1.target_location, "Pune")
+ movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
+ assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
+ reference_doctype = 'Purchase Receipt', reference_name = pr.name)
+ self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
- serial_no = frappe.db.get_value('Serial No', {'asset': asset_name}, 'name')
-
- employee = make_employee("testassetemp@example.com")
- create_asset_movement(asset=asset_name, purpose = 'Transfer',
- company=asset.company, serial_no=serial_no, to_employee=employee)
-
- self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'employee'), employee)
-
- create_asset_movement(asset=asset_name, purpose = 'Transfer', company=asset.company,
- serial_no=serial_no, from_employee=employee, to_employee="_T-Employee-00001")
-
- self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'location'), "Pune")
-
- mov4 = create_asset_movement(asset=asset_name, purpose = 'Transfer',
- company=asset.company, source_location = "Pune", target_location="Nagpur", serial_no=serial_nos)
- self.assertEqual(mov4.target_location, "Nagpur")
- self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'location'), "Nagpur")
- self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'employee'), "_T-Employee-00001")
+ movement1.cancel()
+ self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
def create_asset_movement(**args):
args = frappe._dict(args)
@@ -109,22 +113,14 @@
movement = frappe.new_doc("Asset Movement")
movement.update({
- "asset": args.asset,
+ "assets": args.assets,
"transaction_date": args.transaction_date,
- "target_location": args.target_location,
"company": args.company,
'purpose': args.purpose or 'Receipt',
- 'serial_no': args.serial_no,
- 'quantity': len(get_serial_nos(args.serial_no)) if args.serial_no else 1,
- 'from_employee': "_T-Employee-00001" or args.from_employee,
- 'to_employee': args.to_employee
+ 'reference_doctype': args.reference_doctype,
+ 'reference_name': args.reference_name
})
- if args.source_location:
- movement.update({
- 'source_location': args.source_location
- })
-
movement.insert()
movement.submit()
@@ -137,33 +133,3 @@
'doctype': 'Location',
'location_name': location
}).insert(ignore_permissions = True)
-
-def make_serialized_item():
- asset_item = "Test Serialized Asset Item"
-
- if not frappe.db.exists('Item', asset_item):
- asset_category = frappe.get_all('Asset Category')
-
- if asset_category:
- asset_category = asset_category[0].name
-
- if not asset_category:
- doc = frappe.get_doc({
- 'doctype': 'Asset Category',
- 'asset_category_name': 'Test Asset Category',
- 'depreciation_method': 'Straight Line',
- 'total_number_of_depreciations': 12,
- 'frequency_of_depreciation': 1,
- 'accounts': [{
- 'company_name': '_Test Company',
- 'fixed_asset_account': '_Test Fixed Asset - _TC',
- 'accumulated_depreciation_account': 'Depreciation - _TC',
- 'depreciation_expense_account': 'Depreciation - _TC'
- }]
- }).insert()
-
- asset_category = doc.name
-
- make_item(asset_item, {'is_stock_item':0,
- 'stock_uom': 'Box', 'is_fixed_asset': 1, 'has_serial_no': 1,
- 'asset_category': asset_category, 'serial_no_series': 'ABC.###'})
diff --git a/erpnext/assets/doctype/asset_settings/__init__.py b/erpnext/assets/doctype/asset_movement_item/__init__.py
similarity index 100%
rename from erpnext/assets/doctype/asset_settings/__init__.py
rename to erpnext/assets/doctype/asset_movement_item/__init__.py
diff --git a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json
new file mode 100644
index 0000000..994c3c0
--- /dev/null
+++ b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json
@@ -0,0 +1,86 @@
+{
+ "creation": "2019-10-07 18:49:00.737806",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "company",
+ "asset",
+ "source_location",
+ "from_employee",
+ "column_break_2",
+ "asset_name",
+ "target_location",
+ "to_employee"
+ ],
+ "fields": [
+ {
+ "fieldname": "asset",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Asset",
+ "options": "Asset",
+ "reqd": 1
+ },
+ {
+ "fetch_from": "asset.asset_name",
+ "fieldname": "asset_name",
+ "fieldtype": "Data",
+ "label": "Asset Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "source_location",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Source Location",
+ "options": "Location"
+ },
+ {
+ "fieldname": "target_location",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Target Location",
+ "options": "Location"
+ },
+ {
+ "fieldname": "from_employee",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "in_list_view": 1,
+ "label": "From Employee",
+ "options": "Employee"
+ },
+ {
+ "fieldname": "to_employee",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "in_list_view": 1,
+ "label": "To Employee",
+ "options": "Employee"
+ },
+ {
+ "fieldname": "column_break_2",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Company",
+ "options": "Company",
+ "read_only": 1
+ }
+ ],
+ "istable": 1,
+ "modified": "2019-10-09 15:59:08.265141",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Movement 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/assets/doctype/asset_movement_item/asset_movement_item.py b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py
new file mode 100644
index 0000000..4c6aaab
--- /dev/null
+++ b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# 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.model.document import Document
+
+class AssetMovementItem(Document):
+ pass
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 3b42148..0000000
--- 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 edc5ce1..0000000
--- 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 e303ebd..0000000
--- 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 eac2c92..0000000
--- 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 75f146a..0000000
--- 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/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 66ad97a..c409c1f 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -43,6 +43,7 @@
"base_amount",
"pricing_rules",
"is_free_item",
+ "is_fixed_asset",
"section_break_29",
"net_rate",
"net_amount",
@@ -699,11 +700,19 @@
"fieldtype": "Data",
"label": "Manufacturer Part Number",
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fetch_from": "item_code.is_fixed_asset",
+ "fieldname": "is_fixed_asset",
+ "fieldtype": "Check",
+ "label": "Is Fixed Asset",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
- "modified": "2019-09-17 22:32:34.703923",
+ "modified": "2019-11-07 17:19:12.090355",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
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 9ad06f9..2f0cfa6 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 @@
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 a10ce46..95db33b 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 @@
@frappe.whitelist()
def get_supplier_tag():
- data = frappe.db.sql("select _user_tags from `tabSupplier`")
+ 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)
- tags = []
- for tag in data:
- tags += filter(bool, tag[0].split(","))
-
- tags = list(set(tags))
-
- return tags
-
+ return frappe.cache().hget("Supplier", "Tags")
diff --git a/erpnext/config/assets.py b/erpnext/config/assets.py
index 3c9452f..4cf7cf0 100644
--- a/erpnext/config/assets.py
+++ b/erpnext/config/assets.py
@@ -23,10 +23,6 @@
},
{
"type": "doctype",
- "name": "Asset Settings",
- },
- {
- "type": "doctype",
"name": "Asset Movement",
"description": _("Transfer an asset from one warehouse to another")
},
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 67f453d..a912ef0 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -718,48 +718,6 @@
# at quotation / sales order level and we shouldn't stop someone
# from creating a sales invoice if sales order is already created
- def validate_fixed_asset(self):
- for d in self.get("items"):
- if d.is_fixed_asset:
- # if d.qty > 1:
- # frappe.throw(_("Row #{0}: Qty must be 1, as item is a fixed asset. Please use separate row for multiple qty.").format(d.idx))
-
- if d.meta.get_field("asset") and d.asset:
- asset = frappe.get_doc("Asset", d.asset)
-
- if asset.company != self.company:
- frappe.throw(_("Row #{0}: Asset {1} does not belong to company {2}")
- .format(d.idx, d.asset, self.company))
-
- elif asset.item_code != d.item_code:
- frappe.throw(_("Row #{0}: Asset {1} does not linked to Item {2}")
- .format(d.idx, d.asset, d.item_code))
-
- # elif asset.docstatus != 1:
- # frappe.throw(_("Row #{0}: Asset {1} must be submitted").format(d.idx, d.asset))
-
- elif self.doctype == "Purchase Invoice":
- # if asset.status != "Submitted":
- # frappe.throw(_("Row #{0}: Asset {1} is already {2}")
- # .format(d.idx, d.asset, asset.status))
- if getdate(asset.purchase_date) != getdate(self.posting_date):
- frappe.throw(
- _("Row #{0}: Posting Date must be same as purchase date {1} of asset {2}").format(d.idx,
- asset.purchase_date,
- d.asset))
- elif asset.is_existing_asset:
- frappe.throw(
- _("Row #{0}: Purchase Invoice cannot be made against an existing asset {1}").format(
- d.idx, d.asset))
-
- elif self.docstatus == "Sales Invoice" and self.docstatus == 1:
- if self.update_stock:
- frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
-
- elif asset.status in ("Scrapped", "Cancelled", "Sold"):
- frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}")
- .format(d.idx, d.asset, asset.status))
-
def delink_advance_entries(self, linked_doc_name):
total_allocated_amount = 0
for adv in self.advances:
@@ -1193,8 +1151,9 @@
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/buying_controller.py b/erpnext/controllers/buying_controller.py
index 0dde898..d0befcb 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -101,7 +101,7 @@
msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
def get_asset_items(self):
- if self.doctype not in ['Purchase Invoice', 'Purchase Receipt']:
+ if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']:
return []
return [d.item_code for d in self.items if d.is_fixed_asset]
@@ -150,25 +150,26 @@
TODO: rename item_tax_amount to valuation_tax_amount
"""
- stock_items = self.get_stock_items() + self.get_asset_items()
+ stock_and_asset_items = self.get_stock_items() + self.get_asset_items()
- stock_items_qty, stock_items_amount = 0, 0
- last_stock_item_idx = 1
+ stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0
+ last_item_idx = 1
for d in self.get(parentfield):
- if d.item_code and d.item_code in stock_items:
- stock_items_qty += flt(d.qty)
- stock_items_amount += flt(d.base_net_amount)
- last_stock_item_idx = d.idx
+ if d.item_code and d.item_code in stock_and_asset_items:
+ stock_and_asset_items_qty += flt(d.qty)
+ stock_and_asset_items_amount += flt(d.base_net_amount)
+ last_item_idx = d.idx
total_valuation_amount = sum([flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes")
if d.category in ["Valuation", "Valuation and Total"]])
valuation_amount_adjustment = total_valuation_amount
for i, item in enumerate(self.get(parentfield)):
- if item.item_code and item.qty and item.item_code in stock_items:
- item_proportion = flt(item.base_net_amount) / stock_items_amount if stock_items_amount \
- else flt(item.qty) / stock_items_qty
- if i == (last_stock_item_idx - 1):
+ if item.item_code and item.qty and item.item_code in stock_and_asset_items:
+ item_proportion = flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount \
+ else flt(item.qty) / stock_and_asset_items_qty
+
+ if i == (last_item_idx - 1):
item.item_tax_amount = flt(valuation_amount_adjustment,
self.precision("item_tax_amount", item))
else:
@@ -572,43 +573,28 @@
asset_items = self.get_asset_items()
if asset_items:
- self.make_serial_nos_for_asset(asset_items)
+ self.auto_make_assets(asset_items)
- def make_serial_nos_for_asset(self, asset_items):
+ def auto_make_assets(self, asset_items):
items_data = get_asset_item_details(asset_items)
for d in self.items:
if d.is_fixed_asset:
item_data = items_data.get(d.item_code)
- if not d.asset:
- asset = self.make_asset(d)
- d.db_set('asset', asset)
- if item_data.get('has_serial_no'):
- # If item has serial no
- if item_data.get('serial_no_series') and not d.serial_no:
- serial_nos = get_auto_serial_nos(item_data.get('serial_no_series'), d.qty)
- elif d.serial_no:
- serial_nos = d.serial_no
- elif not d.serial_no:
- frappe.throw(_("Serial no is mandatory for the item {0}").format(d.item_code))
-
- auto_make_serial_nos({
- 'serial_no': serial_nos,
- 'item_code': d.item_code,
- 'via_stock_ledger': False,
- 'company': self.company,
- 'supplier': self.supplier,
- 'actual_qty': d.qty,
- 'purchase_document_type': self.doctype,
- 'purchase_document_no': self.name,
- 'asset': d.asset,
- 'location': d.asset_location
- })
- d.db_set('serial_no', serial_nos)
-
- if d.asset:
- self.make_asset_movement(d)
+ if item_data.get('auto_create_assets'):
+ # If asset has to be auto created
+ # Check for asset naming series
+ if item_data.get('asset_naming_series'):
+ for qty in range(cint(d.qty)):
+ self.make_asset(d)
+ is_plural = 's' if cint(d.qty) != 1 else ''
+ frappe.msgprint(_('{0} Asset{2} Created for {1}').format(cint(d.qty), d.item_code, is_plural))
+ else:
+ frappe.throw(_("Asset Naming Series is mandatory for the auto creation for item {0}").format(d.item_code))
+ else:
+ frappe.msgprint(_("Assets not created. You will have to create asset manually."))
+
def make_asset(self, row):
if not row.asset_location:
@@ -617,7 +603,7 @@
item_data = frappe.db.get_value('Item',
row.item_code, ['asset_naming_series', 'asset_category'], as_dict=1)
- purchase_amount = flt(row.base_net_amount + row.item_tax_amount)
+ purchase_amount = flt(row.base_rate + row.item_tax_amount)
asset = frappe.get_doc({
'doctype': 'Asset',
'item_code': row.item_code,
@@ -640,57 +626,42 @@
asset.set_missing_values()
asset.insert()
- asset_link = frappe.utils.get_link_to_form('Asset', asset.name)
- frappe.msgprint(_("Asset {0} created").format(asset_link))
- return asset.name
-
- def make_asset_movement(self, row):
- asset_movement = frappe.get_doc({
- 'doctype': 'Asset Movement',
- 'asset': row.asset,
- 'target_location': row.asset_location,
- 'purpose': 'Receipt',
- 'serial_no': row.serial_no,
- 'quantity': len(get_serial_nos(row.serial_no)),
- 'company': self.company,
- 'transaction_date': self.posting_date,
- 'reference_doctype': self.doctype,
- 'reference_name': self.name
- }).insert()
-
- return asset_movement.name
-
def update_fixed_asset(self, field, delete_asset = False):
for d in self.get("items"):
- if d.is_fixed_asset and d.asset:
- asset = frappe.get_doc("Asset", d.asset)
+ if d.is_fixed_asset:
+ is_auto_create_enabled = frappe.db.get_value('Item', d.item_code, 'auto_create_assets')
+ assets = frappe.db.get_all('Asset', filters={ field : self.name, 'item_code' : d.item_code })
- if delete_asset and asset.docstatus == 0:
- frappe.delete_doc("Asset", asset.name)
- d.db_set('asset', None)
- continue
+ for asset in assets:
+ asset = frappe.get_doc('Asset', asset.name)
+ if delete_asset and is_auto_create_enabled:
+ # need to delete movements to delete assets otherwise throws link exists error
+ movements = frappe.db.get_all('Asset Movement', filters={ 'reference_name': self.name })
+ for movement in movements:
+ frappe.delete_doc('Asset Movement', movement.name, force=1)
+ frappe.delete_doc("Asset", asset.name, force=1)
+ continue
- if self.docstatus in [0, 1] and not asset.get(field):
- asset.set(field, self.name)
- asset.purchase_date = self.posting_date
- asset.supplier = self.supplier
- elif self.docstatus == 2:
- asset.set(field, None)
- asset.supplier = None
+ if self.docstatus in [0, 1] and not asset.get(field):
+ asset.set(field, self.name)
+ asset.purchase_date = self.posting_date
+ asset.supplier = self.supplier
+ elif self.docstatus == 2:
+ asset.set(field, None)
+ asset.supplier = None
- asset.flags.ignore_validate_update_after_submit = True
- asset.flags.ignore_mandatory = True
- if asset.docstatus == 0:
- asset.flags.ignore_validate = True
+ asset.flags.ignore_validate_update_after_submit = True
+ asset.flags.ignore_mandatory = True
+ if asset.docstatus == 0:
+ asset.flags.ignore_validate = True
- asset.save()
+ asset.save()
def delete_linked_asset(self):
if self.doctype == 'Purchase Invoice' and not self.get('update_stock'):
return
frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name)
- frappe.db.sql("delete from `tabSerial No` where purchase_document_no=%s", self.name)
def validate_schedule_date(self):
if not self.get("items"):
@@ -764,7 +735,7 @@
def get_asset_item_details(asset_items):
asset_items_data = {}
- for d in frappe.get_all('Item', fields = ["name", "has_serial_no", "serial_no_series"],
+ for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series"],
filters = {'name': ('in', asset_items)}):
asset_items_data.setdefault(d.name, d)
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index a9e50ba..3830ca0 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -476,3 +476,29 @@
as_list=1
)
return item_manufacturers
+
+@frappe.whitelist()
+def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters):
+ query = """
+ select pr.name
+ from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pritem
+ where pr.docstatus = 1 and pritem.parent = pr.name
+ and pr.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
+
+ if filters and filters.get('item_code'):
+ query += " and pritem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code')))
+
+ return frappe.db.sql(query, filters)
+
+@frappe.whitelist()
+def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters):
+ query = """
+ select pi.name
+ from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` piitem
+ where pi.docstatus = 1 and piitem.parent = pi.name
+ and pi.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt)))
+
+ if filters and filters.get('item_code'):
+ query += " and piitem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code')))
+
+ return frappe.db.sql(query, filters)
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 9d1389c..2b2c27b 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -49,7 +49,8 @@
["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 @@
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 3259136..736a9d6 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 98e4927..3050d05 100644
--- a/erpnext/crm/doctype/email_campaign/email_campaign.py
+++ b/erpnext/crm/doctype/email_campaign/email_campaign.py
@@ -73,13 +73,13 @@
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 32b5fb8..967a030 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/instructor/instructor.js b/erpnext/education/doctype/instructor/instructor.js
index f9c7a2a..71e044b 100644
--- a/erpnext/education/doctype/instructor/instructor.js
+++ b/erpnext/education/doctype/instructor/instructor.js
@@ -4,11 +4,11 @@
frappe.ui.form.on("Instructor", {
employee: function(frm) {
if(!frm.doc.employee) return;
- frappe.db.get_value('Employee', {name: frm.doc.employee}, 'company', (company) => {
+ frappe.db.get_value('Employee', {name: frm.doc.employee}, 'company', (d) => {
frm.set_query("department", function() {
return {
"filters": {
- "company": company,
+ "company": d.company,
}
};
});
@@ -16,7 +16,7 @@
frm.set_query("department", "instructor_log", function() {
return {
"filters": {
- "company": company,
+ "company": d.company,
}
};
});
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index 705c6e4..9af5e22 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -40,7 +40,8 @@
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 0b6ea8c..28c2ab9 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 @@
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 0cad0cc..64c3b2d 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 @@
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 dd3c24d..956ae09 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 055684d..bd072f4 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.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 @@
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 b1855ec..9e74bfd 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -235,17 +235,16 @@
("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 9f2f201..df0f75a 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -19,14 +19,19 @@
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)
+
+ 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" and employee.leave_approver:
+ approvers.append(frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name']))
if filters.get("doctype") == "Leave Application":
parentfield = "leave_approvers"
@@ -41,4 +46,4 @@
and approver.parentfield = %s
and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True)
- return approvers
\ No newline at end of file
+ return set(tuple(approver) for approver in approvers)
diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py
index 3fc330e..703ec06 100755
--- a/erpnext/hr/doctype/employee/employee.py
+++ b/erpnext/hr/doctype/employee/employee.py
@@ -167,10 +167,11 @@
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 162b697..11ad83b 100644
--- a/erpnext/hr/doctype/employee/employee_dashboard.py
+++ b/erpnext/hr/doctype/employee/employee_dashboard.py
@@ -21,7 +21,7 @@
},
{
'label': _('Expense'),
- 'items': ['Expense Claim', 'Travel Request']
+ 'items': ['Expense Claim', 'Travel Request', 'Employee Advance']
},
{
'label': _('Benefit'),
diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py
index 5a63beb..d3410de 100644
--- a/erpnext/hr/doctype/employee/test_employee.py
+++ b/erpnext/hr/doctype/employee/test_employee.py
@@ -45,7 +45,7 @@
employee1_doc.status = 'Left'
self.assertRaises(EmployeeLeftValidationError, employee1_doc.save)
-def make_employee(user):
+def make_employee(user, company=None):
if not frappe.db.get_value("User", user):
frappe.get_doc({
"doctype": "User",
@@ -55,12 +55,12 @@
"roles": [{"doctype": "Has Role", "role": "Employee"}]
}).insert()
- if not frappe.db.get_value("Employee", {"user_id": user}):
+ if not frappe.db.get_value("Employee", { "user_id": user, "company": company or erpnext.get_default_company() }):
employee = frappe.get_doc({
"doctype": "Employee",
"naming_series": "EMP-",
"first_name": user,
- "company": erpnext.get_default_company(),
+ "company": company or erpnext.get_default_company(),
"user_id": user,
"date_of_birth": "1990-05-08",
"date_of_joining": "2013-01-01",
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json
index 4e2778f..5c2f490 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 caeb2dd..f003627 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -144,6 +144,33 @@
"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 @@
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 e1e5e80..0e66305 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -55,11 +55,11 @@
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 @@
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 d56320a..dd34ef2 100755
--- a/erpnext/hr/doctype/salary_structure/salary_structure.js
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.js
@@ -46,10 +46,12 @@
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 f7d712d..0e1a74f 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.py
@@ -169,5 +169,10 @@
@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 e6afbcc..595bcaa 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -7,6 +7,7 @@
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 @@
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 @@
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 a0b6fc7..29fedcd 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 c4a9030..b28eac4 100644
--- a/erpnext/hr/report/department_analytics/department_analytics.py
+++ b/erpnext/hr/report/department_analytics/department_analytics.py
@@ -7,6 +7,10 @@
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 @@
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 @@
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 c849f5b..db79d7f 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -420,8 +420,12 @@
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 @@
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 @@
# 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 @@
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 87b8f67..2ca4d16 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 @@
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 @@
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 39d59f0..f27197d 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 4b654b4..3b24d0f 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 @@
},
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 @@
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 10d9a47..5d26969 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -99,7 +99,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
'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 @@
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 @@
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 @@
wo_list.extend(work_orders)
frappe.flags.mute_messages = False
+
if wo_list:
wo_list = ["""<a href="#Form/Work Order/%s" target="_blank">%s</a>""" % \
(p, p) for p in wo_list]
@@ -309,16 +311,15 @@
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 @@
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 @@
'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 @@
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 @@
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 @@
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 @@
})
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 4479641..f70c9cc 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.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 cdbce33..107c79b 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -395,6 +395,11 @@
}
});
}
+ },
+
+ additional_operating_cost: function(frm) {
+ erpnext.work_order.calculate_cost(frm.doc);
+ erpnext.work_order.calculate_total_cost(frm);
}
});
@@ -534,8 +539,7 @@
},
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 b57548e..089cb80 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -216,14 +216,24 @@
self.db_set(fieldname, qty)
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
- update_produced_qty_in_so_item(self.sales_order_item)
+
+ if self.sales_order and self.sales_order_item:
+ update_produced_qty_in_so_item(self.sales_order, self.sales_order_item)
if self.production_plan:
self.update_production_plan_status()
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 +655,8 @@
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 ee6bdff..9e4dc12 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -638,6 +638,11 @@
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/make_asset_finance_book_against_old_entries.py b/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py
index 1c8bd68..ee709ac 100644
--- a/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py
+++ b/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py
@@ -17,10 +17,6 @@
frappe.db.sql(""" update `tabAsset` ast, `tabWarehouse` wh
set ast.location = wh.warehouse_name where ast.warehouse = wh.name""")
- frappe.db.sql(""" update `tabAsset Movement` ast_mv
- set ast_mv.source_location = (select warehouse_name from `tabWarehouse` where name = ast_mv.source_warehouse),
- ast_mv.target_location = (select warehouse_name from `tabWarehouse` where name = ast_mv.target_warehouse)""")
-
for d in frappe.get_all('Asset'):
doc = frappe.get_doc('Asset', d.name)
if doc.calculate_depreciation:
diff --git a/erpnext/patches/v11_0/update_delivery_trip_status.py b/erpnext/patches/v11_0/update_delivery_trip_status.py
index 64b3063..42f017e 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 412f320..f25b9ea 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 @@
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 @@
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 @@
'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 0000000..7859606
--- /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 0000000..5842e9e
--- /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 0000000..06ee798
--- /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 0000000..a996a69
--- /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 0000000..fafbec6
--- /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/set_produced_qty_field_in_sales_order_for_work_order.py b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
index 44d8fa7..0702673 100644
--- a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
+++ b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py
@@ -3,8 +3,12 @@
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
def execute():
- frappe.reload_doctype('Sales Order Item')
- frappe.reload_doctype('Sales Order')
- sales_order_items = frappe.db.get_all('Sales Order Item', ['name'])
- for so_item in sales_order_items:
- update_produced_qty_in_so_item(so_item.get('name'))
\ No newline at end of file
+ frappe.reload_doctype('Sales Order Item')
+ frappe.reload_doctype('Sales Order')
+
+ for d in frappe.get_all('Work Order',
+ fields = ['sales_order', 'sales_order_item'],
+ filters={'sales_order': ('!=', ''), 'sales_order_item': ('!=', '')}):
+
+ # update produced qty in sales order
+ update_produced_qty_in_so_item(d.sales_order, d.sales_order_item)
\ 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 0000000..e4dcecd
--- /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 192e1bc..25c97d1 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -19,7 +19,7 @@
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 @@
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 @@
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 @@
}
},
- 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 783bcf3..bf6e21a 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -323,6 +323,37 @@
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 90e9f05..54fce8d 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -10,6 +10,7 @@
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 @@
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 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 bc88250..c4481c9 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -188,6 +188,8 @@
}, 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 ffc5e6a..6f43d9e 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -63,12 +63,15 @@
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 7388ea0..630d5fa 100644
--- a/erpnext/regional/__init__.py
+++ b/erpnext/regional/__init__.py
@@ -10,3 +10,21 @@
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 e4b72f6..424615d 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 0bc277f..aae0779 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -72,8 +72,8 @@
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 922619c..090616b 100644
--- a/erpnext/regional/report/gstr_1/gstr_1.py
+++ b/erpnext/regional/report/gstr_1/gstr_1.py
@@ -116,7 +116,7 @@
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%
copy from erpnext/assets/doctype/asset_settings/__init__.py
copy to erpnext/regional/turkey/__init__.py
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index c4c3c0f..e12b359 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -1038,14 +1038,18 @@
return doc
-def update_produced_qty_in_so_item(sales_order_item):
+def update_produced_qty_in_so_item(sales_order, sales_order_item):
#for multiple work orders against same sales order item
linked_wo_with_so_item = frappe.db.get_all('Work Order', ['produced_qty'], {
'sales_order_item': sales_order_item,
+ 'sales_order': sales_order,
'docstatus': 1
})
- if len(linked_wo_with_so_item) > 0:
- total_produced_qty = 0
- for wo in linked_wo_with_so_item:
- total_produced_qty += flt(wo.get('produced_qty'))
- frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
\ No newline at end of file
+
+ total_produced_qty = 0
+ for wo in linked_wo_with_so_item:
+ total_produced_qty += flt(wo.get('produced_qty'))
+
+ if not total_produced_qty and frappe.flags.in_patch: return
+
+ frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_produced_qty)
\ No newline at end of file
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 0000000..ee806a7
--- /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 88e6f27..a6dda28 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 0000000..226c34f
--- /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.js b/erpnext/setup/doctype/company/company.js
index 313de67..81c5f02 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -29,7 +29,8 @@
company_name: function(frm) {
if(frm.doc.__islocal) {
- let parts = frm.doc.company_name.split();
+ # add missing " " arg in split method
+ let parts = frm.doc.company_name.split(" ");
let abbr = $.map(parts, function (p) {
return p? p.substr(0, 1) : null;
}).join("");
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index bc34189..2d181b5 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 8b42b19..04e8a83 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -207,7 +207,7 @@
"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 4effb5a..6480f60 100644
--- a/erpnext/setup/doctype/currency_exchange/currency_exchange.py
+++ b/erpnext/setup/doctype/currency_exchange/currency_exchange.py
@@ -11,10 +11,19 @@
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 @@
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 c488b99..c5c01c5 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 @@
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 @@
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 @@
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 @@
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 @@
# 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 0c9cfbb..152060e 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 1de5ccb..0bcddc2 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 @@
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 @@
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/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 5603f17..f78246f 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -136,6 +136,7 @@
fields = ['name', 'route', 'description', 'image'],
filters = dict(
show_in_website = 1,
+ parent_item_group = item_group.name,
lft = ('>', item_group.lft),
rgt = ('<', item_group.rgt),
),
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index a4c10cf..1236ade 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -11,6 +11,7 @@
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 @@
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 @@
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 @@
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 @@
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 f609a0b..3e890b4 100644
--- a/erpnext/stock/doctype/batch/batch.py
+++ b/erpnext/stock/doctype/batch/batch.py
@@ -185,9 +185,17 @@
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/item/item.js b/erpnext/stock/doctype/item/item.js
index bfc5e6d..2f4abbc 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -25,7 +25,7 @@
},
refresh: function(frm) {
- if(frm.doc.is_stock_item) {
+ if (frm.doc.is_stock_item) {
frm.add_custom_button(__("Balance"), function() {
frappe.route_options = {
"item_code": frm.doc.name
@@ -46,10 +46,15 @@
}, __("View"));
}
- if(!frm.doc.is_fixed_asset) {
+ if (!frm.doc.is_fixed_asset) {
erpnext.item.make_dashboard(frm);
}
+ if (frm.doc.is_fixed_asset) {
+ frm.trigger('is_fixed_asset');
+ frm.trigger('auto_create_assets');
+ }
+
// clear intro
frm.set_intro();
@@ -132,6 +137,11 @@
},
is_fixed_asset: function(frm) {
+ // set serial no to false & toggles its visibility
+ frm.set_value('has_serial_no', 0);
+ frm.toggle_enable(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
+ frm.toggle_display(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
+
frm.call({
method: "set_asset_naming_series",
doc: frm.doc,
@@ -139,7 +149,7 @@
frm.set_value("is_stock_item", frm.doc.is_fixed_asset ? 0 : 1);
frm.trigger("set_asset_naming_series");
}
- })
+ });
},
set_asset_naming_series: function(frm) {
@@ -148,6 +158,11 @@
}
},
+ auto_create_assets: function(frm) {
+ frm.toggle_reqd(['asset_category', 'asset_naming_series'], frm.doc.auto_create_assets);
+ frm.toggle_display(['asset_category', 'asset_naming_series'], frm.doc.auto_create_assets);
+ },
+
page_name: frappe.utils.warn_page_name_change,
item_code: function(frm) {
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 46efd4e..a2aab3f 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -1,1105 +1,1114 @@
{
- "allow_guest_to_view": 1,
- "allow_import": 1,
- "allow_rename": 1,
- "autoname": "field:item_code",
- "creation": "2013-05-03 10:45:46",
- "description": "A Product or a Service that is bought, sold or kept in stock.",
- "doctype": "DocType",
- "document_type": "Setup",
- "editable_grid": 1,
- "engine": "InnoDB",
- "field_order": [
- "name_and_description_section",
- "naming_series",
- "item_code",
- "variant_of",
- "item_name",
- "item_group",
- "is_item_from_hub",
- "stock_uom",
- "column_break0",
- "disabled",
- "allow_alternative_item",
- "is_stock_item",
- "include_item_in_manufacturing",
- "opening_stock",
- "valuation_rate",
- "standard_rate",
- "is_fixed_asset",
- "asset_category",
- "asset_naming_series",
- "over_delivery_receipt_allowance",
- "over_billing_allowance",
- "image",
- "section_break_11",
- "brand",
- "description",
- "sb_barcodes",
- "barcodes",
- "inventory_section",
- "shelf_life_in_days",
- "end_of_life",
- "default_material_request_type",
- "valuation_method",
- "column_break1",
- "warranty_period",
- "weight_per_unit",
- "weight_uom",
- "reorder_section",
- "reorder_levels",
- "unit_of_measure_conversion",
- "uoms",
- "serial_nos_and_batches",
- "has_batch_no",
- "create_new_batch",
- "batch_number_series",
- "has_expiry_date",
- "retain_sample",
- "sample_quantity",
- "column_break_37",
- "has_serial_no",
- "serial_no_series",
- "variants_section",
- "has_variants",
- "variant_based_on",
- "attributes",
- "defaults",
- "item_defaults",
- "purchase_details",
- "is_purchase_item",
- "purchase_uom",
- "min_order_qty",
- "safety_stock",
- "purchase_details_cb",
- "lead_time_days",
- "last_purchase_rate",
- "is_customer_provided_item",
- "customer",
- "supplier_details",
- "delivered_by_supplier",
- "column_break2",
- "supplier_items",
- "foreign_trade_details",
- "country_of_origin",
- "column_break_59",
- "customs_tariff_number",
- "sales_details",
- "sales_uom",
- "is_sales_item",
- "column_break3",
- "max_discount",
- "deferred_revenue",
- "deferred_revenue_account",
- "enable_deferred_revenue",
- "column_break_85",
- "no_of_months",
- "deferred_expense_section",
- "deferred_expense_account",
- "enable_deferred_expense",
- "column_break_88",
- "no_of_months_exp",
- "customer_details",
- "customer_items",
- "item_tax_section_break",
- "taxes",
- "inspection_criteria",
- "inspection_required_before_purchase",
- "inspection_required_before_delivery",
- "quality_inspection_template",
- "manufacturing",
- "default_bom",
- "is_sub_contracted_item",
- "column_break_74",
- "customer_code",
- "website_section",
- "show_in_website",
- "show_variant_in_website",
- "route",
- "weightage",
- "slideshow",
- "website_image",
- "thumbnail",
- "cb72",
- "website_warehouse",
- "website_item_groups",
- "set_meta_tags",
- "sb72",
- "copy_from_item_group",
- "website_specifications",
- "web_long_description",
- "website_content",
- "total_projected_qty",
- "hub_publishing_sb",
- "publish_in_hub",
- "hub_category_to_publish",
- "hub_warehouse",
- "synced_with_hub"
- ],
- "fields": [
- {
- "fieldname": "name_and_description_section",
- "fieldtype": "Section Break",
- "oldfieldtype": "Section Break",
- "options": "fa fa-flag"
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Series",
- "options": "STO-ITEM-.YYYY.-",
- "set_only_once": 1
- },
- {
- "bold": 1,
- "fieldname": "item_code",
- "fieldtype": "Data",
- "in_global_search": 1,
- "label": "Item Code",
- "oldfieldname": "item_code",
- "oldfieldtype": "Data",
- "unique": 1,
- "reqd": 1
- },
- {
- "depends_on": "variant_of",
- "description": "If item is a variant of another item then description, image, pricing, taxes etc will be set from the template unless explicitly specified",
- "fieldname": "variant_of",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "in_standard_filter": 1,
- "label": "Variant Of",
- "options": "Item",
- "read_only": 1,
- "search_index": 1,
- "set_only_once": 1
- },
- {
- "bold": 1,
- "fieldname": "item_name",
- "fieldtype": "Data",
- "in_global_search": 1,
- "label": "Item Name",
- "oldfieldname": "item_name",
- "oldfieldtype": "Data",
- "search_index": 1
- },
- {
- "fieldname": "item_group",
- "fieldtype": "Link",
- "in_list_view": 1,
- "in_preview": 1,
- "in_standard_filter": 1,
- "label": "Item Group",
- "oldfieldname": "item_group",
- "oldfieldtype": "Link",
- "options": "Item Group",
- "reqd": 1,
- "search_index": 1
- },
- {
- "default": "0",
- "fieldname": "is_item_from_hub",
- "fieldtype": "Check",
- "label": "Is Item from Hub",
- "read_only": 1
- },
- {
- "fieldname": "stock_uom",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Default Unit of Measure",
- "oldfieldname": "stock_uom",
- "oldfieldtype": "Link",
- "options": "UOM",
- "reqd": 1
- },
- {
- "fieldname": "column_break0",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "fieldname": "disabled",
- "fieldtype": "Check",
- "label": "Disabled"
- },
- {
- "default": "0",
- "fieldname": "allow_alternative_item",
- "fieldtype": "Check",
- "label": "Allow Alternative Item"
- },
- {
- "bold": 1,
- "default": "1",
- "fieldname": "is_stock_item",
- "fieldtype": "Check",
- "label": "Maintain Stock",
- "oldfieldname": "is_stock_item",
- "oldfieldtype": "Select"
- },
- {
- "default": "1",
- "fieldname": "include_item_in_manufacturing",
- "fieldtype": "Check",
- "label": "Include Item In Manufacturing"
- },
- {
- "bold": 1,
- "depends_on": "eval:(doc.__islocal&&doc.is_stock_item && !doc.has_serial_no && !doc.has_batch_no)",
- "fieldname": "opening_stock",
- "fieldtype": "Float",
- "label": "Opening Stock"
- },
- {
- "depends_on": "is_stock_item",
- "fieldname": "valuation_rate",
- "fieldtype": "Currency",
- "label": "Valuation Rate"
- },
- {
- "bold": 1,
- "depends_on": "eval:doc.__islocal",
- "fieldname": "standard_rate",
- "fieldtype": "Currency",
- "label": "Standard Selling Rate"
- },
- {
- "default": "0",
- "fieldname": "is_fixed_asset",
- "fieldtype": "Check",
- "label": "Is Fixed Asset",
- "set_only_once": 1
- },
- {
- "depends_on": "is_fixed_asset",
- "fieldname": "asset_category",
- "fieldtype": "Link",
- "label": "Asset Category",
- "options": "Asset Category"
- },
- {
- "depends_on": "is_fixed_asset",
- "fieldname": "asset_naming_series",
- "fieldtype": "Select",
- "label": "Asset Naming Series"
- },
- {
- "fieldname": "image",
- "fieldtype": "Attach Image",
- "hidden": 1,
- "in_preview": 1,
- "label": "Image",
- "options": "image",
- "print_hide": 1
- },
- {
- "collapsible": 1,
- "fieldname": "section_break_11",
- "fieldtype": "Section Break",
- "label": "Description"
- },
- {
- "fieldname": "brand",
- "fieldtype": "Link",
- "label": "Brand",
- "oldfieldname": "brand",
- "oldfieldtype": "Link",
- "options": "Brand",
- "print_hide": 1
- },
- {
- "fieldname": "description",
- "fieldtype": "Text Editor",
- "in_preview": 1,
- "label": "Description",
- "oldfieldname": "description",
- "oldfieldtype": "Text"
- },
- {
- "fieldname": "sb_barcodes",
- "fieldtype": "Section Break",
- "label": "Barcodes"
- },
- {
- "fieldname": "barcodes",
- "fieldtype": "Table",
- "label": "Barcodes",
- "options": "Item Barcode"
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "is_stock_item",
- "depends_on": "is_stock_item",
- "fieldname": "inventory_section",
- "fieldtype": "Section Break",
- "label": "Inventory",
- "oldfieldtype": "Section Break",
- "options": "fa fa-truck"
- },
- {
- "fieldname": "shelf_life_in_days",
- "fieldtype": "Int",
- "label": "Shelf Life In Days"
- },
- {
- "default": "2099-12-31",
- "depends_on": "is_stock_item",
- "fieldname": "end_of_life",
- "fieldtype": "Date",
- "label": "End of Life",
- "oldfieldname": "end_of_life",
- "oldfieldtype": "Date"
- },
- {
- "default": "Purchase",
- "fieldname": "default_material_request_type",
- "fieldtype": "Select",
- "label": "Default Material Request Type",
- "options": "Purchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided"
- },
- {
- "depends_on": "is_stock_item",
- "fieldname": "valuation_method",
- "fieldtype": "Select",
- "label": "Valuation Method",
- "options": "\nFIFO\nMoving Average",
- "set_only_once": 1
- },
- {
- "depends_on": "is_stock_item",
- "fieldname": "column_break1",
- "fieldtype": "Column Break",
- "oldfieldtype": "Column Break",
- "width": "50%"
- },
- {
- "depends_on": "eval:doc.is_stock_item",
- "fieldname": "warranty_period",
- "fieldtype": "Data",
- "label": "Warranty Period (in days)",
- "oldfieldname": "warranty_period",
- "oldfieldtype": "Data"
- },
- {
- "depends_on": "is_stock_item",
- "fieldname": "weight_per_unit",
- "fieldtype": "Float",
- "label": "Weight Per Unit"
- },
- {
- "depends_on": "eval:doc.is_stock_item",
- "fieldname": "weight_uom",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Weight UOM",
- "options": "UOM"
- },
- {
- "collapsible": 1,
- "depends_on": "is_stock_item",
- "fieldname": "reorder_section",
- "fieldtype": "Section Break",
- "label": "Auto re-order",
- "options": "fa fa-rss"
- },
- {
- "description": "Will also apply for variants unless overrridden",
- "fieldname": "reorder_levels",
- "fieldtype": "Table",
- "label": "Reorder level based on Warehouse",
- "options": "Item Reorder"
- },
- {
- "collapsible": 1,
- "fieldname": "unit_of_measure_conversion",
- "fieldtype": "Section Break",
- "label": "Units of Measure"
- },
- {
- "description": "Will also apply for variants",
- "fieldname": "uoms",
- "fieldtype": "Table",
- "label": "UOMs",
- "oldfieldname": "uom_conversion_details",
- "oldfieldtype": "Table",
- "options": "UOM Conversion Detail"
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no || doc.is_fixed_asset",
- "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
- "fieldname": "serial_nos_and_batches",
- "fieldtype": "Section Break",
- "label": "Serial Nos and Batches"
- },
- {
- "default": "0",
- "depends_on": "eval:doc.is_stock_item",
- "fieldname": "has_batch_no",
- "fieldtype": "Check",
- "label": "Has Batch No",
- "no_copy": 1,
- "oldfieldname": "has_batch_no",
- "oldfieldtype": "Select"
- },
- {
- "default": "0",
- "depends_on": "has_batch_no",
- "fieldname": "create_new_batch",
- "fieldtype": "Check",
- "label": "Automatically Create New Batch"
- },
- {
- "depends_on": "eval:doc.has_batch_no==1 && doc.create_new_batch==1",
- "description": "Example: ABCD.#####. If series is set and Batch No is not mentioned in transactions, then automatic batch number will be created based on this series. If you always want to explicitly mention Batch No for this item, leave this blank. Note: this setting will take priority over the Naming Series Prefix in Stock Settings.",
- "fieldname": "batch_number_series",
- "fieldtype": "Data",
- "label": "Batch Number Series",
- "translatable": 1
- },
- {
- "default": "0",
- "depends_on": "has_batch_no",
- "fieldname": "has_expiry_date",
- "fieldtype": "Check",
- "label": "Has Expiry Date"
- },
- {
- "default": "0",
- "fieldname": "retain_sample",
- "fieldtype": "Check",
- "label": "Retain Sample"
- },
- {
- "depends_on": "eval: (doc.retain_sample && doc.has_batch_no)",
- "description": "Maximum sample quantity that can be retained",
- "fieldname": "sample_quantity",
- "fieldtype": "Int",
- "label": "Max Sample Quantity"
- },
- {
- "fieldname": "column_break_37",
- "fieldtype": "Column Break"
- },
- {
- "default": "0",
- "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
- "fieldname": "has_serial_no",
- "fieldtype": "Check",
- "label": "Has Serial No",
- "no_copy": 1,
- "oldfieldname": "has_serial_no",
- "oldfieldtype": "Select"
- },
- {
- "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
- "description": "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.",
- "fieldname": "serial_no_series",
- "fieldtype": "Data",
- "label": "Serial Number Series"
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "attributes",
- "fieldname": "variants_section",
- "fieldtype": "Section Break",
- "label": "Variants"
- },
- {
- "default": "0",
- "depends_on": "eval:!doc.variant_of",
- "description": "If this item has variants, then it cannot be selected in sales orders etc.",
- "fieldname": "has_variants",
- "fieldtype": "Check",
- "in_standard_filter": 1,
- "label": "Has Variants",
- "no_copy": 1
- },
- {
- "default": "Item Attribute",
- "depends_on": "has_variants",
- "fieldname": "variant_based_on",
- "fieldtype": "Select",
- "label": "Variant Based On",
- "options": "Item Attribute\nManufacturer"
- },
- {
- "depends_on": "eval:(doc.has_variants || doc.variant_of) && doc.variant_based_on==='Item Attribute'",
- "fieldname": "attributes",
- "fieldtype": "Table",
- "hidden": 1,
- "label": "Attributes",
- "no_copy": 1,
- "options": "Item Variant Attribute"
- },
- {
- "fieldname": "defaults",
- "fieldtype": "Section Break",
- "label": "Sales, Purchase, Accounting Defaults"
- },
- {
- "fieldname": "item_defaults",
- "fieldtype": "Table",
- "label": "Item Defaults",
- "options": "Item Default"
- },
- {
- "collapsible": 1,
- "fieldname": "purchase_details",
- "fieldtype": "Section Break",
- "label": "Purchase, Replenishment Details",
- "oldfieldtype": "Section Break",
- "options": "fa fa-shopping-cart"
- },
- {
- "default": "1",
- "fieldname": "is_purchase_item",
- "fieldtype": "Check",
- "label": "Is Purchase Item"
- },
- {
- "fieldname": "purchase_uom",
- "fieldtype": "Link",
- "label": "Default Purchase Unit of Measure",
- "options": "UOM"
- },
- {
- "default": "0.00",
- "depends_on": "is_stock_item",
- "fieldname": "min_order_qty",
- "fieldtype": "Float",
- "label": "Minimum Order Qty",
- "oldfieldname": "min_order_qty",
- "oldfieldtype": "Currency"
- },
- {
- "fieldname": "safety_stock",
- "fieldtype": "Float",
- "label": "Safety Stock"
- },
- {
- "fieldname": "purchase_details_cb",
- "fieldtype": "Column Break"
- },
- {
- "description": "Average time taken by the supplier to deliver",
- "fieldname": "lead_time_days",
- "fieldtype": "Int",
- "label": "Lead Time in days",
- "oldfieldname": "lead_time_days",
- "oldfieldtype": "Int"
- },
- {
- "fieldname": "last_purchase_rate",
- "fieldtype": "Float",
- "label": "Last Purchase Rate",
- "no_copy": 1,
- "oldfieldname": "last_purchase_rate",
- "oldfieldtype": "Currency",
- "read_only": 1
- },
- {
- "default": "0",
- "fieldname": "is_customer_provided_item",
- "fieldtype": "Check",
- "label": "Is Customer Provided Item"
- },
- {
- "depends_on": "eval:doc.is_customer_provided_item==1",
- "fieldname": "customer",
- "fieldtype": "Link",
- "label": "Customer",
- "options": "Customer"
- },
- {
- "collapsible": 1,
- "fieldname": "supplier_details",
- "fieldtype": "Section Break",
- "label": "Supplier Details"
- },
- {
- "default": "0",
- "fieldname": "delivered_by_supplier",
- "fieldtype": "Check",
- "label": "Delivered by Supplier (Drop Ship)",
- "print_hide": 1
- },
- {
- "fieldname": "column_break2",
- "fieldtype": "Column Break",
- "oldfieldtype": "Column Break",
- "width": "50%"
- },
- {
- "fieldname": "supplier_items",
- "fieldtype": "Table",
- "label": "Supplier Items",
- "options": "Item Supplier"
- },
- {
- "collapsible": 1,
- "fieldname": "foreign_trade_details",
- "fieldtype": "Section Break",
- "label": "Foreign Trade Details"
- },
- {
- "fieldname": "country_of_origin",
- "fieldtype": "Link",
- "label": "Country of Origin",
- "options": "Country"
- },
- {
- "fieldname": "column_break_59",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "customs_tariff_number",
- "fieldtype": "Link",
- "label": "Customs Tariff Number",
- "options": "Customs Tariff Number"
- },
- {
- "collapsible": 1,
- "fieldname": "sales_details",
- "fieldtype": "Section Break",
- "label": "Sales Details",
- "oldfieldtype": "Section Break",
- "options": "fa fa-tag"
- },
- {
- "fieldname": "sales_uom",
- "fieldtype": "Link",
- "label": "Default Sales Unit of Measure",
- "options": "UOM"
- },
- {
- "default": "1",
- "fieldname": "is_sales_item",
- "fieldtype": "Check",
- "label": "Is Sales Item"
- },
- {
- "fieldname": "column_break3",
- "fieldtype": "Column Break",
- "oldfieldtype": "Column Break",
- "width": "50%"
- },
- {
- "fieldname": "max_discount",
- "fieldtype": "Float",
- "label": "Max Discount (%)",
- "oldfieldname": "max_discount",
- "oldfieldtype": "Currency"
- },
- {
- "collapsible": 1,
- "fieldname": "deferred_revenue",
- "fieldtype": "Section Break",
- "label": "Deferred Revenue"
- },
- {
- "depends_on": "enable_deferred_revenue",
- "fieldname": "deferred_revenue_account",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Deferred Revenue Account",
- "options": "Account"
- },
- {
- "default": "0",
- "fieldname": "enable_deferred_revenue",
- "fieldtype": "Check",
- "label": "Enable Deferred Revenue"
- },
- {
- "fieldname": "column_break_85",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "enable_deferred_revenue",
- "fieldname": "no_of_months",
- "fieldtype": "Int",
- "label": "No of Months"
- },
- {
- "collapsible": 1,
- "fieldname": "deferred_expense_section",
- "fieldtype": "Section Break",
- "label": "Deferred Expense"
- },
- {
- "depends_on": "enable_deferred_expense",
- "fieldname": "deferred_expense_account",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Deferred Expense Account",
- "options": "Account"
- },
- {
- "default": "0",
- "fieldname": "enable_deferred_expense",
- "fieldtype": "Check",
- "label": "Enable Deferred Expense"
- },
- {
- "fieldname": "column_break_88",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "enable_deferred_expense",
- "fieldname": "no_of_months_exp",
- "fieldtype": "Int",
- "label": "No of Months"
- },
- {
- "collapsible": 1,
- "fieldname": "customer_details",
- "fieldtype": "Section Break",
- "label": "Customer Details"
- },
- {
- "fieldname": "customer_items",
- "fieldtype": "Table",
- "label": "Customer Items",
- "options": "Item Customer Detail"
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "taxes",
- "fieldname": "item_tax_section_break",
- "fieldtype": "Section Break",
- "label": "Item Tax",
- "oldfieldtype": "Section Break",
- "options": "fa fa-money"
- },
- {
- "description": "Will also apply for variants",
- "fieldname": "taxes",
- "fieldtype": "Table",
- "label": "Taxes",
- "oldfieldname": "item_tax",
- "oldfieldtype": "Table",
- "options": "Item Tax"
- },
- {
- "collapsible": 1,
- "fieldname": "inspection_criteria",
- "fieldtype": "Section Break",
- "label": "Inspection Criteria",
- "oldfieldtype": "Section Break",
- "options": "fa fa-search"
- },
- {
- "default": "0",
- "fieldname": "inspection_required_before_purchase",
- "fieldtype": "Check",
- "label": "Inspection Required before Purchase",
- "oldfieldname": "inspection_required",
- "oldfieldtype": "Select"
- },
- {
- "default": "0",
- "fieldname": "inspection_required_before_delivery",
- "fieldtype": "Check",
- "label": "Inspection Required before Delivery"
- },
- {
- "depends_on": "eval:(doc.inspection_required_before_purchase || doc.inspection_required_before_delivery)",
- "fieldname": "quality_inspection_template",
- "fieldtype": "Link",
- "label": "Quality Inspection Template",
- "options": "Quality Inspection Template",
- "print_hide": 1
- },
- {
- "collapsible": 1,
- "depends_on": "is_stock_item",
- "fieldname": "manufacturing",
- "fieldtype": "Section Break",
- "label": "Manufacturing",
- "oldfieldtype": "Section Break",
- "options": "fa fa-cogs"
- },
- {
- "fieldname": "default_bom",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Default BOM",
- "no_copy": 1,
- "oldfieldname": "default_bom",
- "oldfieldtype": "Link",
- "options": "BOM",
- "read_only": 1
- },
- {
- "default": "0",
- "description": "If subcontracted to a vendor",
- "fieldname": "is_sub_contracted_item",
- "fieldtype": "Check",
- "label": "Supply Raw Materials for Purchase",
- "oldfieldname": "is_sub_contracted_item",
- "oldfieldtype": "Select"
- },
- {
- "fieldname": "column_break_74",
- "fieldtype": "Column Break"
- },
- {
- "fieldname": "customer_code",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "Customer Code",
- "no_copy": 1,
- "print_hide": 1
- },
- {
- "collapsible": 1,
- "fieldname": "website_section",
- "fieldtype": "Section Break",
- "label": "Website",
- "options": "fa fa-globe"
- },
- {
- "default": "0",
- "depends_on": "eval:!doc.variant_of",
- "fieldname": "show_in_website",
- "fieldtype": "Check",
- "label": "Show in Website",
- "search_index": 1
- },
- {
- "default": "0",
- "depends_on": "variant_of",
- "fieldname": "show_variant_in_website",
- "fieldtype": "Check",
- "label": "Show in Website (Variant)",
- "search_index": 1
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "fieldname": "route",
- "fieldtype": "Small Text",
- "label": "Route",
- "no_copy": 1
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "description": "Items with higher weightage will be shown higher",
- "fieldname": "weightage",
- "fieldtype": "Int",
- "label": "Weightage"
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "description": "Show a slideshow at the top of the page",
- "fieldname": "slideshow",
- "fieldtype": "Link",
- "label": "Slideshow",
- "options": "Website Slideshow"
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "description": "Item Image (if not slideshow)",
- "fieldname": "website_image",
- "fieldtype": "Attach",
- "label": "Website Image"
- },
- {
- "fieldname": "thumbnail",
- "fieldtype": "Data",
- "label": "Thumbnail",
- "read_only": 1
- },
- {
- "fieldname": "cb72",
- "fieldtype": "Column Break"
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "description": "Show \"In Stock\" or \"Not in Stock\" based on stock available in this warehouse.",
- "fieldname": "website_warehouse",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Website Warehouse",
- "options": "Warehouse"
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "description": "List this Item in multiple groups on the website.",
- "fieldname": "website_item_groups",
- "fieldtype": "Table",
- "label": "Website Item Groups",
- "options": "Website Item Group"
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "fieldname": "set_meta_tags",
- "fieldtype": "Button",
- "label": "Set Meta Tags"
- },
- {
- "collapsible": 1,
- "collapsible_depends_on": "website_specifications",
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "fieldname": "sb72",
- "fieldtype": "Section Break",
- "label": "Website Specifications"
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "fieldname": "copy_from_item_group",
- "fieldtype": "Button",
- "label": "Copy From Item Group"
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "fieldname": "website_specifications",
- "fieldtype": "Table",
- "label": "Website Specifications",
- "options": "Item Website Specification"
- },
- {
- "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
- "fieldname": "web_long_description",
- "fieldtype": "Text Editor",
- "label": "Website Description"
- },
- {
- "description": "You can use any valid Bootstrap 4 markup in this field. It will be shown on your Item Page.",
- "fieldname": "website_content",
- "fieldtype": "HTML Editor",
- "label": "Website Content"
- },
- {
- "fieldname": "total_projected_qty",
- "fieldtype": "Float",
- "hidden": 1,
- "label": "Total Projected Qty",
- "print_hide": 1,
- "read_only": 1
- },
- {
- "depends_on": "eval:(!doc.is_item_from_hub)",
- "fieldname": "hub_publishing_sb",
- "fieldtype": "Section Break",
- "label": "Hub Publishing Details"
- },
- {
- "default": "0",
- "description": "Publish Item to hub.erpnext.com",
- "fieldname": "publish_in_hub",
- "fieldtype": "Check",
- "label": "Publish in Hub"
- },
- {
- "fieldname": "hub_category_to_publish",
- "fieldtype": "Data",
- "label": "Hub Category to Publish",
- "read_only": 1
- },
- {
- "description": "Publish \"In Stock\" or \"Not in Stock\" on Hub based on stock available in this warehouse.",
- "fieldname": "hub_warehouse",
- "fieldtype": "Link",
- "ignore_user_permissions": 1,
- "label": "Hub Warehouse",
- "options": "Warehouse"
- },
- {
- "default": "0",
- "fieldname": "synced_with_hub",
- "fieldtype": "Check",
- "label": "Synced With Hub",
- "read_only": 1
- },
- {
- "fieldname": "manufacturers",
- "fieldtype": "Table",
- "label": "Manufacturers",
- "options": "Item Manufacturer"
- },
- {
- "depends_on": "eval:!doc.__islocal",
- "fieldname": "over_delivery_receipt_allowance",
- "fieldtype": "Float",
- "label": "Over Delivery/Receipt Allowance (%)",
- "oldfieldname": "tolerance",
- "oldfieldtype": "Currency"
- },
- {
- "fieldname": "over_billing_allowance",
- "fieldtype": "Float",
- "label": "Over Billing Allowance (%)",
- "depends_on": "eval:!doc.__islocal"
- }
- ],
- "has_web_view": 1,
- "icon": "fa fa-tag",
- "idx": 2,
- "image_field": "image",
- "max_attachments": 1,
- "modified": "2019-09-03 18:34:13.977931",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Item",
- "owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "import": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Item Manager",
- "share": 1,
- "write": 1
- },
- {
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Stock Manager"
- },
- {
- "email": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Stock User"
- },
- {
- "read": 1,
- "role": "Sales User"
- },
- {
- "read": 1,
- "role": "Purchase User"
- },
- {
- "read": 1,
- "role": "Maintenance User"
- },
- {
- "read": 1,
- "role": "Accounts User"
- },
- {
- "read": 1,
- "role": "Manufacturing User"
- }
- ],
- "quick_entry": 1,
- "search_fields": "item_name,description,item_group,customer_code",
- "show_name_in_global_search": 1,
- "show_preview_popup": 1,
- "sort_field": "idx desc,modified desc",
- "sort_order": "DESC",
- "title_field": "item_name",
- "track_changes": 1
- }
+ "allow_guest_to_view": 1,
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:item_code",
+ "creation": "2013-05-03 10:45:46",
+ "description": "A Product or a Service that is bought, sold or kept in stock.",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "name_and_description_section",
+ "naming_series",
+ "item_code",
+ "variant_of",
+ "item_name",
+ "item_group",
+ "is_item_from_hub",
+ "stock_uom",
+ "column_break0",
+ "disabled",
+ "allow_alternative_item",
+ "is_stock_item",
+ "include_item_in_manufacturing",
+ "opening_stock",
+ "valuation_rate",
+ "standard_rate",
+ "is_fixed_asset",
+ "auto_create_assets",
+ "asset_category",
+ "asset_naming_series",
+ "over_delivery_receipt_allowance",
+ "over_billing_allowance",
+ "image",
+ "section_break_11",
+ "brand",
+ "description",
+ "sb_barcodes",
+ "barcodes",
+ "inventory_section",
+ "shelf_life_in_days",
+ "end_of_life",
+ "default_material_request_type",
+ "valuation_method",
+ "column_break1",
+ "warranty_period",
+ "weight_per_unit",
+ "weight_uom",
+ "reorder_section",
+ "reorder_levels",
+ "unit_of_measure_conversion",
+ "uoms",
+ "serial_nos_and_batches",
+ "has_batch_no",
+ "create_new_batch",
+ "batch_number_series",
+ "has_expiry_date",
+ "retain_sample",
+ "sample_quantity",
+ "column_break_37",
+ "has_serial_no",
+ "serial_no_series",
+ "variants_section",
+ "has_variants",
+ "variant_based_on",
+ "attributes",
+ "defaults",
+ "item_defaults",
+ "purchase_details",
+ "is_purchase_item",
+ "purchase_uom",
+ "min_order_qty",
+ "safety_stock",
+ "purchase_details_cb",
+ "lead_time_days",
+ "last_purchase_rate",
+ "is_customer_provided_item",
+ "customer",
+ "supplier_details",
+ "delivered_by_supplier",
+ "column_break2",
+ "supplier_items",
+ "foreign_trade_details",
+ "country_of_origin",
+ "column_break_59",
+ "customs_tariff_number",
+ "sales_details",
+ "sales_uom",
+ "is_sales_item",
+ "column_break3",
+ "max_discount",
+ "deferred_revenue",
+ "deferred_revenue_account",
+ "enable_deferred_revenue",
+ "column_break_85",
+ "no_of_months",
+ "deferred_expense_section",
+ "deferred_expense_account",
+ "enable_deferred_expense",
+ "column_break_88",
+ "no_of_months_exp",
+ "customer_details",
+ "customer_items",
+ "item_tax_section_break",
+ "taxes",
+ "inspection_criteria",
+ "inspection_required_before_purchase",
+ "inspection_required_before_delivery",
+ "quality_inspection_template",
+ "manufacturing",
+ "default_bom",
+ "is_sub_contracted_item",
+ "column_break_74",
+ "customer_code",
+ "website_section",
+ "show_in_website",
+ "show_variant_in_website",
+ "route",
+ "weightage",
+ "slideshow",
+ "website_image",
+ "thumbnail",
+ "cb72",
+ "website_warehouse",
+ "website_item_groups",
+ "set_meta_tags",
+ "sb72",
+ "copy_from_item_group",
+ "website_specifications",
+ "web_long_description",
+ "website_content",
+ "total_projected_qty",
+ "hub_publishing_sb",
+ "publish_in_hub",
+ "hub_category_to_publish",
+ "hub_warehouse",
+ "synced_with_hub",
+ "manufacturers"
+ ],
+ "fields": [
+ {
+ "fieldname": "name_and_description_section",
+ "fieldtype": "Section Break",
+ "oldfieldtype": "Section Break",
+ "options": "fa fa-flag"
+ },
+ {
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "options": "STO-ITEM-.YYYY.-",
+ "set_only_once": 1
+ },
+ {
+ "bold": 1,
+ "fieldname": "item_code",
+ "fieldtype": "Data",
+ "in_global_search": 1,
+ "label": "Item Code",
+ "oldfieldname": "item_code",
+ "oldfieldtype": "Data",
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "depends_on": "variant_of",
+ "description": "If item is a variant of another item then description, image, pricing, taxes etc will be set from the template unless explicitly specified",
+ "fieldname": "variant_of",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "in_standard_filter": 1,
+ "label": "Variant Of",
+ "options": "Item",
+ "read_only": 1,
+ "search_index": 1,
+ "set_only_once": 1
+ },
+ {
+ "bold": 1,
+ "fieldname": "item_name",
+ "fieldtype": "Data",
+ "in_global_search": 1,
+ "label": "Item Name",
+ "oldfieldname": "item_name",
+ "oldfieldtype": "Data",
+ "search_index": 1
+ },
+ {
+ "fieldname": "item_group",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_preview": 1,
+ "in_standard_filter": 1,
+ "label": "Item Group",
+ "oldfieldname": "item_group",
+ "oldfieldtype": "Link",
+ "options": "Item Group",
+ "reqd": 1,
+ "search_index": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "is_item_from_hub",
+ "fieldtype": "Check",
+ "label": "Is Item from Hub",
+ "read_only": 1
+ },
+ {
+ "fieldname": "stock_uom",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Default Unit of Measure",
+ "oldfieldname": "stock_uom",
+ "oldfieldtype": "Link",
+ "options": "UOM",
+ "reqd": 1
+ },
+ {
+ "fieldname": "column_break0",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "disabled",
+ "fieldtype": "Check",
+ "label": "Disabled"
+ },
+ {
+ "default": "0",
+ "fieldname": "allow_alternative_item",
+ "fieldtype": "Check",
+ "label": "Allow Alternative Item"
+ },
+ {
+ "bold": 1,
+ "default": "1",
+ "fieldname": "is_stock_item",
+ "fieldtype": "Check",
+ "label": "Maintain Stock",
+ "oldfieldname": "is_stock_item",
+ "oldfieldtype": "Select"
+ },
+ {
+ "default": "1",
+ "fieldname": "include_item_in_manufacturing",
+ "fieldtype": "Check",
+ "label": "Include Item In Manufacturing"
+ },
+ {
+ "bold": 1,
+ "depends_on": "eval:(doc.__islocal&&doc.is_stock_item && !doc.has_serial_no && !doc.has_batch_no)",
+ "fieldname": "opening_stock",
+ "fieldtype": "Float",
+ "label": "Opening Stock"
+ },
+ {
+ "depends_on": "is_stock_item",
+ "fieldname": "valuation_rate",
+ "fieldtype": "Currency",
+ "label": "Valuation Rate"
+ },
+ {
+ "bold": 1,
+ "depends_on": "eval:doc.__islocal",
+ "fieldname": "standard_rate",
+ "fieldtype": "Currency",
+ "label": "Standard Selling Rate"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_fixed_asset",
+ "fieldtype": "Check",
+ "label": "Is Fixed Asset",
+ "set_only_once": 1
+ },
+ {
+ "depends_on": "is_fixed_asset",
+ "fieldname": "asset_category",
+ "fieldtype": "Link",
+ "label": "Asset Category",
+ "options": "Asset Category"
+ },
+ {
+ "depends_on": "is_fixed_asset",
+ "fieldname": "asset_naming_series",
+ "fieldtype": "Select",
+ "label": "Asset Naming Series"
+ },
+ {
+ "fieldname": "image",
+ "fieldtype": "Attach Image",
+ "hidden": 1,
+ "in_preview": 1,
+ "label": "Image",
+ "options": "image",
+ "print_hide": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "section_break_11",
+ "fieldtype": "Section Break",
+ "label": "Description"
+ },
+ {
+ "fieldname": "brand",
+ "fieldtype": "Link",
+ "label": "Brand",
+ "oldfieldname": "brand",
+ "oldfieldtype": "Link",
+ "options": "Brand",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "description",
+ "fieldtype": "Text Editor",
+ "in_preview": 1,
+ "label": "Description",
+ "oldfieldname": "description",
+ "oldfieldtype": "Text"
+ },
+ {
+ "fieldname": "sb_barcodes",
+ "fieldtype": "Section Break",
+ "label": "Barcodes"
+ },
+ {
+ "fieldname": "barcodes",
+ "fieldtype": "Table",
+ "label": "Barcodes",
+ "options": "Item Barcode"
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "is_stock_item",
+ "depends_on": "is_stock_item",
+ "fieldname": "inventory_section",
+ "fieldtype": "Section Break",
+ "label": "Inventory",
+ "oldfieldtype": "Section Break",
+ "options": "fa fa-truck"
+ },
+ {
+ "fieldname": "shelf_life_in_days",
+ "fieldtype": "Int",
+ "label": "Shelf Life In Days"
+ },
+ {
+ "default": "2099-12-31",
+ "depends_on": "is_stock_item",
+ "fieldname": "end_of_life",
+ "fieldtype": "Date",
+ "label": "End of Life",
+ "oldfieldname": "end_of_life",
+ "oldfieldtype": "Date"
+ },
+ {
+ "default": "Purchase",
+ "fieldname": "default_material_request_type",
+ "fieldtype": "Select",
+ "label": "Default Material Request Type",
+ "options": "Purchase\nMaterial Transfer\nMaterial Issue\nManufacture\nCustomer Provided"
+ },
+ {
+ "depends_on": "is_stock_item",
+ "fieldname": "valuation_method",
+ "fieldtype": "Select",
+ "label": "Valuation Method",
+ "options": "\nFIFO\nMoving Average",
+ "set_only_once": 1
+ },
+ {
+ "depends_on": "is_stock_item",
+ "fieldname": "column_break1",
+ "fieldtype": "Column Break",
+ "oldfieldtype": "Column Break",
+ "width": "50%"
+ },
+ {
+ "depends_on": "eval:doc.is_stock_item",
+ "fieldname": "warranty_period",
+ "fieldtype": "Data",
+ "label": "Warranty Period (in days)",
+ "oldfieldname": "warranty_period",
+ "oldfieldtype": "Data"
+ },
+ {
+ "depends_on": "is_stock_item",
+ "fieldname": "weight_per_unit",
+ "fieldtype": "Float",
+ "label": "Weight Per Unit"
+ },
+ {
+ "depends_on": "eval:doc.is_stock_item",
+ "fieldname": "weight_uom",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Weight UOM",
+ "options": "UOM"
+ },
+ {
+ "collapsible": 1,
+ "depends_on": "is_stock_item",
+ "fieldname": "reorder_section",
+ "fieldtype": "Section Break",
+ "label": "Auto re-order",
+ "options": "fa fa-rss"
+ },
+ {
+ "description": "Will also apply for variants unless overrridden",
+ "fieldname": "reorder_levels",
+ "fieldtype": "Table",
+ "label": "Reorder level based on Warehouse",
+ "options": "Item Reorder"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "unit_of_measure_conversion",
+ "fieldtype": "Section Break",
+ "label": "Units of Measure"
+ },
+ {
+ "description": "Will also apply for variants",
+ "fieldname": "uoms",
+ "fieldtype": "Table",
+ "label": "UOMs",
+ "oldfieldname": "uom_conversion_details",
+ "oldfieldtype": "Table",
+ "options": "UOM Conversion Detail"
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "eval:doc.has_batch_no || doc.has_serial_no || doc.is_fixed_asset",
+ "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
+ "fieldname": "serial_nos_and_batches",
+ "fieldtype": "Section Break",
+ "label": "Serial Nos and Batches"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.is_stock_item",
+ "fieldname": "has_batch_no",
+ "fieldtype": "Check",
+ "label": "Has Batch No",
+ "no_copy": 1,
+ "oldfieldname": "has_batch_no",
+ "oldfieldtype": "Select"
+ },
+ {
+ "default": "0",
+ "depends_on": "has_batch_no",
+ "fieldname": "create_new_batch",
+ "fieldtype": "Check",
+ "label": "Automatically Create New Batch"
+ },
+ {
+ "depends_on": "eval:doc.has_batch_no==1 && doc.create_new_batch==1",
+ "description": "Example: ABCD.#####. If series is set and Batch No is not mentioned in transactions, then automatic batch number will be created based on this series. If you always want to explicitly mention Batch No for this item, leave this blank. Note: this setting will take priority over the Naming Series Prefix in Stock Settings.",
+ "fieldname": "batch_number_series",
+ "fieldtype": "Data",
+ "label": "Batch Number Series",
+ "translatable": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "has_batch_no",
+ "fieldname": "has_expiry_date",
+ "fieldtype": "Check",
+ "label": "Has Expiry Date"
+ },
+ {
+ "default": "0",
+ "fieldname": "retain_sample",
+ "fieldtype": "Check",
+ "label": "Retain Sample"
+ },
+ {
+ "depends_on": "eval: (doc.retain_sample && doc.has_batch_no)",
+ "description": "Maximum sample quantity that can be retained",
+ "fieldname": "sample_quantity",
+ "fieldtype": "Int",
+ "label": "Max Sample Quantity"
+ },
+ {
+ "fieldname": "column_break_37",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
+ "fieldname": "has_serial_no",
+ "fieldtype": "Check",
+ "label": "Has Serial No",
+ "no_copy": 1,
+ "oldfieldname": "has_serial_no",
+ "oldfieldtype": "Select"
+ },
+ {
+ "depends_on": "eval:doc.is_stock_item || doc.is_fixed_asset",
+ "description": "Example: ABCD.#####\nIf series is set and Serial No is not mentioned in transactions, then automatic serial number will be created based on this series. If you always want to explicitly mention Serial Nos for this item. leave this blank.",
+ "fieldname": "serial_no_series",
+ "fieldtype": "Data",
+ "label": "Serial Number Series"
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "attributes",
+ "fieldname": "variants_section",
+ "fieldtype": "Section Break",
+ "label": "Variants"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:!doc.variant_of",
+ "description": "If this item has variants, then it cannot be selected in sales orders etc.",
+ "fieldname": "has_variants",
+ "fieldtype": "Check",
+ "in_standard_filter": 1,
+ "label": "Has Variants",
+ "no_copy": 1
+ },
+ {
+ "default": "Item Attribute",
+ "depends_on": "has_variants",
+ "fieldname": "variant_based_on",
+ "fieldtype": "Select",
+ "label": "Variant Based On",
+ "options": "Item Attribute\nManufacturer"
+ },
+ {
+ "depends_on": "eval:(doc.has_variants || doc.variant_of) && doc.variant_based_on==='Item Attribute'",
+ "fieldname": "attributes",
+ "fieldtype": "Table",
+ "hidden": 1,
+ "label": "Attributes",
+ "no_copy": 1,
+ "options": "Item Variant Attribute"
+ },
+ {
+ "fieldname": "defaults",
+ "fieldtype": "Section Break",
+ "label": "Sales, Purchase, Accounting Defaults"
+ },
+ {
+ "fieldname": "item_defaults",
+ "fieldtype": "Table",
+ "label": "Item Defaults",
+ "options": "Item Default"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "purchase_details",
+ "fieldtype": "Section Break",
+ "label": "Purchase, Replenishment Details",
+ "oldfieldtype": "Section Break",
+ "options": "fa fa-shopping-cart"
+ },
+ {
+ "default": "1",
+ "fieldname": "is_purchase_item",
+ "fieldtype": "Check",
+ "label": "Is Purchase Item"
+ },
+ {
+ "fieldname": "purchase_uom",
+ "fieldtype": "Link",
+ "label": "Default Purchase Unit of Measure",
+ "options": "UOM"
+ },
+ {
+ "default": "0.00",
+ "depends_on": "is_stock_item",
+ "fieldname": "min_order_qty",
+ "fieldtype": "Float",
+ "label": "Minimum Order Qty",
+ "oldfieldname": "min_order_qty",
+ "oldfieldtype": "Currency"
+ },
+ {
+ "fieldname": "safety_stock",
+ "fieldtype": "Float",
+ "label": "Safety Stock"
+ },
+ {
+ "fieldname": "purchase_details_cb",
+ "fieldtype": "Column Break"
+ },
+ {
+ "description": "Average time taken by the supplier to deliver",
+ "fieldname": "lead_time_days",
+ "fieldtype": "Int",
+ "label": "Lead Time in days",
+ "oldfieldname": "lead_time_days",
+ "oldfieldtype": "Int"
+ },
+ {
+ "fieldname": "last_purchase_rate",
+ "fieldtype": "Float",
+ "label": "Last Purchase Rate",
+ "no_copy": 1,
+ "oldfieldname": "last_purchase_rate",
+ "oldfieldtype": "Currency",
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "is_customer_provided_item",
+ "fieldtype": "Check",
+ "label": "Is Customer Provided Item"
+ },
+ {
+ "depends_on": "eval:doc.is_customer_provided_item==1",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "label": "Customer",
+ "options": "Customer"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "supplier_details",
+ "fieldtype": "Section Break",
+ "label": "Supplier Details"
+ },
+ {
+ "default": "0",
+ "fieldname": "delivered_by_supplier",
+ "fieldtype": "Check",
+ "label": "Delivered by Supplier (Drop Ship)",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "column_break2",
+ "fieldtype": "Column Break",
+ "oldfieldtype": "Column Break",
+ "width": "50%"
+ },
+ {
+ "fieldname": "supplier_items",
+ "fieldtype": "Table",
+ "label": "Supplier Items",
+ "options": "Item Supplier"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "foreign_trade_details",
+ "fieldtype": "Section Break",
+ "label": "Foreign Trade Details"
+ },
+ {
+ "fieldname": "country_of_origin",
+ "fieldtype": "Link",
+ "label": "Country of Origin",
+ "options": "Country"
+ },
+ {
+ "fieldname": "column_break_59",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "customs_tariff_number",
+ "fieldtype": "Link",
+ "label": "Customs Tariff Number",
+ "options": "Customs Tariff Number"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "sales_details",
+ "fieldtype": "Section Break",
+ "label": "Sales Details",
+ "oldfieldtype": "Section Break",
+ "options": "fa fa-tag"
+ },
+ {
+ "fieldname": "sales_uom",
+ "fieldtype": "Link",
+ "label": "Default Sales Unit of Measure",
+ "options": "UOM"
+ },
+ {
+ "default": "1",
+ "fieldname": "is_sales_item",
+ "fieldtype": "Check",
+ "label": "Is Sales Item"
+ },
+ {
+ "fieldname": "column_break3",
+ "fieldtype": "Column Break",
+ "oldfieldtype": "Column Break",
+ "width": "50%"
+ },
+ {
+ "fieldname": "max_discount",
+ "fieldtype": "Float",
+ "label": "Max Discount (%)",
+ "oldfieldname": "max_discount",
+ "oldfieldtype": "Currency"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "deferred_revenue",
+ "fieldtype": "Section Break",
+ "label": "Deferred Revenue"
+ },
+ {
+ "depends_on": "enable_deferred_revenue",
+ "fieldname": "deferred_revenue_account",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Deferred Revenue Account",
+ "options": "Account"
+ },
+ {
+ "default": "0",
+ "fieldname": "enable_deferred_revenue",
+ "fieldtype": "Check",
+ "label": "Enable Deferred Revenue"
+ },
+ {
+ "fieldname": "column_break_85",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "enable_deferred_revenue",
+ "fieldname": "no_of_months",
+ "fieldtype": "Int",
+ "label": "No of Months"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "deferred_expense_section",
+ "fieldtype": "Section Break",
+ "label": "Deferred Expense"
+ },
+ {
+ "depends_on": "enable_deferred_expense",
+ "fieldname": "deferred_expense_account",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Deferred Expense Account",
+ "options": "Account"
+ },
+ {
+ "default": "0",
+ "fieldname": "enable_deferred_expense",
+ "fieldtype": "Check",
+ "label": "Enable Deferred Expense"
+ },
+ {
+ "fieldname": "column_break_88",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "enable_deferred_expense",
+ "fieldname": "no_of_months_exp",
+ "fieldtype": "Int",
+ "label": "No of Months"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "customer_details",
+ "fieldtype": "Section Break",
+ "label": "Customer Details"
+ },
+ {
+ "fieldname": "customer_items",
+ "fieldtype": "Table",
+ "label": "Customer Items",
+ "options": "Item Customer Detail"
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "taxes",
+ "fieldname": "item_tax_section_break",
+ "fieldtype": "Section Break",
+ "label": "Item Tax",
+ "oldfieldtype": "Section Break",
+ "options": "fa fa-money"
+ },
+ {
+ "description": "Will also apply for variants",
+ "fieldname": "taxes",
+ "fieldtype": "Table",
+ "label": "Taxes",
+ "oldfieldname": "item_tax",
+ "oldfieldtype": "Table",
+ "options": "Item Tax"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "inspection_criteria",
+ "fieldtype": "Section Break",
+ "label": "Inspection Criteria",
+ "oldfieldtype": "Section Break",
+ "options": "fa fa-search"
+ },
+ {
+ "default": "0",
+ "fieldname": "inspection_required_before_purchase",
+ "fieldtype": "Check",
+ "label": "Inspection Required before Purchase",
+ "oldfieldname": "inspection_required",
+ "oldfieldtype": "Select"
+ },
+ {
+ "default": "0",
+ "fieldname": "inspection_required_before_delivery",
+ "fieldtype": "Check",
+ "label": "Inspection Required before Delivery"
+ },
+ {
+ "depends_on": "eval:(doc.inspection_required_before_purchase || doc.inspection_required_before_delivery)",
+ "fieldname": "quality_inspection_template",
+ "fieldtype": "Link",
+ "label": "Quality Inspection Template",
+ "options": "Quality Inspection Template",
+ "print_hide": 1
+ },
+ {
+ "collapsible": 1,
+ "depends_on": "is_stock_item",
+ "fieldname": "manufacturing",
+ "fieldtype": "Section Break",
+ "label": "Manufacturing",
+ "oldfieldtype": "Section Break",
+ "options": "fa fa-cogs"
+ },
+ {
+ "fieldname": "default_bom",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Default BOM",
+ "no_copy": 1,
+ "oldfieldname": "default_bom",
+ "oldfieldtype": "Link",
+ "options": "BOM",
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "description": "If subcontracted to a vendor",
+ "fieldname": "is_sub_contracted_item",
+ "fieldtype": "Check",
+ "label": "Supply Raw Materials for Purchase",
+ "oldfieldname": "is_sub_contracted_item",
+ "oldfieldtype": "Select"
+ },
+ {
+ "fieldname": "column_break_74",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "customer_code",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Customer Code",
+ "no_copy": 1,
+ "print_hide": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "website_section",
+ "fieldtype": "Section Break",
+ "label": "Website",
+ "options": "fa fa-globe"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:!doc.variant_of",
+ "fieldname": "show_in_website",
+ "fieldtype": "Check",
+ "label": "Show in Website",
+ "search_index": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "variant_of",
+ "fieldname": "show_variant_in_website",
+ "fieldtype": "Check",
+ "label": "Show in Website (Variant)",
+ "search_index": 1
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "fieldname": "route",
+ "fieldtype": "Small Text",
+ "label": "Route",
+ "no_copy": 1
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "description": "Items with higher weightage will be shown higher",
+ "fieldname": "weightage",
+ "fieldtype": "Int",
+ "label": "Weightage"
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "description": "Show a slideshow at the top of the page",
+ "fieldname": "slideshow",
+ "fieldtype": "Link",
+ "label": "Slideshow",
+ "options": "Website Slideshow"
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "description": "Item Image (if not slideshow)",
+ "fieldname": "website_image",
+ "fieldtype": "Attach",
+ "label": "Website Image"
+ },
+ {
+ "fieldname": "thumbnail",
+ "fieldtype": "Data",
+ "label": "Thumbnail",
+ "read_only": 1
+ },
+ {
+ "fieldname": "cb72",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "description": "Show \"In Stock\" or \"Not in Stock\" based on stock available in this warehouse.",
+ "fieldname": "website_warehouse",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Website Warehouse",
+ "options": "Warehouse"
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "description": "List this Item in multiple groups on the website.",
+ "fieldname": "website_item_groups",
+ "fieldtype": "Table",
+ "label": "Website Item Groups",
+ "options": "Website Item Group"
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "fieldname": "set_meta_tags",
+ "fieldtype": "Button",
+ "label": "Set Meta Tags"
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "website_specifications",
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "fieldname": "sb72",
+ "fieldtype": "Section Break",
+ "label": "Website Specifications"
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "fieldname": "copy_from_item_group",
+ "fieldtype": "Button",
+ "label": "Copy From Item Group"
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "fieldname": "website_specifications",
+ "fieldtype": "Table",
+ "label": "Website Specifications",
+ "options": "Item Website Specification"
+ },
+ {
+ "depends_on": "eval: doc.show_in_website || doc.show_variant_in_website",
+ "fieldname": "web_long_description",
+ "fieldtype": "Text Editor",
+ "label": "Website Description"
+ },
+ {
+ "description": "You can use any valid Bootstrap 4 markup in this field. It will be shown on your Item Page.",
+ "fieldname": "website_content",
+ "fieldtype": "HTML Editor",
+ "label": "Website Content"
+ },
+ {
+ "fieldname": "total_projected_qty",
+ "fieldtype": "Float",
+ "hidden": 1,
+ "label": "Total Projected Qty",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "depends_on": "eval:(!doc.is_item_from_hub)",
+ "fieldname": "hub_publishing_sb",
+ "fieldtype": "Section Break",
+ "label": "Hub Publishing Details"
+ },
+ {
+ "default": "0",
+ "description": "Publish Item to hub.erpnext.com",
+ "fieldname": "publish_in_hub",
+ "fieldtype": "Check",
+ "label": "Publish in Hub"
+ },
+ {
+ "fieldname": "hub_category_to_publish",
+ "fieldtype": "Data",
+ "label": "Hub Category to Publish",
+ "read_only": 1
+ },
+ {
+ "description": "Publish \"In Stock\" or \"Not in Stock\" on Hub based on stock available in this warehouse.",
+ "fieldname": "hub_warehouse",
+ "fieldtype": "Link",
+ "ignore_user_permissions": 1,
+ "label": "Hub Warehouse",
+ "options": "Warehouse"
+ },
+ {
+ "default": "0",
+ "fieldname": "synced_with_hub",
+ "fieldtype": "Check",
+ "label": "Synced With Hub",
+ "read_only": 1
+ },
+ {
+ "fieldname": "manufacturers",
+ "fieldtype": "Table",
+ "label": "Manufacturers",
+ "options": "Item Manufacturer"
+ },
+ {
+ "depends_on": "eval:!doc.__islocal",
+ "fieldname": "over_delivery_receipt_allowance",
+ "fieldtype": "Float",
+ "label": "Over Delivery/Receipt Allowance (%)",
+ "oldfieldname": "tolerance",
+ "oldfieldtype": "Currency"
+ },
+ {
+ "depends_on": "eval:!doc.__islocal",
+ "fieldname": "over_billing_allowance",
+ "fieldtype": "Float",
+ "label": "Over Billing Allowance (%)"
+ },
+ {
+ "default": "0",
+ "depends_on": "is_fixed_asset",
+ "fieldname": "auto_create_assets",
+ "fieldtype": "Check",
+ "label": "Auto Create Assets on Purchase"
+ }
+ ],
+ "has_web_view": 1,
+ "icon": "fa fa-tag",
+ "idx": 2,
+ "image_field": "image",
+ "max_attachments": 1,
+ "modified": "2019-10-09 17:05:59.576119",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Item",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "import": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Item Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Stock Manager"
+ },
+ {
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Stock User"
+ },
+ {
+ "read": 1,
+ "role": "Sales User"
+ },
+ {
+ "read": 1,
+ "role": "Purchase User"
+ },
+ {
+ "read": 1,
+ "role": "Maintenance User"
+ },
+ {
+ "read": 1,
+ "role": "Accounts User"
+ },
+ {
+ "read": 1,
+ "role": "Manufacturing User"
+ }
+ ],
+ "quick_entry": 1,
+ "search_fields": "item_name,description,item_group,customer_code",
+ "show_name_in_global_search": 1,
+ "show_preview_popup": 1,
+ "sort_field": "idx desc,modified desc",
+ "sort_order": "DESC",
+ "title_field": "item_name",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
index 66c33a1..90a392c 100644
--- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
+++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json
@@ -13,6 +13,7 @@
"qty",
"rate",
"amount",
+ "is_fixed_asset",
"applicable_charges",
"purchase_receipt_item",
"accounting_dimensions_section",
@@ -119,14 +120,25 @@
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fetch_from": "item_code.is_fixed_asset",
+ "fieldname": "is_fixed_asset",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Is Fixed Asset",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
- "modified": "2019-05-26 09:48:15.569956",
+ "modified": "2019-11-12 15:41:21.053462",
"modified_by": "Administrator",
"module": "Stock",
"name": "Landed Cost Item",
"owner": "wasim@webnotestech.com",
- "permissions": []
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
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 1aaf73f..0cc243d 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 edc3444..5de1352 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 @@
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 @@
<table class="table table-bordered" style="background-color: #f9f9f9;">
<tr><td>
<h4>
- <i class="fa fa-hand-right"></i>
+ <i class="fa fa-hand-right"></i>
${__("Notes")}:
</h4>
<ul>
@@ -96,7 +106,7 @@
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 @@
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
});
@@ -119,6 +129,10 @@
},
distribute_charges_based_on: function (frm) {
this.set_applicable_charges_for_item();
+ },
+
+ items_remove: () => {
+ this.trigger('set_applicable_charges_for_item');
}
});
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
index c2c6692..46fdc8f 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.json
@@ -1,545 +1,149 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "naming_series:",
- "beta": 0,
- "creation": "2014-07-11 11:33:42.547339",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Document",
- "editable_grid": 0,
- "engine": "InnoDB",
+ "autoname": "naming_series:",
+ "creation": "2014-07-11 11:33:42.547339",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "engine": "InnoDB",
+ "field_order": [
+ "naming_series",
+ "company",
+ "purchase_receipts",
+ "sec_break1",
+ "taxes",
+ "purchase_receipt_items",
+ "get_items_from_purchase_receipts",
+ "items",
+ "section_break_9",
+ "total_taxes_and_charges",
+ "col_break1",
+ "distribute_charges_based_on",
+ "amended_from",
+ "sec_break2",
+ "landed_cost_help"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "naming_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": "Series",
- "length": 0,
- "no_copy": 1,
- "options": "MAT-LCV-.YYYY.-",
- "permlevel": 0,
- "precision": "",
- "print_hide": 1,
- "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": 1,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "no_copy": 1,
+ "options": "MAT-LCV-.YYYY.-",
+ "print_hide": 1,
+ "reqd": 1,
+ "set_only_once": 1
+ },
{
- "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": 1,
- "in_standard_filter": 1,
- "label": "Company",
- "length": 0,
- "no_copy": 0,
- "options": "Company",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 1,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Company",
+ "options": "Company",
+ "remember_last_selected_value": 1,
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "purchase_receipts",
- "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": "Purchase Receipts",
- "length": 0,
- "no_copy": 0,
- "options": "Landed Cost Purchase Receipt",
- "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,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "purchase_receipts",
+ "fieldtype": "Table",
+ "label": "Purchase Receipts",
+ "options": "Landed Cost Purchase Receipt",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "purchase_receipt_items",
- "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": "Purchase Receipt Items",
- "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": "purchase_receipt_items",
+ "fieldtype": "Section Break",
+ "label": "Purchase Receipt Items"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "get_items_from_purchase_receipts",
- "fieldtype": "Button",
- "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": "Get Items From Purchase Receipts",
- "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,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "get_items_from_purchase_receipts",
+ "fieldtype": "Button",
+ "label": "Get Items From Purchase Receipts"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "items",
- "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": "Purchase Receipt Items",
- "length": 0,
- "no_copy": 1,
- "options": "Landed Cost Item",
- "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,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "items",
+ "fieldtype": "Table",
+ "label": "Purchase Receipt Items",
+ "no_copy": 1,
+ "options": "Landed Cost Item",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sec_break1",
- "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": "Additional Charges",
- "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": "sec_break1",
+ "fieldtype": "Section Break",
+ "label": "Applicable Charges"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "taxes",
- "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": "Taxes and Charges",
- "length": 0,
- "no_copy": 0,
- "options": "Landed Cost Taxes and Charges",
- "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,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "taxes",
+ "fieldtype": "Table",
+ "label": "Taxes and Charges",
+ "options": "Landed Cost Taxes and Charges",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_9",
- "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_9",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "total_taxes_and_charges",
- "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": "Total Taxes and Charges",
- "length": 0,
- "no_copy": 0,
- "options": "Company:company:default_currency",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "total_taxes_and_charges",
+ "fieldtype": "Currency",
+ "label": "Total Taxes and Charges",
+ "options": "Company:company:default_currency",
+ "read_only": 1,
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "col_break1",
- "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": "col_break1",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "distribute_charges_based_on",
- "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": "Distribute Charges Based On",
- "length": 0,
- "no_copy": 0,
- "options": "\nQty\nAmount",
- "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": "distribute_charges_based_on",
+ "fieldtype": "Select",
+ "label": "Distribute Charges Based On",
+ "options": "Qty\nAmount",
+ "reqd": 1
+ },
{
- "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": "Landed Cost Voucher",
- "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": "Landed Cost Voucher",
+ "print_hide": 1,
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sec_break2",
- "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": "sec_break2",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "landed_cost_help",
- "fieldtype": "HTML",
- "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": "Landed Cost Help",
- "length": 0,
- "no_copy": 0,
- "options": "",
- "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,
- "translatable": 0,
- "unique": 0
+ "fieldname": "landed_cost_help",
+ "fieldtype": "HTML",
+ "label": "Landed Cost Help"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "icon-usd",
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 1,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-08-21 14:44:30.850736",
- "modified_by": "Administrator",
- "module": "Stock",
- "name": "Landed Cost Voucher",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "icon": "icon-usd",
+ "is_submittable": 1,
+ "modified": "2019-10-09 13:39:36.082777",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Landed Cost Voucher",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 1,
- "cancel": 1,
- "create": 1,
- "delete": 1,
- "email": 0,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 0,
- "read": 1,
- "report": 1,
- "role": "Stock Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 1,
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "export": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Stock Manager",
+ "share": 1,
+ "submit": 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,
- "track_views": 0
+ ],
+ "show_name_in_global_search": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC"
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 3f37093..173b394 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -16,16 +16,13 @@
if pr.receipt_document_type and pr.receipt_document:
pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description,
pr_item.qty, pr_item.base_rate, pr_item.base_amount, pr_item.name,
- pr_item.cost_center, pr_item.asset
+ pr_item.cost_center, pr_item.is_fixed_asset
from `tab{doctype} Item` pr_item where parent = %s
and exists(select name from tabItem
where name = pr_item.item_code and (is_stock_item = 1 or is_fixed_asset=1))
""".format(doctype=pr.receipt_document_type), pr.receipt_document, as_dict=True)
for d in pr_items:
- if d.asset and frappe.db.get_value("Asset", d.asset, 'docstatus') == 1:
- continue
-
item = self.append("items")
item.item_code = d.item_code
item.description = d.description
@@ -37,15 +34,16 @@
item.receipt_document_type = pr.receipt_document_type
item.receipt_document = pr.receipt_document
item.purchase_receipt_item = d.name
+ item.is_fixed_asset = d.is_fixed_asset
def validate(self):
self.check_mandatory()
- self.validate_purchase_receipts()
- self.set_total_taxes_and_charges()
if not self.get("items"):
self.get_items_from_purchase_receipts()
else:
self.validate_applicable_charges_for_item()
+ self.validate_purchase_receipts()
+ self.set_total_taxes_and_charges()
def check_mandatory(self):
if not self.get("purchase_receipts"):
@@ -64,6 +62,7 @@
for item in self.get("items"):
if not item.receipt_document:
frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button"))
+
elif item.receipt_document not in receipt_documents:
frappe.throw(_("Item Row {0}: {1} {2} does not exist in above '{1}' table")
.format(item.idx, item.receipt_document_type, item.receipt_document))
@@ -96,8 +95,6 @@
else:
frappe.throw(_("Total Applicable Charges in Purchase Receipt Items table must be same as Total Taxes and Charges"))
-
-
def on_submit(self):
self.update_landed_cost()
@@ -107,6 +104,9 @@
def update_landed_cost(self):
for d in self.get("purchase_receipts"):
doc = frappe.get_doc(d.receipt_document_type, d.receipt_document)
+
+ # check if there are {qty} assets created and linked to this receipt document
+ self.validate_asset_qty_and_status(d.receipt_document_type, doc)
# set landed cost voucher amount in pr item
doc.set_landed_cost_voucher_amount()
@@ -118,23 +118,42 @@
for item in doc.get("items"):
item.db_update()
+ # asset rate will be updated while creating asset gl entries from PI or PY
+
# update latest valuation rate in serial no
- self.update_rate_in_serial_no(doc)
+ self.update_rate_in_serial_no_for_non_asset_items(doc)
# update stock & gl entries for cancelled state of PR
doc.docstatus = 2
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
doc.make_gl_entries_on_cancel(repost_future_gle=False)
-
# update stock & gl entries for submit state of PR
doc.docstatus = 1
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
doc.make_gl_entries()
- def update_rate_in_serial_no(self, receipt_document):
+ def validate_asset_qty_and_status(self, receipt_document_type, receipt_document):
+ for item in self.get('items'):
+ if item.is_fixed_asset:
+ receipt_document_type = 'purchase_invoice' if item.receipt_document_type == 'Purchase Invoice' \
+ else 'purchase_receipt'
+ docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document },
+ fields=['name', 'docstatus'])
+ if not docs or len(docs) != item.qty:
+ frappe.throw(_('There are not enough asset created or linked to {0}. \
+ Please create or link {1} Assets with respective document.').format(item.receipt_document, item.qty))
+ if docs:
+ for d in docs:
+ if d.docstatus == 1:
+ frappe.throw(_('{2} <b>{0}</b> has submitted Assets.\
+ Remove Item <b>{1}</b> from table to continue.').format(
+ item.receipt_document, item.item_code, item.receipt_document_type)
+ )
+
+ def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
for item in receipt_document.get("items"):
- if item.serial_no:
+ if not item.is_fixed_asset and item.serial_no:
serial_nos = get_serial_nos(item.serial_no)
if serial_nos:
frappe.db.sql("update `tabSerial No` set purchase_rate=%s where name in ({0})"
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 4dc0b7b..988cf52 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
@@ -54,9 +54,10 @@
expected_values = {
stock_in_hand_account: [800.0, 0.0],
"Stock Received But Not Billed - TCP1": [0.0, 500.0],
- "Expenses Included In Valuation - TCP1": [0.0, 300.0]
+ "Expenses Included In Valuation - TCP1": [0.0, 50.0],
+ "_Test Account Customs Duty - TCP1": [0.0, 150],
+ "_Test Account Shipping Charges - TCP1": [0.0, 100.00]
}
-
else:
expected_values = {
stock_in_hand_account: [400.0, 0.0],
@@ -179,7 +180,7 @@
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 3f66743..2789711 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -173,8 +173,10 @@
});
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.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index aef53ed..d5914f9 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -6,27 +6,35 @@
frappe.provide("erpnext.stock");
frappe.ui.form.on("Purchase Receipt", {
- setup: function(frm) {
+ setup: (frm) => {
+ frm.make_methods = {
+ 'Landed Cost Voucher': () => {
+ let lcv = frappe.model.get_new_doc('Landed Cost Voucher');
+ lcv.company = frm.doc.company;
+
+ let lcv_receipt = frappe.model.get_new_doc('Landed Cost Purchase Receipt');
+ lcv_receipt.receipt_document_type = 'Purchase Receipt';
+ lcv_receipt.receipt_document = frm.doc.name;
+ lcv_receipt.supplier = frm.doc.supplier;
+ lcv_receipt.grand_total = frm.doc.grand_total;
+ lcv.purchase_receipts = [lcv_receipt];
+
+ frappe.set_route("Form", lcv.doctype, lcv.name);
+ },
+ }
+
frm.custom_make_buttons = {
'Stock Entry': 'Return',
'Purchase Invoice': 'Invoice'
};
- frm.set_query("asset", "items", function() {
- return {
- filters: {
- "purchase_receipt": frm.doc.name
- }
- }
- });
-
frm.set_query("expense_account", "items", function() {
return {
query: "erpnext.controllers.queries.get_expense_account",
- filters: {'company': frm.doc.company}
+ filters: {'company': frm.doc.company }
}
});
-
+
},
onload: function(frm) {
erpnext.queries.setup_queries(frm, "Warehouse", function() {
@@ -57,7 +65,7 @@
toggle_display_account_head: function(frm) {
var enabled = erpnext.is_perpetual_inventory_enabled(frm.doc.company)
frm.fields_dict["items"].grid.set_column_disp(["cost_center"], enabled);
- },
+ }
});
erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index fae4de3..0cb21d7 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -14,7 +14,7 @@
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 @@
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 @@
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):
@@ -279,15 +281,15 @@
d.rejected_warehouse not in warehouse_with_no_account:
warehouse_with_no_account.append(d.warehouse)
- self.get_asset_gl_entry(gl_entries, expenses_included_in_valuation)
+ self.get_asset_gl_entry(gl_entries)
# Cost center-wise amount breakup for other charges included for valuation
valuation_tax = {}
for tax in self.get("taxes"):
if tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
if not tax.cost_center:
frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category)))
- valuation_tax.setdefault(tax.cost_center, 0)
- valuation_tax[tax.cost_center] += \
+ valuation_tax.setdefault(tax.name, 0)
+ valuation_tax[tax.name] += \
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
if negative_expense_to_be_booked and valuation_tax:
@@ -295,37 +297,42 @@
# If expenses_included_in_valuation account has been credited in against PI
# and charges added via Landed Cost Voucher,
# post valuation related charges on "Stock Received But Not Billed"
+ # introduced in 2014 for backward compatibility of expenses already booked in expenses_included_in_valuation account
negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi
where docstatus = 1 and purchase_receipt=%s
and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice'
and voucher_no=pi.parent and account=%s)""", (self.name, expenses_included_in_valuation))
- if negative_expense_booked_in_pi:
- expenses_included_in_valuation = stock_rbnb
-
against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
total_valuation_amount = sum(valuation_tax.values())
amount_including_divisional_loss = negative_expense_to_be_booked
i = 1
- for cost_center, amount in iteritems(valuation_tax):
- if i == len(valuation_tax):
- applicable_amount = amount_including_divisional_loss
- else:
- applicable_amount = negative_expense_to_be_booked * (amount / total_valuation_amount)
- amount_including_divisional_loss -= applicable_amount
+ for tax in self.get("taxes"):
+ if valuation_tax.get(tax.name):
- gl_entries.append(
- self.get_gl_dict({
- "account": expenses_included_in_valuation,
- "cost_center": cost_center,
- "credit": applicable_amount,
- "remarks": self.remarks or _("Accounting Entry for Stock"),
- "against": against_account
- })
- )
+ if negative_expense_booked_in_pi:
+ account = stock_rbnb
+ else:
+ account = tax.account_head
- i += 1
+ if i == len(valuation_tax):
+ applicable_amount = amount_including_divisional_loss
+ else:
+ applicable_amount = negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount)
+ amount_including_divisional_loss -= applicable_amount
+
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": account,
+ "cost_center": tax.cost_center,
+ "credit": applicable_amount,
+ "remarks": self.remarks or _("Accounting Entry for Stock"),
+ "against": against_account
+ }, item=tax)
+ )
+
+ i += 1
if warehouse_with_no_account:
frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
@@ -333,81 +340,85 @@
return process_gl_map(gl_entries)
- 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"):
- if d.is_fixed_asset and not (arbnb_account and cwip_account):
- arbnb_account = self.get_company_default("asset_received_but_not_billed")
-
- # CWIP entry
- cwip_account = get_asset_account("capital_work_in_progress_account", d.asset,
- company = self.company)
-
- if d.is_fixed_asset and not cwip_disabled:
-
- 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)
-
- cwip_account_currency = get_account_currency(cwip_account)
- gl_entries.append(self.get_gl_dict({
- "account": cwip_account,
- "against": arbnb_account,
- "cost_center": d.cost_center,
- "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
- "debit": base_asset_amount,
- "debit_in_account_currency": (base_asset_amount
- if cwip_account_currency == self.company_currency else asset_amount)
- }, item=d))
-
- # Asset received but not billed
- asset_rbnb_currency = get_account_currency(arbnb_account)
- gl_entries.append(self.get_gl_dict({
- "account": arbnb_account,
- "against": cwip_account,
- "cost_center": d.cost_center,
- "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
- "credit": base_asset_amount,
- "credit_in_account_currency": (base_asset_amount
- if asset_rbnb_currency == self.company_currency else asset_amount)
- }, item=d))
-
- 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)
-
- gl_entries.append(self.get_gl_dict({
- "account": expenses_included_in_valuation,
- "against": asset_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))
-
- gl_entries.append(self.get_gl_dict({
- "account": asset_account,
- "against": expenses_included_in_valuation,
- "cost_center": d.cost_center,
- "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "debit": flt(d.landed_cost_voucher_amount),
- "project": d.project
- }, item=d))
-
- if d.asset:
- doc = frappe.get_doc("Asset", d.asset)
- frappe.db.set_value("Asset", d.asset, "gross_purchase_amount",
- doc.gross_purchase_amount + flt(d.landed_cost_voucher_amount))
-
- frappe.db.set_value("Asset", d.asset, "purchase_receipt_amount",
- doc.purchase_receipt_amount + flt(d.landed_cost_voucher_amount))
-
+ def get_asset_gl_entry(self, gl_entries):
+ for item in self.get("items"):
+ if item.is_fixed_asset:
+ if is_cwip_accounting_enabled(self.company, item.asset_category):
+ self.add_asset_gl_entries(item, gl_entries)
+ if flt(item.landed_cost_voucher_amount):
+ self.add_lcv_gl_entries(item, gl_entries)
+ # update assets gross amount by its valuation rate
+ # valuation rate is total of net rate, raw mat supp cost, tax amount, lcv amount per item
+ self.update_assets(item, item.valuation_rate)
return gl_entries
+
+ def add_asset_gl_entries(self, item, gl_entries):
+ arbnb_account = self.get_company_default("asset_received_but_not_billed")
+ # This returns company's default cwip account
+ cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company)
+
+ asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
+ base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
+
+ cwip_account_currency = get_account_currency(cwip_account)
+ # debit cwip account
+ gl_entries.append(self.get_gl_dict({
+ "account": cwip_account,
+ "against": arbnb_account,
+ "cost_center": item.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+ "debit": base_asset_amount,
+ "debit_in_account_currency": (base_asset_amount
+ if cwip_account_currency == self.company_currency else asset_amount)
+ }, item=item))
+
+ asset_rbnb_currency = get_account_currency(arbnb_account)
+ # credit arbnb account
+ gl_entries.append(self.get_gl_dict({
+ "account": arbnb_account,
+ "against": cwip_account,
+ "cost_center": item.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+ "credit": base_asset_amount,
+ "credit_in_account_currency": (base_asset_amount
+ if asset_rbnb_currency == self.company_currency else asset_amount)
+ }, item=item))
+
+ def add_lcv_gl_entries(self, item, gl_entries):
+ expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
+ if not is_cwip_accounting_enabled(self.company, item.asset_category):
+ asset_account = get_asset_category_account(asset_category=item.asset_category, \
+ fieldname='fixed_asset_account', company=self.company)
+ else:
+ # This returns company's default cwip account
+ asset_account = get_asset_account("capital_work_in_progress_account", company=self.company)
+
+ gl_entries.append(self.get_gl_dict({
+ "account": expenses_included_in_asset_valuation,
+ "against": asset_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))
+
+ gl_entries.append(self.get_gl_dict({
+ "account": asset_account,
+ "against": expenses_included_in_asset_valuation,
+ "cost_center": item.cost_center,
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+ "debit": flt(item.landed_cost_voucher_amount),
+ "project": item.project
+ }, item=item))
+
+ def update_assets(self, item, valuation_rate):
+ assets = frappe.db.get_all('Asset',
+ filters={ 'purchase_receipt': self.name, 'item_code': item.item_code }
+ )
+
+ for asset in assets:
+ frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(valuation_rate))
+ frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(valuation_rate))
def update_status(self, status):
self.set_status(update=True, status = status)
@@ -515,7 +526,8 @@
"purchase_order_item": "po_detail",
"purchase_order": "purchase_order",
"is_fixed_asset": "is_fixed_asset",
- "asset": "asset",
+ "asset_location": "asset_location",
+ "asset_category": 'asset_category'
},
"postprocess": update_item,
"filter": lambda d: get_pending_qty(d)[0] <= 0 if not doc.get("is_return") else get_pending_qty(d)[0] > 0
@@ -584,3 +596,30 @@
}, 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/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index e9ddf9d..c80b9bd 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -66,14 +66,15 @@
expected_values = {
stock_in_hand_account: [750.0, 0.0],
"Stock Received But Not Billed - TCP1": [0.0, 500.0],
- "Expenses Included In Valuation - TCP1": [0.0, 250.0]
+ "_Test Account Shipping Charges - TCP1": [0.0, 100.0],
+ "_Test Account Customs Duty - TCP1": [0.0, 150.0]
}
else:
expected_values = {
stock_in_hand_account: [375.0, 0.0],
fixed_asset_account: [375.0, 0.0],
"Stock Received But Not Billed - TCP1": [0.0, 500.0],
- "Expenses Included In Valuation - TCP1": [0.0, 250.0]
+ "_Test Account Shipping Charges - TCP1": [0.0, 250.0]
}
for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.debit)
@@ -281,8 +282,8 @@
serial_no=serial_no, basic_rate=100, do_not_submit=True)
self.assertRaises(SerialNoDuplicateError, se.submit)
- def test_serialized_asset_item(self):
- asset_item = "Test Serialized Asset Item"
+ def test_auto_asset_creation(self):
+ asset_item = "Test Asset Item"
if not frappe.db.exists('Item', asset_item):
asset_category = frappe.get_all('Asset Category')
@@ -308,30 +309,18 @@
asset_category = doc.name
item_data = make_item(asset_item, {'is_stock_item':0,
- 'stock_uom': 'Box', 'is_fixed_asset': 1, 'has_serial_no': 1,
- 'asset_category': asset_category, 'serial_no_series': 'ABC.###'})
+ 'stock_uom': 'Box', 'is_fixed_asset': 1, 'auto_create_assets': 1,
+ 'asset_category': asset_category, 'asset_naming_series': 'ABC.###'})
asset_item = item_data.item_code
pr = make_purchase_receipt(item_code=asset_item, qty=3)
- asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name}, 'name')
- asset_movement = frappe.db.get_value('Asset Movement', {'reference_name': pr.name}, 'name')
- serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name')
+ assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name})
- self.assertEquals(len(serial_nos), 3)
+ self.assertEquals(len(assets), 3)
- location = frappe.db.get_value('Serial No', serial_nos[0].name, 'location')
+ location = frappe.db.get_value('Asset', assets[0].name, 'location')
self.assertEquals(location, "Test Location")
- frappe.db.set_value("Asset", asset, "purchase_receipt", "")
- frappe.db.set_value("Purchase Receipt Item", pr.items[0].name, "asset", "")
-
- pr.load_from_db()
-
- pr.cancel()
- serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or []
- self.assertEquals(len(serial_nos), 0)
- frappe.db.sql("delete from `tabAsset`")
-
def test_purchase_receipt_for_enable_allow_cost_center_in_entry_of_bs_account(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
@@ -534,8 +523,10 @@
received_qty = args.received_qty or qty
rejected_qty = args.rejected_qty or flt(received_qty) - flt(qty)
+ item_code = args.item or args.item_code or "_Test Item"
+ uom = args.uom or frappe.db.get_value("Item", item_code, "stock_uom") or "_Test UOM"
pr.append("items", {
- "item_code": args.item or args.item_code or "_Test Item",
+ "item_code": item_code,
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"qty": qty,
"received_qty": received_qty,
@@ -545,7 +536,7 @@
"conversion_factor": args.conversion_factor or 1.0,
"serial_no": args.serial_no,
"stock_uom": args.stock_uom or "_Test UOM",
- "uom": args.uom or "_Test UOM",
+ "uom": uom,
"cost_center": args.cost_center or frappe.get_cached_value('Company', pr.company, 'cost_center'),
"asset_location": args.location or "Test Location"
})
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 446a488..16ec8db 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -67,26 +67,26 @@
"warehouse_and_reference",
"warehouse",
"rejected_warehouse",
- "quality_inspection",
"purchase_order",
"material_request",
- "purchase_order_item",
- "material_request_item",
"column_break_40",
"is_fixed_asset",
- "asset",
"asset_location",
+ "asset_category",
"schedule_date",
+ "quality_inspection",
"stock_qty",
+ "purchase_order_item",
+ "material_request_item",
"section_break_45",
+ "allow_zero_valuation_rate",
+ "bom",
+ "col_break5",
"serial_no",
"batch_no",
"column_break_48",
"rejected_serial_no",
"expense_account",
- "col_break5",
- "allow_zero_valuation_rate",
- "bom",
"include_exploded_items",
"item_tax_rate",
"accounting_dimensions_section",
@@ -501,21 +501,6 @@
"read_only": 1
},
{
- "depends_on": "is_fixed_asset",
- "fieldname": "asset",
- "fieldtype": "Link",
- "label": "Asset",
- "no_copy": 1,
- "options": "Asset"
- },
- {
- "depends_on": "is_fixed_asset",
- "fieldname": "asset_location",
- "fieldtype": "Link",
- "label": "Asset Location",
- "options": "Location"
- },
- {
"fieldname": "purchase_order",
"fieldtype": "Link",
"label": "Purchase Order",
@@ -553,6 +538,7 @@
"fieldtype": "Section Break"
},
{
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "serial_no",
"fieldtype": "Small Text",
"in_list_view": 1,
@@ -562,10 +548,11 @@
"oldfieldtype": "Text"
},
{
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "batch_no",
"fieldtype": "Link",
"in_list_view": 1,
- "label": "Batch No",
+ "label": "Batch No!",
"no_copy": 1,
"oldfieldname": "batch_no",
"oldfieldtype": "Link",
@@ -577,6 +564,7 @@
"fieldtype": "Column Break"
},
{
+ "depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "rejected_serial_no",
"fieldtype": "Small Text",
"label": "Rejected Serial No",
@@ -814,11 +802,28 @@
"fieldtype": "Data",
"label": "Manufacturer Part Number",
"read_only": 1
+ },
+ {
+ "depends_on": "is_fixed_asset",
+ "fieldname": "asset_location",
+ "fieldtype": "Link",
+ "label": "Asset Location",
+ "options": "Location"
+ },
+ {
+ "depends_on": "is_fixed_asset",
+ "fetch_from": "item_code.asset_category",
+ "fieldname": "asset_category",
+ "fieldtype": "Link",
+ "in_preview": 1,
+ "label": "Asset Category",
+ "options": "Asset Category",
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
- "modified": "2019-09-17 22:33:01.109004",
+ "modified": "2019-10-14 16:03:25.499557",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 0b02302..6e78b98 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -68,6 +68,16 @@
}
});
+ 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 @@
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 @@
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 55e02a4..26693d2 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -644,28 +644,37 @@
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 @@
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 @@
"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 @@
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 941472b..eddab5d 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 @@
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 @@
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 98a8c59..ca2741c 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -52,9 +52,10 @@
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 @@
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 @@
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 @@
# 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 @@
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 @@
"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 @@
}
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 5bdbca2..db7f6ad 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -19,10 +19,26 @@
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 @@
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 2ac0bae..2c6c953 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -271,6 +271,7 @@
'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 @@
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 cecaaaa..7c8925e 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.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 fc850d5..ce0f47d 100644
--- a/erpnext/tests/test_woocommerce.py
+++ b/erpnext/tests/test_woocommerce.py
@@ -18,6 +18,7 @@
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"