Merge branch 'develop' of https://github.com/frappe/erpnext into develop
This commit is contained in:
commit
ea7979234d
@ -331,15 +331,15 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
asset: function(frm, cdt, cdn) {
|
item_code: function(frm, cdt, cdn) {
|
||||||
var row = locals[cdt][cdn];
|
var row = locals[cdt][cdn];
|
||||||
if(row.asset) {
|
if(row.item_code) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.assets.doctype.asset_category.asset_category.get_asset_category_account",
|
method: "erpnext.assets.doctype.asset_category.asset_category.get_asset_category_account",
|
||||||
args: {
|
args: {
|
||||||
"asset": row.asset,
|
"item": row.item_code,
|
||||||
"fieldname": "fixed_asset_account",
|
"fieldname": "fixed_asset_account",
|
||||||
"account": row.expense_account
|
"company": frm.doc.company
|
||||||
},
|
},
|
||||||
callback: function(r, rt) {
|
callback: function(r, rt) {
|
||||||
frappe.model.set_value(cdt, cdn, "expense_account", r.message);
|
frappe.model.set_value(cdt, cdn, "expense_account", r.message);
|
||||||
@ -430,19 +430,7 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn)
|
|||||||
cur_frm.set_query("expense_account", "items", function(doc) {
|
cur_frm.set_query("expense_account", "items", function(doc) {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.get_expense_account",
|
query: "erpnext.controllers.queries.get_expense_account",
|
||||||
filters: {'company': doc.company}
|
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'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -98,7 +98,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
self.set_against_expense_account()
|
self.set_against_expense_account()
|
||||||
self.validate_write_off_account()
|
self.validate_write_off_account()
|
||||||
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
|
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
|
||||||
self.validate_fixed_asset()
|
|
||||||
self.create_remarks()
|
self.create_remarks()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.validate_purchase_receipt_if_update_stock()
|
self.validate_purchase_receipt_if_update_stock()
|
||||||
@ -238,13 +237,8 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.expense_account = warehouse_account[item.warehouse]["account"]
|
item.expense_account = warehouse_account[item.warehouse]["account"]
|
||||||
else:
|
else:
|
||||||
item.expense_account = stock_not_billed_account
|
item.expense_account = stock_not_billed_account
|
||||||
|
|
||||||
elif item.is_fixed_asset and not is_cwip_accounting_enabled(self.company, asset_category):
|
elif item.is_fixed_asset and not is_cwip_accounting_enabled(self.company, asset_category):
|
||||||
if not item.asset:
|
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
|
||||||
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',
|
|
||||||
company = self.company)
|
company = self.company)
|
||||||
elif item.is_fixed_asset and item.pr_detail:
|
elif item.is_fixed_asset and item.pr_detail:
|
||||||
item.expense_account = asset_received_but_not_billed
|
item.expense_account = asset_received_but_not_billed
|
||||||
@ -511,18 +505,48 @@ class PurchaseInvoice(BuyingController):
|
|||||||
expense_account = (item.expense_account
|
expense_account = (item.expense_account
|
||||||
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
||||||
|
|
||||||
gl_entries.append(
|
if not item.is_fixed_asset:
|
||||||
self.get_gl_dict({
|
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({
|
||||||
"account": expense_account,
|
"account": expense_account,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
"debit": 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"))),
|
|
||||||
"cost_center": item.cost_center,
|
"cost_center": item.cost_center,
|
||||||
"project": item.project
|
"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 \
|
if self.auto_accounting_for_stock and self.is_opening == "No" and \
|
||||||
item.item_code in stock_items and item.item_tax_amount:
|
item.item_code in stock_items and item.item_tax_amount:
|
||||||
@ -547,30 +571,27 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.precision("item_tax_amount"))
|
item.precision("item_tax_amount"))
|
||||||
|
|
||||||
def get_asset_gl_entry(self, gl_entries):
|
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"):
|
for item in self.get("items"):
|
||||||
if item.item_code and item.is_fixed_asset :
|
if item.is_fixed_asset:
|
||||||
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
|
|
||||||
|
|
||||||
if item.is_fixed_asset and is_cwip_accounting_enabled(self.company, asset_category) :
|
|
||||||
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
|
|
||||||
|
|
||||||
asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate)
|
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)
|
base_asset_amount = flt(item.base_net_amount + item.item_tax_amount)
|
||||||
|
|
||||||
if (not item.expense_account or frappe.db.get_value('Account',
|
item_exp_acc_type = frappe.db.get_value('Account', item.expense_account, 'account_type')
|
||||||
item.expense_account, 'account_type') not in ['Asset Received But Not Billed', 'Fixed Asset']):
|
if (not item.expense_account or item_exp_acc_type not in ['Asset Received But Not Billed', 'Fixed Asset']):
|
||||||
arbnb_account = self.get_company_default("asset_received_but_not_billed")
|
|
||||||
item.expense_account = arbnb_account
|
item.expense_account = arbnb_account
|
||||||
|
|
||||||
if not self.update_stock:
|
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({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": item.expense_account,
|
"account": item.expense_account,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"debit": base_asset_amount,
|
"debit": base_asset_amount,
|
||||||
"debit_in_account_currency": (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
|
"cost_center": item.cost_center
|
||||||
}, item=item))
|
}, item=item))
|
||||||
|
|
||||||
@ -587,8 +608,7 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.item_tax_amount / self.conversion_rate)
|
item.item_tax_amount / self.conversion_rate)
|
||||||
}, item=item))
|
}, item=item))
|
||||||
else:
|
else:
|
||||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company)
|
||||||
item.asset, company = self.company)
|
|
||||||
|
|
||||||
cwip_account_currency = get_account_currency(cwip_account)
|
cwip_account_currency = get_account_currency(cwip_account)
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
@ -614,6 +634,36 @@ class PurchaseInvoice(BuyingController):
|
|||||||
item.item_tax_amount / self.conversion_rate)
|
item.item_tax_amount / self.conversion_rate)
|
||||||
}, item=item))
|
}, 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
|
return gl_entries
|
||||||
|
|
||||||
def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
|
def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
|
||||||
@ -664,14 +714,14 @@ class PurchaseInvoice(BuyingController):
|
|||||||
if account_currency==self.company_currency \
|
if account_currency==self.company_currency \
|
||||||
else tax.tax_amount_after_discount_amount,
|
else tax.tax_amount_after_discount_amount,
|
||||||
"cost_center": tax.cost_center
|
"cost_center": tax.cost_center
|
||||||
}, account_currency)
|
}, account_currency, item=tax)
|
||||||
)
|
)
|
||||||
# accumulate valuation 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.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:
|
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)))
|
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.setdefault(tax.name, 0)
|
||||||
valuation_tax[tax.cost_center] += \
|
valuation_tax[tax.name] += \
|
||||||
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
|
(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:
|
if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax:
|
||||||
@ -681,36 +731,38 @@ class PurchaseInvoice(BuyingController):
|
|||||||
total_valuation_amount = sum(valuation_tax.values())
|
total_valuation_amount = sum(valuation_tax.values())
|
||||||
amount_including_divisional_loss = self.negative_expense_to_be_booked
|
amount_including_divisional_loss = self.negative_expense_to_be_booked
|
||||||
i = 1
|
i = 1
|
||||||
for cost_center, amount in iteritems(valuation_tax):
|
for tax in self.get("taxes"):
|
||||||
if i == len(valuation_tax):
|
if valuation_tax.get(tax.name):
|
||||||
applicable_amount = amount_including_divisional_loss
|
if i == len(valuation_tax):
|
||||||
else:
|
applicable_amount = amount_including_divisional_loss
|
||||||
applicable_amount = self.negative_expense_to_be_booked * (amount / total_valuation_amount)
|
else:
|
||||||
amount_including_divisional_loss -= applicable_amount
|
applicable_amount = self.negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount)
|
||||||
|
amount_including_divisional_loss -= applicable_amount
|
||||||
|
|
||||||
gl_entries.append(
|
gl_entries.append(
|
||||||
self.get_gl_dict({
|
self.get_gl_dict({
|
||||||
"account": self.expenses_included_in_valuation,
|
"account": tax.account_head,
|
||||||
"cost_center": cost_center,
|
"cost_center": tax.cost_center,
|
||||||
"against": self.supplier,
|
"against": self.supplier,
|
||||||
"credit": applicable_amount,
|
"credit": applicable_amount,
|
||||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
"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:
|
if self.auto_accounting_for_stock and self.update_stock and valuation_tax:
|
||||||
for cost_center, amount in iteritems(valuation_tax):
|
for tax in self.get("taxes"):
|
||||||
gl_entries.append(
|
if valuation_tax.get(tax.name):
|
||||||
self.get_gl_dict({
|
gl_entries.append(
|
||||||
"account": self.expenses_included_in_valuation,
|
self.get_gl_dict({
|
||||||
"cost_center": cost_center,
|
"account": tax.account_head,
|
||||||
"against": self.supplier,
|
"cost_center": tax.cost_center,
|
||||||
"credit": amount,
|
"against": self.supplier,
|
||||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
"credit": valuation_tax[tax.name],
|
||||||
})
|
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||||
)
|
}, item=tax)
|
||||||
|
)
|
||||||
|
|
||||||
def make_payment_gl_entries(self, gl_entries):
|
def make_payment_gl_entries(self, gl_entries):
|
||||||
# Make Cash GL Entries
|
# Make Cash GL Entries
|
||||||
|
@ -204,19 +204,40 @@ class TestPurchaseInvoice(unittest.TestCase):
|
|||||||
pi.insert()
|
pi.insert()
|
||||||
pi.submit()
|
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):
|
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
|
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)
|
self.assertTrue(gl_entries)
|
||||||
|
|
||||||
expected_values = dict((d[0], d) for d in [
|
expected_values = dict((d[0], d) for d in [
|
||||||
["Creditors - TCP1", 0, 720],
|
["Creditors - TCP1", 0, 720],
|
||||||
["Stock Received But Not Billed - TCP1", 500.0, 0],
|
["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 VAT - TCP1", 120.0, 0],
|
||||||
|
["_Test Account Customs Duty - TCP1", 0, 150]
|
||||||
])
|
])
|
||||||
|
|
||||||
for i, gle in enumerate(gl_entries):
|
for i, gle in enumerate(gl_entries):
|
||||||
|
@ -71,8 +71,8 @@
|
|||||||
"expense_account",
|
"expense_account",
|
||||||
"col_break5",
|
"col_break5",
|
||||||
"is_fixed_asset",
|
"is_fixed_asset",
|
||||||
"asset",
|
|
||||||
"asset_location",
|
"asset_location",
|
||||||
|
"asset_category",
|
||||||
"deferred_expense_section",
|
"deferred_expense_section",
|
||||||
"deferred_expense_account",
|
"deferred_expense_account",
|
||||||
"service_stop_date",
|
"service_stop_date",
|
||||||
@ -116,6 +116,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.item_name",
|
||||||
"fieldname": "item_name",
|
"fieldname": "item_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
@ -191,6 +192,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"fetch_from": "item_code.stock_uom",
|
||||||
"fieldname": "uom",
|
"fieldname": "uom",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "UOM",
|
"label": "UOM",
|
||||||
@ -414,6 +416,7 @@
|
|||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "batch_no",
|
"fieldname": "batch_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Batch No",
|
"label": "Batch No",
|
||||||
@ -425,12 +428,14 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "serial_no",
|
"fieldname": "serial_no",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
"label": "Serial No",
|
"label": "Serial No",
|
||||||
"no_copy": 1
|
"no_copy": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "rejected_serial_no",
|
"fieldname": "rejected_serial_no",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
"label": "Rejected Serial No",
|
"label": "Rejected Serial No",
|
||||||
@ -615,6 +620,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"fetch_from": "item_code.is_fixed_asset",
|
||||||
"fieldname": "is_fixed_asset",
|
"fieldname": "is_fixed_asset",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
@ -623,14 +629,6 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"depends_on": "is_fixed_asset",
|
|
||||||
"fieldname": "asset",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Asset",
|
|
||||||
"no_copy": 1,
|
|
||||||
"options": "Asset"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"depends_on": "is_fixed_asset",
|
"depends_on": "is_fixed_asset",
|
||||||
"fieldname": "asset_location",
|
"fieldname": "asset_location",
|
||||||
@ -676,7 +674,7 @@
|
|||||||
"fieldname": "pr_detail",
|
"fieldname": "pr_detail",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "PR Detail",
|
"label": "Purchase Receipt Detail",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "pr_detail",
|
"oldfieldname": "pr_detail",
|
||||||
"oldfieldtype": "Data",
|
"oldfieldtype": "Data",
|
||||||
@ -754,11 +752,21 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Manufacturer Part Number",
|
"label": "Manufacturer Part Number",
|
||||||
"read_only": 1
|
"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,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-09-17 22:32:05.984240",
|
"modified": "2019-11-03 13:43:23.782877",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice Item",
|
"name": "Purchase Invoice Item",
|
||||||
|
@ -136,6 +136,16 @@ class SalesInvoice(SellingController):
|
|||||||
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
|
if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points:
|
||||||
validate_loyalty_points(self, 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):
|
def before_save(self):
|
||||||
set_account_for_mode_of_payment(self)
|
set_account_for_mode_of_payment(self)
|
||||||
|
@ -41,6 +41,21 @@ frappe.ui.form.on('Asset', {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
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) {
|
refresh: function(frm) {
|
||||||
frappe.ui.form.trigger("Asset", "is_existing_asset");
|
frappe.ui.form.trigger("Asset", "is_existing_asset");
|
||||||
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
|
frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1);
|
||||||
@ -78,11 +93,6 @@ frappe.ui.form.on('Asset', {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) {
|
||||||
frm.add_custom_button(__("Asset Maintenance"), function() {
|
frm.add_custom_button(__("Asset Maintenance"), function() {
|
||||||
frm.trigger("create_asset_maintenance");
|
frm.trigger("create_asset_maintenance");
|
||||||
@ -104,11 +114,36 @@ frappe.ui.form.on('Asset', {
|
|||||||
frm.trigger("setup_chart");
|
frm.trigger("setup_chart");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frm.trigger("toggle_reference_doc");
|
||||||
|
|
||||||
if (frm.doc.docstatus == 0) {
|
if (frm.doc.docstatus == 0) {
|
||||||
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
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) {
|
make_journal_entry: function(frm) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.assets.doctype.asset.asset.make_journal_entry",
|
method: "erpnext.assets.doctype.asset.asset.make_journal_entry",
|
||||||
@ -176,21 +211,25 @@ frappe.ui.form.on('Asset', {
|
|||||||
|
|
||||||
item_code: function(frm) {
|
item_code: function(frm) {
|
||||||
if(frm.doc.item_code) {
|
if(frm.doc.item_code) {
|
||||||
frappe.call({
|
frm.trigger('set_finance_book');
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
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) {
|
available_for_use_date: function(frm) {
|
||||||
$.each(frm.doc.finance_books || [], function(i, d) {
|
$.each(frm.doc.finance_books || [], function(i, d) {
|
||||||
if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date;
|
if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date;
|
||||||
@ -207,29 +246,14 @@ frappe.ui.form.on('Asset', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
make_schedules_editable: function(frm) {
|
make_schedules_editable: function(frm) {
|
||||||
var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
|
if (frm.doc.finance_books) {
|
||||||
? true : false;
|
var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
|
||||||
|
? true : false;
|
||||||
|
|
||||||
frm.toggle_enable("schedules", is_editable);
|
frm.toggle_enable("schedules", is_editable);
|
||||||
frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
|
frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
|
||||||
frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", 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);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
|
||||||
make_sales_invoice: function(frm) {
|
make_sales_invoice: function(frm) {
|
||||||
@ -291,6 +315,65 @@ frappe.ui.form.on('Asset', {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
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) {
|
set_depreciation_rate: function(frm, row) {
|
||||||
if (row.total_number_of_depreciations && row.frequency_of_depreciation
|
if (row.total_number_of_depreciations && row.frequency_of_depreciation
|
||||||
&& row.expected_value_after_useful_life) {
|
&& row.expected_value_after_useful_life) {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
"supplier",
|
"supplier",
|
||||||
"customer",
|
"customer",
|
||||||
"image",
|
"image",
|
||||||
|
"purchase_invoice",
|
||||||
"column_break_3",
|
"column_break_3",
|
||||||
"company",
|
"company",
|
||||||
"location",
|
"location",
|
||||||
@ -25,6 +26,7 @@
|
|||||||
"purchase_date",
|
"purchase_date",
|
||||||
"disposal_date",
|
"disposal_date",
|
||||||
"journal_entry_for_scrap",
|
"journal_entry_for_scrap",
|
||||||
|
"purchase_receipt",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
"cost_center",
|
"cost_center",
|
||||||
"dimension_col_break",
|
"dimension_col_break",
|
||||||
@ -62,9 +64,8 @@
|
|||||||
"status",
|
"status",
|
||||||
"booked_fixed_asset",
|
"booked_fixed_asset",
|
||||||
"column_break_51",
|
"column_break_51",
|
||||||
"purchase_receipt",
|
|
||||||
"purchase_receipt_amount",
|
"purchase_receipt_amount",
|
||||||
"purchase_invoice",
|
|
||||||
"default_finance_book",
|
"default_finance_book",
|
||||||
"amended_from"
|
"amended_from"
|
||||||
],
|
],
|
||||||
@ -403,8 +404,7 @@
|
|||||||
"label": "Purchase Receipt",
|
"label": "Purchase Receipt",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Purchase Receipt",
|
"options": "Purchase Receipt",
|
||||||
"print_hide": 1,
|
"print_hide": 1
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "purchase_receipt_amount",
|
"fieldname": "purchase_receipt_amount",
|
||||||
@ -420,8 +420,7 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Purchase Invoice",
|
"label": "Purchase Invoice",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Purchase Invoice",
|
"options": "Purchase Invoice"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "company.default_finance_book",
|
"fetch_from": "company.default_finance_book",
|
||||||
|
@ -18,6 +18,7 @@ from erpnext.controllers.accounts_controller import AccountsController
|
|||||||
class Asset(AccountsController):
|
class Asset(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_asset_values()
|
self.validate_asset_values()
|
||||||
|
self.validate_asset_and_reference()
|
||||||
self.validate_item()
|
self.validate_item()
|
||||||
self.set_missing_values()
|
self.set_missing_values()
|
||||||
self.prepare_depreciation_data()
|
self.prepare_depreciation_data()
|
||||||
@ -29,11 +30,14 @@ class Asset(AccountsController):
|
|||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.validate_in_use_date()
|
self.validate_in_use_date()
|
||||||
self.set_status()
|
self.set_status()
|
||||||
self.update_stock_movement()
|
self.make_asset_movement()
|
||||||
if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.company,
|
if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.company,
|
||||||
self.asset_category):
|
self.asset_category):
|
||||||
self.make_gl_entries()
|
self.make_gl_entries()
|
||||||
|
|
||||||
|
def before_cancel(self):
|
||||||
|
self.cancel_auto_gen_movement()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.validate_cancellation()
|
self.validate_cancellation()
|
||||||
self.delete_depreciation_entries()
|
self.delete_depreciation_entries()
|
||||||
@ -41,6 +45,18 @@ class Asset(AccountsController):
|
|||||||
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
|
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
|
||||||
self.db_set('booked_fixed_asset', 0)
|
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):
|
def prepare_depreciation_data(self):
|
||||||
if self.calculate_depreciation:
|
if self.calculate_depreciation:
|
||||||
self.value_after_depreciation = 0
|
self.value_after_depreciation = 0
|
||||||
@ -109,6 +125,36 @@ class Asset(AccountsController):
|
|||||||
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
|
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"))
|
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):
|
def set_depreciation_rate(self):
|
||||||
for d in self.get("finance_books"):
|
for d in self.get("finance_books"):
|
||||||
d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
|
d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
|
||||||
@ -398,22 +444,13 @@ class Asset(AccountsController):
|
|||||||
if d.finance_book == self.default_finance_book:
|
if d.finance_book == self.default_finance_book:
|
||||||
return cint(d.idx) - 1
|
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):
|
def make_gl_entries(self):
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
|
||||||
if ((self.purchase_receipt or (self.purchase_invoice and
|
if ((self.purchase_receipt \
|
||||||
frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock')))
|
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()):
|
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)
|
asset_category = self.asset_category, company = self.company)
|
||||||
|
|
||||||
cwip_account = get_asset_account("capital_work_in_progress_account",
|
cwip_account = get_asset_account("capital_work_in_progress_account",
|
||||||
@ -421,7 +458,7 @@ class Asset(AccountsController):
|
|||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": cwip_account,
|
"account": cwip_account,
|
||||||
"against": fixed_aseet_account,
|
"against": fixed_asset_account,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"posting_date": self.available_for_use_date,
|
"posting_date": self.available_for_use_date,
|
||||||
"credit": self.purchase_receipt_amount,
|
"credit": self.purchase_receipt_amount,
|
||||||
@ -430,7 +467,7 @@ class Asset(AccountsController):
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": fixed_aseet_account,
|
"account": fixed_asset_account,
|
||||||
"against": cwip_account,
|
"against": cwip_account,
|
||||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
"posting_date": self.available_for_use_date,
|
"posting_date": self.available_for_use_date,
|
||||||
@ -491,25 +528,6 @@ def get_asset_naming_series():
|
|||||||
meta = frappe.get_meta('Asset')
|
meta = frappe.get_meta('Asset')
|
||||||
return meta.get_field("naming_series").options
|
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()
|
@frappe.whitelist()
|
||||||
def make_sales_invoice(asset, item_code, company, serial_no=None):
|
def make_sales_invoice(asset, item_code, company, serial_no=None):
|
||||||
si = frappe.new_doc("Sales Invoice")
|
si = frappe.new_doc("Sales Invoice")
|
||||||
@ -584,7 +602,7 @@ def get_item_details(item_code, asset_category):
|
|||||||
def get_asset_account(account_name, asset=None, asset_category=None, company=None):
|
def get_asset_account(account_name, asset=None, asset_category=None, company=None):
|
||||||
account = None
|
account = None
|
||||||
if asset:
|
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)
|
asset_category = asset_category, company = company)
|
||||||
|
|
||||||
if not account:
|
if not account:
|
||||||
@ -627,6 +645,44 @@ def make_journal_entry(asset_name):
|
|||||||
|
|
||||||
return je
|
return je
|
||||||
|
|
||||||
|
@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):
|
def is_cwip_accounting_enabled(company, asset_category=None):
|
||||||
enable_cwip_in_company = cint(frappe.db.get_value("Company", company, "enable_cwip_accounting"))
|
enable_cwip_in_company = cint(frappe.db.get_value("Company", company, "enable_cwip_accounting"))
|
||||||
|
|
||||||
|
@ -30,8 +30,23 @@ frappe.listview_settings['Asset'] = {
|
|||||||
|
|
||||||
} else if (doc.status === "Draft") {
|
} else if (doc.status === "Draft") {
|
||||||
return [__("Draft"), "red", "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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@ -29,8 +29,11 @@ class AssetCategory(Document):
|
|||||||
frappe.bold(d.idx), frappe.bold(d.company_name)))
|
frappe.bold(d.idx), frappe.bold(d.company_name)))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_asset_category_account(asset, fieldname, account=None, asset_category = None, company = None):
|
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
|
||||||
if not asset_category and company:
|
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 account:
|
||||||
if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
|
if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
|
||||||
account=None
|
account=None
|
||||||
|
@ -75,6 +75,8 @@ def create_asset_data():
|
|||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
if not frappe.db.exists("Item", "Photocopier"):
|
if not frappe.db.exists("Item", "Photocopier"):
|
||||||
|
meta = frappe.get_meta('Asset')
|
||||||
|
naming_series = meta.get_field("naming_series").options
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "Item",
|
"doctype": "Item",
|
||||||
"item_code": "Photocopier",
|
"item_code": "Photocopier",
|
||||||
@ -83,7 +85,9 @@ def create_asset_data():
|
|||||||
"company": "_Test Company",
|
"company": "_Test Company",
|
||||||
"is_fixed_asset": 1,
|
"is_fixed_asset": 1,
|
||||||
"is_stock_item": 0,
|
"is_stock_item": 0,
|
||||||
"asset_category": "Equipment"
|
"asset_category": "Equipment",
|
||||||
|
"auto_create_assets": 1,
|
||||||
|
"asset_naming_series": naming_series
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
def create_maintenance_team():
|
def create_maintenance_team():
|
||||||
|
@ -2,27 +2,138 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Asset Movement', {
|
frappe.ui.form.on('Asset Movement', {
|
||||||
select_serial_no: function(frm) {
|
setup: (frm) => {
|
||||||
if (frm.doc.select_serial_no) {
|
frm.set_query("to_employee", "assets", (doc) => {
|
||||||
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() {
|
|
||||||
return {
|
return {
|
||||||
filters: {
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
@ -1,27 +1,20 @@
|
|||||||
{
|
{
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"autoname": "naming_series:",
|
"autoname": "format:ACC-ASM-{YYYY}-{#####}",
|
||||||
"creation": "2016-04-25 18:00:23.559973",
|
"creation": "2016-04-25 18:00:23.559973",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"naming_series",
|
|
||||||
"company",
|
"company",
|
||||||
"purpose",
|
"purpose",
|
||||||
"asset",
|
|
||||||
"transaction_date",
|
|
||||||
"column_break_4",
|
"column_break_4",
|
||||||
"quantity",
|
"transaction_date",
|
||||||
"select_serial_no",
|
|
||||||
"serial_no",
|
|
||||||
"section_break_7",
|
|
||||||
"source_location",
|
|
||||||
"target_location",
|
|
||||||
"column_break_10",
|
|
||||||
"from_employee",
|
|
||||||
"to_employee",
|
|
||||||
"reference",
|
"reference",
|
||||||
"reference_doctype",
|
"reference_doctype",
|
||||||
|
"column_break_9",
|
||||||
"reference_name",
|
"reference_name",
|
||||||
|
"section_break_10",
|
||||||
|
"assets",
|
||||||
"amended_from"
|
"amended_from"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
@ -36,23 +29,12 @@
|
|||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "Transfer",
|
|
||||||
"fieldname": "purpose",
|
"fieldname": "purpose",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Purpose",
|
"label": "Purpose",
|
||||||
"options": "\nIssue\nReceipt\nTransfer",
|
"options": "\nIssue\nReceipt\nTransfer",
|
||||||
"reqd": 1
|
"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",
|
"fieldname": "transaction_date",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
@ -64,56 +46,6 @@
|
|||||||
"fieldname": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break"
|
"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",
|
"fieldname": "reference",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
@ -125,7 +57,7 @@
|
|||||||
"label": "Reference DocType",
|
"label": "Reference DocType",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "DocType",
|
"options": "DocType",
|
||||||
"read_only": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "reference_name",
|
"fieldname": "reference_name",
|
||||||
@ -133,7 +65,7 @@
|
|||||||
"label": "Reference Name",
|
"label": "Reference Name",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "reference_doctype",
|
"options": "reference_doctype",
|
||||||
"read_only": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
@ -145,16 +77,23 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "ACC-ASM-.YYYY.-",
|
"fieldname": "section_break_10",
|
||||||
"fieldname": "naming_series",
|
"fieldtype": "Section Break"
|
||||||
"fieldtype": "Select",
|
},
|
||||||
"label": "Series",
|
{
|
||||||
"options": "ACC-ASM-.YYYY.-",
|
"fieldname": "assets",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Assets",
|
||||||
|
"options": "Asset Movement Item",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_9",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"modified": "2019-09-16 16:27:53.887634",
|
"modified": "2019-11-13 15:37:48.870147",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Movement",
|
"name": "Asset Movement",
|
||||||
|
@ -5,101 +5,142 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class AssetMovement(Document):
|
class AssetMovement(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_asset()
|
self.validate_asset()
|
||||||
self.validate_location()
|
self.validate_location()
|
||||||
|
self.validate_employee()
|
||||||
|
|
||||||
def validate_asset(self):
|
def validate_asset(self):
|
||||||
status, company = frappe.db.get_value("Asset", self.asset, ["status", "company"])
|
for d in self.assets:
|
||||||
if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
|
status, company = frappe.db.get_value("Asset", d.asset, ["status", "company"])
|
||||||
frappe.throw(_("{0} asset cannot be transferred").format(status))
|
if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"):
|
||||||
|
frappe.throw(_("{0} asset cannot be transferred").format(status))
|
||||||
|
|
||||||
if company != self.company:
|
if company != self.company:
|
||||||
frappe.throw(_("Asset {0} does not belong to company {1}").format(self.asset, 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:
|
if not(d.source_location or d.target_location or d.from_employee or d.to_employee):
|
||||||
frappe.throw(_("Number of serial nos and quantity must be the same"))
|
frappe.throw(_("Either location or employee must be required"))
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
def validate_location(self):
|
def validate_location(self):
|
||||||
if self.purpose in ['Transfer', 'Issue']:
|
for d in self.assets:
|
||||||
if not self.serial_no and not (self.from_employee or self.to_employee):
|
if self.purpose in ['Transfer', 'Issue']:
|
||||||
self.source_location = frappe.db.get_value("Asset", self.asset, "location")
|
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):
|
if not d.source_location:
|
||||||
frappe.throw(_("Source Location is required for the asset {0}").format(self.asset))
|
frappe.throw(_("Source Location is required for the Asset {0}").format(d.asset))
|
||||||
|
|
||||||
if self.serial_no and self.source_location:
|
if d.source_location:
|
||||||
s_nos = get_serial_nos(self.serial_no)
|
current_location = frappe.db.get_value("Asset", d.asset, "location")
|
||||||
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 serial_nos:
|
if current_location != d.source_location:
|
||||||
frappe.throw(_("Serial nos {0} does not belongs to the location {1}").
|
frappe.throw(_("Asset {0} does not belongs to the location {1}").
|
||||||
format(','.join(serial_nos), self.source_location))
|
format(d.asset, d.source_location))
|
||||||
|
|
||||||
if self.source_location and self.source_location == self.target_location and self.purpose == 'Transfer':
|
if self.purpose == 'Issue':
|
||||||
frappe.throw(_("Source and Target Location cannot be same"))
|
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 == 'Receipt' and not (self.target_location or self.to_employee):
|
if self.purpose == 'Transfer':
|
||||||
frappe.throw(_("Target Location is required for the asset {0}").format(self.asset))
|
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))
|
||||||
|
|
||||||
|
def validate_employee(self):
|
||||||
|
for d in self.assets:
|
||||||
|
if d.from_employee:
|
||||||
|
current_custodian = frappe.db.get_value("Asset", d.asset, "custodian")
|
||||||
|
|
||||||
|
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):
|
def on_submit(self):
|
||||||
self.set_latest_location_in_asset()
|
self.set_latest_location_in_asset()
|
||||||
|
|
||||||
|
def before_cancel(self):
|
||||||
|
self.validate_last_movement()
|
||||||
|
|
||||||
def on_cancel(self):
|
def on_cancel(self):
|
||||||
self.set_latest_location_in_asset()
|
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):
|
def set_latest_location_in_asset(self):
|
||||||
location, employee = '', ''
|
current_location, current_employee = '', ''
|
||||||
cond = "1=1"
|
cond = "1=1"
|
||||||
|
|
||||||
args = {
|
for d in self.assets:
|
||||||
'asset': self.asset,
|
args = {
|
||||||
'company': self.company
|
'asset': d.asset,
|
||||||
}
|
'company': self.company
|
||||||
|
}
|
||||||
|
|
||||||
if self.serial_no:
|
# latest entry corresponds to current document's location, employee when transaction date > previous dates
|
||||||
cond = "serial_no like %(txt)s"
|
# In case of cancellation it corresponds to previous latest document's location, employee
|
||||||
args.update({
|
latest_movement_entry = frappe.db.sql(
|
||||||
'txt': "%%%s%%" % self.serial_no
|
"""
|
||||||
})
|
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`
|
frappe.db.set_value('Asset', d.asset, 'location', current_location)
|
||||||
where asset=%(asset)s and docstatus=1 and company=%(company)s and {0}
|
frappe.db.set_value('Asset', d.asset, 'custodian', current_employee)
|
||||||
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)
|
|
||||||
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
|
import erpnext
|
||||||
from erpnext.stock.doctype.item.test_item import make_item
|
from erpnext.stock.doctype.item.test_item import make_item
|
||||||
from frappe.utils import now, nowdate, get_last_day, add_days
|
from frappe.utils import now, nowdate, get_last_day, add_days
|
||||||
from erpnext.assets.doctype.asset.test_asset import create_asset_data
|
from erpnext.assets.doctype.asset.test_asset import create_asset_data
|
||||||
@ -16,7 +17,6 @@ class TestAssetMovement(unittest.TestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
create_asset_data()
|
create_asset_data()
|
||||||
make_location()
|
make_location()
|
||||||
make_serialized_item()
|
|
||||||
|
|
||||||
def test_movement(self):
|
def test_movement(self):
|
||||||
pr = make_purchase_receipt(item_code="Macbook Pro",
|
pr = make_purchase_receipt(item_code="Macbook Pro",
|
||||||
@ -38,68 +38,72 @@ class TestAssetMovement(unittest.TestCase):
|
|||||||
|
|
||||||
if asset.docstatus == 0:
|
if asset.docstatus == 0:
|
||||||
asset.submit()
|
asset.submit()
|
||||||
|
|
||||||
|
# check asset movement is created
|
||||||
if not frappe.db.exists("Location", "Test Location 2"):
|
if not frappe.db.exists("Location", "Test Location 2"):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
'doctype': 'Location',
|
'doctype': 'Location',
|
||||||
'location_name': 'Test Location 2'
|
'location_name': 'Test Location 2'
|
||||||
}).insert()
|
}).insert()
|
||||||
|
|
||||||
movement1 = create_asset_movement(asset= asset.name, purpose = 'Transfer',
|
movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||||
company=asset.company, source_location="Test Location", target_location="Test Location 2")
|
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")
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
|
||||||
|
|
||||||
movement2 = create_asset_movement(asset= asset.name, purpose = 'Transfer',
|
movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||||
company=asset.company, source_location = "Test Location 2", target_location="Test Location")
|
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")
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||||
|
|
||||||
movement1.cancel()
|
movement1.cancel()
|
||||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||||
|
|
||||||
movement2.cancel()
|
employee = make_employee("testassetmovemp@example.com", company="_Test Company")
|
||||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
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)
|
||||||
|
|
||||||
def test_movement_for_serialized_asset(self):
|
# after issuing asset should belong to an employee not at a location
|
||||||
asset_item = "Test Serialized Asset Item"
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None)
|
||||||
pr = make_purchase_receipt(item_code=asset_item, rate = 1000, qty=3, location = "Mumbai")
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee)
|
||||||
asset_name = frappe.db.get_value('Asset', {'purchase_receipt': pr.name}, 'name')
|
|
||||||
|
|
||||||
|
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)
|
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.calculate_depreciation = 1
|
||||||
|
asset.available_for_use_date = '2020-06-06'
|
||||||
|
asset.purchase_date = '2020-06-06'
|
||||||
asset.append("finance_books", {
|
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",
|
"depreciation_method": "Straight Line",
|
||||||
"total_number_of_depreciations": 3,
|
"total_number_of_depreciations": 3,
|
||||||
"frequency_of_depreciation": 10,
|
"frequency_of_depreciation": 10,
|
||||||
"depreciation_start_date": month_end_date
|
"depreciation_start_date": "2020-06-06"
|
||||||
})
|
})
|
||||||
asset.submit()
|
if asset.docstatus == 0:
|
||||||
serial_nos = frappe.db.get_value('Asset Movement', {'reference_name': pr.name}, 'serial_no')
|
asset.submit()
|
||||||
|
|
||||||
mov1 = create_asset_movement(asset=asset_name, purpose = 'Transfer',
|
if not frappe.db.exists("Location", "Test Location 2"):
|
||||||
company=asset.company, source_location = "Mumbai", target_location="Pune", serial_no=serial_nos)
|
frappe.get_doc({
|
||||||
self.assertEqual(mov1.target_location, "Pune")
|
'doctype': 'Location',
|
||||||
|
'location_name': 'Test Location 2'
|
||||||
|
}).insert()
|
||||||
|
|
||||||
serial_no = frappe.db.get_value('Serial No', {'asset': asset_name}, 'name')
|
movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name })
|
||||||
|
self.assertRaises(frappe.ValidationError, movement.cancel)
|
||||||
|
|
||||||
employee = make_employee("testassetemp@example.com")
|
movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||||
create_asset_movement(asset=asset_name, purpose = 'Transfer',
|
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}],
|
||||||
company=asset.company, serial_no=serial_no, to_employee=employee)
|
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||||
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
|
||||||
|
|
||||||
self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'employee'), employee)
|
movement1.cancel()
|
||||||
|
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||||
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")
|
|
||||||
|
|
||||||
def create_asset_movement(**args):
|
def create_asset_movement(**args):
|
||||||
args = frappe._dict(args)
|
args = frappe._dict(args)
|
||||||
@ -109,22 +113,14 @@ def create_asset_movement(**args):
|
|||||||
|
|
||||||
movement = frappe.new_doc("Asset Movement")
|
movement = frappe.new_doc("Asset Movement")
|
||||||
movement.update({
|
movement.update({
|
||||||
"asset": args.asset,
|
"assets": args.assets,
|
||||||
"transaction_date": args.transaction_date,
|
"transaction_date": args.transaction_date,
|
||||||
"target_location": args.target_location,
|
|
||||||
"company": args.company,
|
"company": args.company,
|
||||||
'purpose': args.purpose or 'Receipt',
|
'purpose': args.purpose or 'Receipt',
|
||||||
'serial_no': args.serial_no,
|
'reference_doctype': args.reference_doctype,
|
||||||
'quantity': len(get_serial_nos(args.serial_no)) if args.serial_no else 1,
|
'reference_name': args.reference_name
|
||||||
'from_employee': "_T-Employee-00001" or args.from_employee,
|
|
||||||
'to_employee': args.to_employee
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if args.source_location:
|
|
||||||
movement.update({
|
|
||||||
'source_location': args.source_location
|
|
||||||
})
|
|
||||||
|
|
||||||
movement.insert()
|
movement.insert()
|
||||||
movement.submit()
|
movement.submit()
|
||||||
|
|
||||||
@ -137,33 +133,3 @@ def make_location():
|
|||||||
'doctype': 'Location',
|
'doctype': 'Location',
|
||||||
'location_name': location
|
'location_name': location
|
||||||
}).insert(ignore_permissions = True)
|
}).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.###'})
|
|
||||||
|
@ -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
|
||||||
|
}
|
@ -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
|
@ -43,6 +43,7 @@
|
|||||||
"base_amount",
|
"base_amount",
|
||||||
"pricing_rules",
|
"pricing_rules",
|
||||||
"is_free_item",
|
"is_free_item",
|
||||||
|
"is_fixed_asset",
|
||||||
"section_break_29",
|
"section_break_29",
|
||||||
"net_rate",
|
"net_rate",
|
||||||
"net_amount",
|
"net_amount",
|
||||||
@ -699,11 +700,19 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Manufacturer Part Number",
|
"label": "Manufacturer Part Number",
|
||||||
"read_only": 1
|
"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,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-09-17 22:32:34.703923",
|
"modified": "2019-11-07 17:19:12.090355",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order Item",
|
"name": "Purchase Order Item",
|
||||||
|
@ -718,48 +718,6 @@ class AccountsController(TransactionBase):
|
|||||||
# at quotation / sales order level and we shouldn't stop someone
|
# at quotation / sales order level and we shouldn't stop someone
|
||||||
# from creating a sales invoice if sales order is already created
|
# 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):
|
def delink_advance_entries(self, linked_doc_name):
|
||||||
total_allocated_amount = 0
|
total_allocated_amount = 0
|
||||||
for adv in self.advances:
|
for adv in self.advances:
|
||||||
|
@ -101,7 +101,7 @@ class BuyingController(StockController):
|
|||||||
msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
|
msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items'))
|
||||||
|
|
||||||
def get_asset_items(self):
|
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 []
|
||||||
|
|
||||||
return [d.item_code for d in self.items if d.is_fixed_asset]
|
return [d.item_code for d in self.items if d.is_fixed_asset]
|
||||||
@ -150,25 +150,26 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
TODO: rename item_tax_amount to valuation_tax_amount
|
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
|
stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0
|
||||||
last_stock_item_idx = 1
|
last_item_idx = 1
|
||||||
for d in self.get(parentfield):
|
for d in self.get(parentfield):
|
||||||
if d.item_code and d.item_code in stock_items:
|
if d.item_code and d.item_code in stock_and_asset_items:
|
||||||
stock_items_qty += flt(d.qty)
|
stock_and_asset_items_qty += flt(d.qty)
|
||||||
stock_items_amount += flt(d.base_net_amount)
|
stock_and_asset_items_amount += flt(d.base_net_amount)
|
||||||
last_stock_item_idx = d.idx
|
last_item_idx = d.idx
|
||||||
|
|
||||||
total_valuation_amount = sum([flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes")
|
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"]])
|
if d.category in ["Valuation", "Valuation and Total"]])
|
||||||
|
|
||||||
valuation_amount_adjustment = total_valuation_amount
|
valuation_amount_adjustment = total_valuation_amount
|
||||||
for i, item in enumerate(self.get(parentfield)):
|
for i, item in enumerate(self.get(parentfield)):
|
||||||
if item.item_code and item.qty and item.item_code in stock_items:
|
if item.item_code and item.qty and item.item_code in stock_and_asset_items:
|
||||||
item_proportion = flt(item.base_net_amount) / stock_items_amount if stock_items_amount \
|
item_proportion = flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount \
|
||||||
else flt(item.qty) / stock_items_qty
|
else flt(item.qty) / stock_and_asset_items_qty
|
||||||
if i == (last_stock_item_idx - 1):
|
|
||||||
|
if i == (last_item_idx - 1):
|
||||||
item.item_tax_amount = flt(valuation_amount_adjustment,
|
item.item_tax_amount = flt(valuation_amount_adjustment,
|
||||||
self.precision("item_tax_amount", item))
|
self.precision("item_tax_amount", item))
|
||||||
else:
|
else:
|
||||||
@ -572,43 +573,28 @@ class BuyingController(StockController):
|
|||||||
|
|
||||||
asset_items = self.get_asset_items()
|
asset_items = self.get_asset_items()
|
||||||
if 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)
|
items_data = get_asset_item_details(asset_items)
|
||||||
|
|
||||||
for d in self.items:
|
for d in self.items:
|
||||||
if d.is_fixed_asset:
|
if d.is_fixed_asset:
|
||||||
item_data = items_data.get(d.item_code)
|
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_data.get('auto_create_assets'):
|
||||||
# If item has serial no
|
# If asset has to be auto created
|
||||||
if item_data.get('serial_no_series') and not d.serial_no:
|
# Check for asset naming series
|
||||||
serial_nos = get_auto_serial_nos(item_data.get('serial_no_series'), d.qty)
|
if item_data.get('asset_naming_series'):
|
||||||
elif d.serial_no:
|
for qty in range(cint(d.qty)):
|
||||||
serial_nos = d.serial_no
|
self.make_asset(d)
|
||||||
elif not d.serial_no:
|
is_plural = 's' if cint(d.qty) != 1 else ''
|
||||||
frappe.throw(_("Serial no is mandatory for the item {0}").format(d.item_code))
|
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."))
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
def make_asset(self, row):
|
def make_asset(self, row):
|
||||||
if not row.asset_location:
|
if not row.asset_location:
|
||||||
@ -617,7 +603,7 @@ class BuyingController(StockController):
|
|||||||
item_data = frappe.db.get_value('Item',
|
item_data = frappe.db.get_value('Item',
|
||||||
row.item_code, ['asset_naming_series', 'asset_category'], as_dict=1)
|
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({
|
asset = frappe.get_doc({
|
||||||
'doctype': 'Asset',
|
'doctype': 'Asset',
|
||||||
'item_code': row.item_code,
|
'item_code': row.item_code,
|
||||||
@ -640,57 +626,42 @@ class BuyingController(StockController):
|
|||||||
asset.set_missing_values()
|
asset.set_missing_values()
|
||||||
asset.insert()
|
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):
|
def update_fixed_asset(self, field, delete_asset = False):
|
||||||
for d in self.get("items"):
|
for d in self.get("items"):
|
||||||
if d.is_fixed_asset and d.asset:
|
if d.is_fixed_asset:
|
||||||
asset = frappe.get_doc("Asset", d.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:
|
for asset in assets:
|
||||||
frappe.delete_doc("Asset", asset.name)
|
asset = frappe.get_doc('Asset', asset.name)
|
||||||
d.db_set('asset', None)
|
if delete_asset and is_auto_create_enabled:
|
||||||
continue
|
# 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):
|
if self.docstatus in [0, 1] and not asset.get(field):
|
||||||
asset.set(field, self.name)
|
asset.set(field, self.name)
|
||||||
asset.purchase_date = self.posting_date
|
asset.purchase_date = self.posting_date
|
||||||
asset.supplier = self.supplier
|
asset.supplier = self.supplier
|
||||||
elif self.docstatus == 2:
|
elif self.docstatus == 2:
|
||||||
asset.set(field, None)
|
asset.set(field, None)
|
||||||
asset.supplier = None
|
asset.supplier = None
|
||||||
|
|
||||||
asset.flags.ignore_validate_update_after_submit = True
|
asset.flags.ignore_validate_update_after_submit = True
|
||||||
asset.flags.ignore_mandatory = True
|
asset.flags.ignore_mandatory = True
|
||||||
if asset.docstatus == 0:
|
if asset.docstatus == 0:
|
||||||
asset.flags.ignore_validate = True
|
asset.flags.ignore_validate = True
|
||||||
|
|
||||||
asset.save()
|
asset.save()
|
||||||
|
|
||||||
def delete_linked_asset(self):
|
def delete_linked_asset(self):
|
||||||
if self.doctype == 'Purchase Invoice' and not self.get('update_stock'):
|
if self.doctype == 'Purchase Invoice' and not self.get('update_stock'):
|
||||||
return
|
return
|
||||||
|
|
||||||
frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name)
|
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):
|
def validate_schedule_date(self):
|
||||||
if not self.get("items"):
|
if not self.get("items"):
|
||||||
@ -764,7 +735,7 @@ def get_backflushed_subcontracted_raw_materials_from_se(purchase_orders, purchas
|
|||||||
|
|
||||||
def get_asset_item_details(asset_items):
|
def get_asset_item_details(asset_items):
|
||||||
asset_items_data = {}
|
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)}):
|
filters = {'name': ('in', asset_items)}):
|
||||||
asset_items_data.setdefault(d.name, d)
|
asset_items_data.setdefault(d.name, d)
|
||||||
|
|
||||||
|
@ -476,3 +476,29 @@ def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters)
|
|||||||
as_list=1
|
as_list=1
|
||||||
)
|
)
|
||||||
return item_manufacturers
|
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)
|
||||||
|
@ -4,11 +4,11 @@ cur_frm.add_fetch("employee", "image", "image");
|
|||||||
frappe.ui.form.on("Instructor", {
|
frappe.ui.form.on("Instructor", {
|
||||||
employee: function(frm) {
|
employee: function(frm) {
|
||||||
if(!frm.doc.employee) return;
|
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() {
|
frm.set_query("department", function() {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
"filters": {
|
||||||
"company": company,
|
"company": d.company,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
@ -16,7 +16,7 @@ frappe.ui.form.on("Instructor", {
|
|||||||
frm.set_query("department", "instructor_log", function() {
|
frm.set_query("department", "instructor_log", function() {
|
||||||
return {
|
return {
|
||||||
"filters": {
|
"filters": {
|
||||||
"company": company,
|
"company": d.company,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -45,7 +45,7 @@ class TestEmployee(unittest.TestCase):
|
|||||||
employee1_doc.status = 'Left'
|
employee1_doc.status = 'Left'
|
||||||
self.assertRaises(EmployeeLeftValidationError, employee1_doc.save)
|
self.assertRaises(EmployeeLeftValidationError, employee1_doc.save)
|
||||||
|
|
||||||
def make_employee(user):
|
def make_employee(user, company=None):
|
||||||
if not frappe.db.get_value("User", user):
|
if not frappe.db.get_value("User", user):
|
||||||
frappe.get_doc({
|
frappe.get_doc({
|
||||||
"doctype": "User",
|
"doctype": "User",
|
||||||
@ -55,12 +55,12 @@ def make_employee(user):
|
|||||||
"roles": [{"doctype": "Has Role", "role": "Employee"}]
|
"roles": [{"doctype": "Has Role", "role": "Employee"}]
|
||||||
}).insert()
|
}).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({
|
employee = frappe.get_doc({
|
||||||
"doctype": "Employee",
|
"doctype": "Employee",
|
||||||
"naming_series": "EMP-",
|
"naming_series": "EMP-",
|
||||||
"first_name": user,
|
"first_name": user,
|
||||||
"company": erpnext.get_default_company(),
|
"company": company or erpnext.get_default_company(),
|
||||||
"user_id": user,
|
"user_id": user,
|
||||||
"date_of_birth": "1990-05-08",
|
"date_of_birth": "1990-05-08",
|
||||||
"date_of_joining": "2013-01-01",
|
"date_of_joining": "2013-01-01",
|
||||||
|
@ -216,7 +216,9 @@ class WorkOrder(Document):
|
|||||||
self.db_set(fieldname, qty)
|
self.db_set(fieldname, qty)
|
||||||
|
|
||||||
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
|
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:
|
if self.production_plan:
|
||||||
self.update_production_plan_status()
|
self.update_production_plan_status()
|
||||||
|
@ -17,10 +17,6 @@ def execute():
|
|||||||
frappe.db.sql(""" update `tabAsset` ast, `tabWarehouse` wh
|
frappe.db.sql(""" update `tabAsset` ast, `tabWarehouse` wh
|
||||||
set ast.location = wh.warehouse_name where ast.warehouse = wh.name""")
|
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'):
|
for d in frappe.get_all('Asset'):
|
||||||
doc = frappe.get_doc('Asset', d.name)
|
doc = frappe.get_doc('Asset', d.name)
|
||||||
if doc.calculate_depreciation:
|
if doc.calculate_depreciation:
|
||||||
|
@ -3,8 +3,12 @@ from frappe.utils import flt
|
|||||||
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
|
from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item
|
||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
frappe.reload_doctype('Sales Order Item')
|
frappe.reload_doctype('Sales Order Item')
|
||||||
frappe.reload_doctype('Sales Order')
|
frappe.reload_doctype('Sales Order')
|
||||||
sales_order_items = frappe.db.get_all('Sales Order Item', ['name'])
|
|
||||||
for so_item in sales_order_items:
|
for d in frappe.get_all('Work Order',
|
||||||
update_produced_qty_in_so_item(so_item.get('name'))
|
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)
|
@ -1038,14 +1038,18 @@ def create_pick_list(source_name, target_doc=None):
|
|||||||
|
|
||||||
return doc
|
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
|
#for multiple work orders against same sales order item
|
||||||
linked_wo_with_so_item = frappe.db.get_all('Work Order', ['produced_qty'], {
|
linked_wo_with_so_item = frappe.db.get_all('Work Order', ['produced_qty'], {
|
||||||
'sales_order_item': sales_order_item,
|
'sales_order_item': sales_order_item,
|
||||||
|
'sales_order': sales_order,
|
||||||
'docstatus': 1
|
'docstatus': 1
|
||||||
})
|
})
|
||||||
if len(linked_wo_with_so_item) > 0:
|
|
||||||
total_produced_qty = 0
|
total_produced_qty = 0
|
||||||
for wo in linked_wo_with_so_item:
|
for wo in linked_wo_with_so_item:
|
||||||
total_produced_qty += flt(wo.get('produced_qty'))
|
total_produced_qty += flt(wo.get('produced_qty'))
|
||||||
frappe.db.set_value('Sales Order Item', sales_order_item, 'produced_qty', total_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)
|
@ -25,7 +25,7 @@ frappe.ui.form.on("Item", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
if(frm.doc.is_stock_item) {
|
if (frm.doc.is_stock_item) {
|
||||||
frm.add_custom_button(__("Balance"), function() {
|
frm.add_custom_button(__("Balance"), function() {
|
||||||
frappe.route_options = {
|
frappe.route_options = {
|
||||||
"item_code": frm.doc.name
|
"item_code": frm.doc.name
|
||||||
@ -46,10 +46,15 @@ frappe.ui.form.on("Item", {
|
|||||||
}, __("View"));
|
}, __("View"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!frm.doc.is_fixed_asset) {
|
if (!frm.doc.is_fixed_asset) {
|
||||||
erpnext.item.make_dashboard(frm);
|
erpnext.item.make_dashboard(frm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frm.doc.is_fixed_asset) {
|
||||||
|
frm.trigger('is_fixed_asset');
|
||||||
|
frm.trigger('auto_create_assets');
|
||||||
|
}
|
||||||
|
|
||||||
// clear intro
|
// clear intro
|
||||||
frm.set_intro();
|
frm.set_intro();
|
||||||
|
|
||||||
@ -132,6 +137,11 @@ frappe.ui.form.on("Item", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
is_fixed_asset: function(frm) {
|
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({
|
frm.call({
|
||||||
method: "set_asset_naming_series",
|
method: "set_asset_naming_series",
|
||||||
doc: frm.doc,
|
doc: frm.doc,
|
||||||
@ -139,7 +149,7 @@ frappe.ui.form.on("Item", {
|
|||||||
frm.set_value("is_stock_item", frm.doc.is_fixed_asset ? 0 : 1);
|
frm.set_value("is_stock_item", frm.doc.is_fixed_asset ? 0 : 1);
|
||||||
frm.trigger("set_asset_naming_series");
|
frm.trigger("set_asset_naming_series");
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
set_asset_naming_series: function(frm) {
|
set_asset_naming_series: function(frm) {
|
||||||
@ -148,6 +158,11 @@ frappe.ui.form.on("Item", {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
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,
|
page_name: frappe.utils.warn_page_name_change,
|
||||||
|
|
||||||
item_code: function(frm) {
|
item_code: function(frm) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,6 +13,7 @@
|
|||||||
"qty",
|
"qty",
|
||||||
"rate",
|
"rate",
|
||||||
"amount",
|
"amount",
|
||||||
|
"is_fixed_asset",
|
||||||
"applicable_charges",
|
"applicable_charges",
|
||||||
"purchase_receipt_item",
|
"purchase_receipt_item",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
@ -119,14 +120,25 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "dimension_col_break",
|
"fieldname": "dimension_col_break",
|
||||||
"fieldtype": "Column 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,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-05-26 09:48:15.569956",
|
"modified": "2019-11-12 15:41:21.053462",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Landed Cost Item",
|
"name": "Landed Cost Item",
|
||||||
"owner": "wasim@webnotestech.com",
|
"owner": "wasim@webnotestech.com",
|
||||||
"permissions": []
|
"permissions": [],
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC"
|
||||||
}
|
}
|
@ -129,6 +129,10 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
|||||||
},
|
},
|
||||||
distribute_charges_based_on: function (frm) {
|
distribute_charges_based_on: function (frm) {
|
||||||
this.set_applicable_charges_for_item();
|
this.set_applicable_charges_for_item();
|
||||||
|
},
|
||||||
|
|
||||||
|
items_remove: () => {
|
||||||
|
this.trigger('set_applicable_charges_for_item');
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,516 +1,132 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "naming_series:",
|
"autoname": "naming_series:",
|
||||||
"beta": 0,
|
|
||||||
"creation": "2014-07-11 11:33:42.547339",
|
"creation": "2014-07-11 11:33:42.547339",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"editable_grid": 0,
|
|
||||||
"engine": "InnoDB",
|
"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": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"default": "",
|
|
||||||
"fieldname": "naming_series",
|
"fieldname": "naming_series",
|
||||||
"fieldtype": "Select",
|
"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",
|
"label": "Series",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "MAT-LCV-.YYYY.-",
|
"options": "MAT-LCV-.YYYY.-",
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 0,
|
"set_only_once": 1
|
||||||
"set_only_once": 1,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company",
|
"options": "Company",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 1,
|
"remember_last_selected_value": 1,
|
||||||
"report_hide": 0,
|
"reqd": 1
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "purchase_receipts",
|
"fieldname": "purchase_receipts",
|
||||||
"fieldtype": "Table",
|
"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",
|
"label": "Purchase Receipts",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Landed Cost Purchase Receipt",
|
"options": "Landed Cost Purchase Receipt",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "purchase_receipt_items",
|
"fieldname": "purchase_receipt_items",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Purchase Receipt Items"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "get_items_from_purchase_receipts",
|
||||||
"fieldtype": "Button",
|
"fieldtype": "Button",
|
||||||
"hidden": 0,
|
"label": "Get Items From Purchase Receipts"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "items",
|
"fieldname": "items",
|
||||||
"fieldtype": "Table",
|
"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",
|
"label": "Purchase Receipt Items",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Landed Cost Item",
|
"options": "Landed Cost Item",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "sec_break1",
|
"fieldname": "sec_break1",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Applicable Charges"
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "taxes",
|
"fieldname": "taxes",
|
||||||
"fieldtype": "Table",
|
"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",
|
"label": "Taxes and Charges",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Landed Cost Taxes and Charges",
|
"options": "Landed Cost Taxes and Charges",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"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,
|
|
||||||
"fieldname": "section_break_9",
|
"fieldname": "section_break_9",
|
||||||
"fieldtype": "Section Break",
|
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "total_taxes_and_charges",
|
"fieldname": "total_taxes_and_charges",
|
||||||
"fieldtype": "Currency",
|
"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",
|
"label": "Total Taxes and Charges",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 1,
|
"read_only": 1,
|
||||||
"remember_last_selected_value": 0,
|
"reqd": 1
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "col_break1",
|
"fieldname": "col_break1",
|
||||||
"fieldtype": "Column Break",
|
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"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",
|
"fieldname": "distribute_charges_based_on",
|
||||||
"fieldtype": "Select",
|
"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",
|
"label": "Distribute Charges Based On",
|
||||||
"length": 0,
|
"options": "Qty\nAmount",
|
||||||
"no_copy": 0,
|
"reqd": 1
|
||||||
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
"fieldtype": "Link",
|
"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",
|
"label": "Amended From",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Landed Cost Voucher",
|
"options": "Landed Cost Voucher",
|
||||||
"permlevel": 0,
|
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"print_hide_if_no_value": 0,
|
"read_only": 1
|
||||||
"read_only": 1,
|
|
||||||
"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,
|
|
||||||
"fieldname": "sec_break2",
|
"fieldname": "sec_break2",
|
||||||
"fieldtype": "Section Break",
|
"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
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "landed_cost_help",
|
"fieldname": "landed_cost_help",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"hidden": 0,
|
"label": "Landed Cost Help"
|
||||||
"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
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"icon": "icon-usd",
|
"icon": "icon-usd",
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"issingle": 0,
|
"modified": "2019-10-09 13:39:36.082777",
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2018-08-21 14:44:30.850736",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Landed Cost Voucher",
|
"name": "Landed Cost Voucher",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
@ -518,28 +134,16 @@
|
|||||||
"cancel": 1,
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 0,
|
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 0,
|
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Stock Manager",
|
"role": "Stock Manager",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 1,
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC"
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -16,16 +16,13 @@ class LandedCostVoucher(Document):
|
|||||||
if pr.receipt_document_type and pr.receipt_document:
|
if pr.receipt_document_type and pr.receipt_document:
|
||||||
pr_items = frappe.db.sql("""select pr_item.item_code, pr_item.description,
|
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.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
|
from `tab{doctype} Item` pr_item where parent = %s
|
||||||
and exists(select name from tabItem
|
and exists(select name from tabItem
|
||||||
where name = pr_item.item_code and (is_stock_item = 1 or is_fixed_asset=1))
|
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)
|
""".format(doctype=pr.receipt_document_type), pr.receipt_document, as_dict=True)
|
||||||
|
|
||||||
for d in pr_items:
|
for d in pr_items:
|
||||||
if d.asset and frappe.db.get_value("Asset", d.asset, 'docstatus') == 1:
|
|
||||||
continue
|
|
||||||
|
|
||||||
item = self.append("items")
|
item = self.append("items")
|
||||||
item.item_code = d.item_code
|
item.item_code = d.item_code
|
||||||
item.description = d.description
|
item.description = d.description
|
||||||
@ -37,15 +34,16 @@ class LandedCostVoucher(Document):
|
|||||||
item.receipt_document_type = pr.receipt_document_type
|
item.receipt_document_type = pr.receipt_document_type
|
||||||
item.receipt_document = pr.receipt_document
|
item.receipt_document = pr.receipt_document
|
||||||
item.purchase_receipt_item = d.name
|
item.purchase_receipt_item = d.name
|
||||||
|
item.is_fixed_asset = d.is_fixed_asset
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.check_mandatory()
|
self.check_mandatory()
|
||||||
self.validate_purchase_receipts()
|
|
||||||
self.set_total_taxes_and_charges()
|
|
||||||
if not self.get("items"):
|
if not self.get("items"):
|
||||||
self.get_items_from_purchase_receipts()
|
self.get_items_from_purchase_receipts()
|
||||||
else:
|
else:
|
||||||
self.validate_applicable_charges_for_item()
|
self.validate_applicable_charges_for_item()
|
||||||
|
self.validate_purchase_receipts()
|
||||||
|
self.set_total_taxes_and_charges()
|
||||||
|
|
||||||
def check_mandatory(self):
|
def check_mandatory(self):
|
||||||
if not self.get("purchase_receipts"):
|
if not self.get("purchase_receipts"):
|
||||||
@ -64,6 +62,7 @@ class LandedCostVoucher(Document):
|
|||||||
for item in self.get("items"):
|
for item in self.get("items"):
|
||||||
if not item.receipt_document:
|
if not item.receipt_document:
|
||||||
frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button"))
|
frappe.throw(_("Item must be added using 'Get Items from Purchase Receipts' button"))
|
||||||
|
|
||||||
elif item.receipt_document not in receipt_documents:
|
elif item.receipt_document not in receipt_documents:
|
||||||
frappe.throw(_("Item Row {0}: {1} {2} does not exist in above '{1}' table")
|
frappe.throw(_("Item Row {0}: {1} {2} does not exist in above '{1}' table")
|
||||||
.format(item.idx, item.receipt_document_type, item.receipt_document))
|
.format(item.idx, item.receipt_document_type, item.receipt_document))
|
||||||
@ -96,8 +95,6 @@ class LandedCostVoucher(Document):
|
|||||||
else:
|
else:
|
||||||
frappe.throw(_("Total Applicable Charges in Purchase Receipt Items table must be same as Total Taxes and Charges"))
|
frappe.throw(_("Total Applicable Charges in Purchase Receipt Items table must be same as Total Taxes and Charges"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.update_landed_cost()
|
self.update_landed_cost()
|
||||||
|
|
||||||
@ -108,6 +105,9 @@ class LandedCostVoucher(Document):
|
|||||||
for d in self.get("purchase_receipts"):
|
for d in self.get("purchase_receipts"):
|
||||||
doc = frappe.get_doc(d.receipt_document_type, d.receipt_document)
|
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
|
# set landed cost voucher amount in pr item
|
||||||
doc.set_landed_cost_voucher_amount()
|
doc.set_landed_cost_voucher_amount()
|
||||||
|
|
||||||
@ -118,23 +118,42 @@ class LandedCostVoucher(Document):
|
|||||||
for item in doc.get("items"):
|
for item in doc.get("items"):
|
||||||
item.db_update()
|
item.db_update()
|
||||||
|
|
||||||
|
# asset rate will be updated while creating asset gl entries from PI or PY
|
||||||
|
|
||||||
# update latest valuation rate in serial no
|
# 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
|
# update stock & gl entries for cancelled state of PR
|
||||||
doc.docstatus = 2
|
doc.docstatus = 2
|
||||||
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
|
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
|
||||||
doc.make_gl_entries_on_cancel(repost_future_gle=False)
|
doc.make_gl_entries_on_cancel(repost_future_gle=False)
|
||||||
|
|
||||||
|
|
||||||
# update stock & gl entries for submit state of PR
|
# update stock & gl entries for submit state of PR
|
||||||
doc.docstatus = 1
|
doc.docstatus = 1
|
||||||
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
|
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
|
||||||
doc.make_gl_entries()
|
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"):
|
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)
|
serial_nos = get_serial_nos(item.serial_no)
|
||||||
if serial_nos:
|
if serial_nos:
|
||||||
frappe.db.sql("update `tabSerial No` set purchase_rate=%s where name in ({0})"
|
frappe.db.sql("update `tabSerial No` set purchase_rate=%s where name in ({0})"
|
||||||
|
@ -54,9 +54,10 @@ class TestLandedCostVoucher(unittest.TestCase):
|
|||||||
expected_values = {
|
expected_values = {
|
||||||
stock_in_hand_account: [800.0, 0.0],
|
stock_in_hand_account: [800.0, 0.0],
|
||||||
"Stock Received But Not Billed - TCP1": [0.0, 500.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:
|
else:
|
||||||
expected_values = {
|
expected_values = {
|
||||||
stock_in_hand_account: [400.0, 0.0],
|
stock_in_hand_account: [400.0, 0.0],
|
||||||
|
@ -6,24 +6,32 @@
|
|||||||
frappe.provide("erpnext.stock");
|
frappe.provide("erpnext.stock");
|
||||||
|
|
||||||
frappe.ui.form.on("Purchase Receipt", {
|
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 = {
|
frm.custom_make_buttons = {
|
||||||
'Stock Entry': 'Return',
|
'Stock Entry': 'Return',
|
||||||
'Purchase Invoice': 'Invoice'
|
'Purchase Invoice': 'Invoice'
|
||||||
};
|
};
|
||||||
|
|
||||||
frm.set_query("asset", "items", function() {
|
|
||||||
return {
|
|
||||||
filters: {
|
|
||||||
"purchase_receipt": frm.doc.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frm.set_query("expense_account", "items", function() {
|
frm.set_query("expense_account", "items", function() {
|
||||||
return {
|
return {
|
||||||
query: "erpnext.controllers.queries.get_expense_account",
|
query: "erpnext.controllers.queries.get_expense_account",
|
||||||
filters: {'company': frm.doc.company}
|
filters: {'company': frm.doc.company }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -57,7 +65,7 @@ frappe.ui.form.on("Purchase Receipt", {
|
|||||||
toggle_display_account_head: function(frm) {
|
toggle_display_account_head: function(frm) {
|
||||||
var enabled = erpnext.is_perpetual_inventory_enabled(frm.doc.company)
|
var enabled = erpnext.is_perpetual_inventory_enabled(frm.doc.company)
|
||||||
frm.fields_dict["items"].grid.set_column_disp(["cost_center"], enabled);
|
frm.fields_dict["items"].grid.set_column_disp(["cost_center"], enabled);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({
|
erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend({
|
||||||
|
@ -281,15 +281,15 @@ class PurchaseReceipt(BuyingController):
|
|||||||
d.rejected_warehouse not in warehouse_with_no_account:
|
d.rejected_warehouse not in warehouse_with_no_account:
|
||||||
warehouse_with_no_account.append(d.warehouse)
|
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
|
# Cost center-wise amount breakup for other charges included for valuation
|
||||||
valuation_tax = {}
|
valuation_tax = {}
|
||||||
for tax in self.get("taxes"):
|
for tax in self.get("taxes"):
|
||||||
if tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
if tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
|
||||||
if not tax.cost_center:
|
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)))
|
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.setdefault(tax.name, 0)
|
||||||
valuation_tax[tax.cost_center] += \
|
valuation_tax[tax.name] += \
|
||||||
(tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount)
|
(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:
|
if negative_expense_to_be_booked and valuation_tax:
|
||||||
@ -297,37 +297,42 @@ class PurchaseReceipt(BuyingController):
|
|||||||
# If expenses_included_in_valuation account has been credited in against PI
|
# If expenses_included_in_valuation account has been credited in against PI
|
||||||
# and charges added via Landed Cost Voucher,
|
# and charges added via Landed Cost Voucher,
|
||||||
# post valuation related charges on "Stock Received But Not Billed"
|
# 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
|
negative_expense_booked_in_pi = frappe.db.sql("""select name from `tabPurchase Invoice Item` pi
|
||||||
where docstatus = 1 and purchase_receipt=%s
|
where docstatus = 1 and purchase_receipt=%s
|
||||||
and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice'
|
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))
|
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])
|
against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
|
||||||
total_valuation_amount = sum(valuation_tax.values())
|
total_valuation_amount = sum(valuation_tax.values())
|
||||||
amount_including_divisional_loss = negative_expense_to_be_booked
|
amount_including_divisional_loss = negative_expense_to_be_booked
|
||||||
i = 1
|
i = 1
|
||||||
for cost_center, amount in iteritems(valuation_tax):
|
for tax in self.get("taxes"):
|
||||||
if i == len(valuation_tax):
|
if valuation_tax.get(tax.name):
|
||||||
applicable_amount = amount_including_divisional_loss
|
|
||||||
else:
|
|
||||||
applicable_amount = negative_expense_to_be_booked * (amount / total_valuation_amount)
|
|
||||||
amount_including_divisional_loss -= applicable_amount
|
|
||||||
|
|
||||||
gl_entries.append(
|
if negative_expense_booked_in_pi:
|
||||||
self.get_gl_dict({
|
account = stock_rbnb
|
||||||
"account": expenses_included_in_valuation,
|
else:
|
||||||
"cost_center": cost_center,
|
account = tax.account_head
|
||||||
"credit": applicable_amount,
|
|
||||||
"remarks": self.remarks or _("Accounting Entry for Stock"),
|
|
||||||
"against": against_account
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
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:
|
if warehouse_with_no_account:
|
||||||
frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
|
frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
|
||||||
@ -335,82 +340,86 @@ class PurchaseReceipt(BuyingController):
|
|||||||
|
|
||||||
return process_gl_map(gl_entries)
|
return process_gl_map(gl_entries)
|
||||||
|
|
||||||
def get_asset_gl_entry(self, gl_entries, expenses_included_in_valuation=None):
|
def get_asset_gl_entry(self, gl_entries):
|
||||||
arbnb_account, cwip_account = None, None
|
for item in self.get("items"):
|
||||||
|
if item.is_fixed_asset:
|
||||||
if not expenses_included_in_valuation:
|
if is_cwip_accounting_enabled(self.company, item.asset_category):
|
||||||
expenses_included_in_valuation = self.get_company_default("expenses_included_in_valuation")
|
self.add_asset_gl_entries(item, gl_entries)
|
||||||
|
if flt(item.landed_cost_voucher_amount):
|
||||||
for d in self.get("items"):
|
self.add_lcv_gl_entries(item, gl_entries)
|
||||||
asset_category = frappe.get_cached_value("Item", d.item_code, "asset_category")
|
# update assets gross amount by its valuation rate
|
||||||
cwip_enabled = is_cwip_accounting_enabled(self.company, asset_category)
|
# valuation rate is total of net rate, raw mat supp cost, tax amount, lcv amount per item
|
||||||
|
self.update_assets(item, item.valuation_rate)
|
||||||
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 cwip_enabled:
|
|
||||||
asset_amount = flt(d.net_amount) + flt(d.item_tax_amount/self.conversion_rate)
|
|
||||||
base_asset_amount = flt(d.base_net_amount + d.item_tax_amount)
|
|
||||||
|
|
||||||
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 not cwip_enabled 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))
|
|
||||||
|
|
||||||
return gl_entries
|
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):
|
def update_status(self, status):
|
||||||
self.set_status(update=True, status = status)
|
self.set_status(update=True, status = status)
|
||||||
self.notify_update()
|
self.notify_update()
|
||||||
@ -517,7 +526,8 @@ def make_purchase_invoice(source_name, target_doc=None):
|
|||||||
"purchase_order_item": "po_detail",
|
"purchase_order_item": "po_detail",
|
||||||
"purchase_order": "purchase_order",
|
"purchase_order": "purchase_order",
|
||||||
"is_fixed_asset": "is_fixed_asset",
|
"is_fixed_asset": "is_fixed_asset",
|
||||||
"asset": "asset",
|
"asset_location": "asset_location",
|
||||||
|
"asset_category": 'asset_category'
|
||||||
},
|
},
|
||||||
"postprocess": update_item,
|
"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
|
"filter": lambda d: get_pending_qty(d)[0] <= 0 if not doc.get("is_return") else get_pending_qty(d)[0] > 0
|
||||||
|
@ -66,14 +66,15 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
expected_values = {
|
expected_values = {
|
||||||
stock_in_hand_account: [750.0, 0.0],
|
stock_in_hand_account: [750.0, 0.0],
|
||||||
"Stock Received But Not Billed - TCP1": [0.0, 500.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:
|
else:
|
||||||
expected_values = {
|
expected_values = {
|
||||||
stock_in_hand_account: [375.0, 0.0],
|
stock_in_hand_account: [375.0, 0.0],
|
||||||
fixed_asset_account: [375.0, 0.0],
|
fixed_asset_account: [375.0, 0.0],
|
||||||
"Stock Received But Not Billed - TCP1": [0.0, 500.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:
|
for gle in gl_entries:
|
||||||
self.assertEqual(expected_values[gle.account][0], gle.debit)
|
self.assertEqual(expected_values[gle.account][0], gle.debit)
|
||||||
@ -281,8 +282,8 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
serial_no=serial_no, basic_rate=100, do_not_submit=True)
|
serial_no=serial_no, basic_rate=100, do_not_submit=True)
|
||||||
self.assertRaises(SerialNoDuplicateError, se.submit)
|
self.assertRaises(SerialNoDuplicateError, se.submit)
|
||||||
|
|
||||||
def test_serialized_asset_item(self):
|
def test_auto_asset_creation(self):
|
||||||
asset_item = "Test Serialized Asset Item"
|
asset_item = "Test Asset Item"
|
||||||
|
|
||||||
if not frappe.db.exists('Item', asset_item):
|
if not frappe.db.exists('Item', asset_item):
|
||||||
asset_category = frappe.get_all('Asset Category')
|
asset_category = frappe.get_all('Asset Category')
|
||||||
@ -308,30 +309,18 @@ class TestPurchaseReceipt(unittest.TestCase):
|
|||||||
asset_category = doc.name
|
asset_category = doc.name
|
||||||
|
|
||||||
item_data = make_item(asset_item, {'is_stock_item':0,
|
item_data = make_item(asset_item, {'is_stock_item':0,
|
||||||
'stock_uom': 'Box', 'is_fixed_asset': 1, 'has_serial_no': 1,
|
'stock_uom': 'Box', 'is_fixed_asset': 1, 'auto_create_assets': 1,
|
||||||
'asset_category': asset_category, 'serial_no_series': 'ABC.###'})
|
'asset_category': asset_category, 'asset_naming_series': 'ABC.###'})
|
||||||
asset_item = item_data.item_code
|
asset_item = item_data.item_code
|
||||||
|
|
||||||
pr = make_purchase_receipt(item_code=asset_item, qty=3)
|
pr = make_purchase_receipt(item_code=asset_item, qty=3)
|
||||||
asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name}, 'name')
|
assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name})
|
||||||
asset_movement = frappe.db.get_value('Asset Movement', {'reference_name': pr.name}, 'name')
|
|
||||||
serial_nos = frappe.get_all('Serial No', {'asset': asset}, '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")
|
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):
|
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
|
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
|
||||||
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
accounts_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||||
@ -534,8 +523,10 @@ def make_purchase_receipt(**args):
|
|||||||
received_qty = args.received_qty or qty
|
received_qty = args.received_qty or qty
|
||||||
rejected_qty = args.rejected_qty or flt(received_qty) - flt(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", {
|
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",
|
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||||
"qty": qty,
|
"qty": qty,
|
||||||
"received_qty": received_qty,
|
"received_qty": received_qty,
|
||||||
@ -545,7 +536,7 @@ def make_purchase_receipt(**args):
|
|||||||
"conversion_factor": args.conversion_factor or 1.0,
|
"conversion_factor": args.conversion_factor or 1.0,
|
||||||
"serial_no": args.serial_no,
|
"serial_no": args.serial_no,
|
||||||
"stock_uom": args.stock_uom or "_Test UOM",
|
"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'),
|
"cost_center": args.cost_center or frappe.get_cached_value('Company', pr.company, 'cost_center'),
|
||||||
"asset_location": args.location or "Test Location"
|
"asset_location": args.location or "Test Location"
|
||||||
})
|
})
|
||||||
|
@ -67,26 +67,26 @@
|
|||||||
"warehouse_and_reference",
|
"warehouse_and_reference",
|
||||||
"warehouse",
|
"warehouse",
|
||||||
"rejected_warehouse",
|
"rejected_warehouse",
|
||||||
"quality_inspection",
|
|
||||||
"purchase_order",
|
"purchase_order",
|
||||||
"material_request",
|
"material_request",
|
||||||
"purchase_order_item",
|
|
||||||
"material_request_item",
|
|
||||||
"column_break_40",
|
"column_break_40",
|
||||||
"is_fixed_asset",
|
"is_fixed_asset",
|
||||||
"asset",
|
|
||||||
"asset_location",
|
"asset_location",
|
||||||
|
"asset_category",
|
||||||
"schedule_date",
|
"schedule_date",
|
||||||
|
"quality_inspection",
|
||||||
"stock_qty",
|
"stock_qty",
|
||||||
|
"purchase_order_item",
|
||||||
|
"material_request_item",
|
||||||
"section_break_45",
|
"section_break_45",
|
||||||
|
"allow_zero_valuation_rate",
|
||||||
|
"bom",
|
||||||
|
"col_break5",
|
||||||
"serial_no",
|
"serial_no",
|
||||||
"batch_no",
|
"batch_no",
|
||||||
"column_break_48",
|
"column_break_48",
|
||||||
"rejected_serial_no",
|
"rejected_serial_no",
|
||||||
"expense_account",
|
"expense_account",
|
||||||
"col_break5",
|
|
||||||
"allow_zero_valuation_rate",
|
|
||||||
"bom",
|
|
||||||
"include_exploded_items",
|
"include_exploded_items",
|
||||||
"item_tax_rate",
|
"item_tax_rate",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
@ -500,21 +500,6 @@
|
|||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"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",
|
"fieldname": "purchase_order",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
@ -553,6 +538,7 @@
|
|||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "serial_no",
|
"fieldname": "serial_no",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
@ -562,10 +548,11 @@
|
|||||||
"oldfieldtype": "Text"
|
"oldfieldtype": "Text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "batch_no",
|
"fieldname": "batch_no",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Batch No",
|
"label": "Batch No!",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "batch_no",
|
"oldfieldname": "batch_no",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
@ -577,6 +564,7 @@
|
|||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:!doc.is_fixed_asset",
|
||||||
"fieldname": "rejected_serial_no",
|
"fieldname": "rejected_serial_no",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Rejected Serial No",
|
"label": "Rejected Serial No",
|
||||||
@ -814,11 +802,28 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Manufacturer Part Number",
|
"label": "Manufacturer Part Number",
|
||||||
"read_only": 1
|
"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,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-09-17 22:33:01.109004",
|
"modified": "2019-10-14 16:03:25.499557",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Stock",
|
"module": "Stock",
|
||||||
"name": "Purchase Receipt Item",
|
"name": "Purchase Receipt Item",
|
||||||
|
Loading…
Reference in New Issue
Block a user