fix: merge conflict
diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index d031bc5..f40b957 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '12.1.8'
+__version__ = '12.2.0'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 75107b0..3bb3df8 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -357,7 +357,7 @@
return
if not gl_entries:
gl_entries = self.get_gl_entries()
-
+
if gl_entries:
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
@@ -507,7 +507,7 @@
elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)):
expense_account = (item.expense_account
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
-
+
if not item.is_fixed_asset:
amount = flt(item.base_net_amount, item.precision("base_net_amount"))
else:
@@ -520,7 +520,7 @@
"cost_center": item.cost_center,
"project": item.project
}, 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")
@@ -542,9 +542,9 @@
"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',
+ assets = frappe.db.get_all('Asset',
filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
)
for asset in assets:
@@ -636,7 +636,7 @@
if asset_eiiav_currency == self.company_currency else
item.item_tax_amount / self.conversion_rate)
}, item=item))
-
+
# When update stock is checked
# Assets are bought through this document then it will be linked to this document
if self.update_stock:
@@ -658,9 +658,9 @@
"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',
+ assets = frappe.db.get_all('Asset',
filters={ 'purchase_invoice': self.name, 'item_code': item.item_code }
)
for asset in assets:
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 7a48fcc..2ba319d 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -185,7 +185,7 @@
raise_exception=StockValueAndAccountBalanceOutOfSync,
title=_('Values Out Of Sync'),
primary_action={
- 'label': 'Make Journal Entry',
+ 'label': _('Make Journal Entry'),
'client_action': 'erpnext.route_to_adjustment_jv',
'args': journal_entry_args
})
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 382a89b..94697be 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -630,7 +630,7 @@
'select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()',
as_dict=1
)
- held_invoices = [d['name'] for d in held_invoices]
+ held_invoices = set([d['name'] for d in held_invoices])
return held_invoices
@@ -639,14 +639,19 @@
outstanding_invoices = []
precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2
- if erpnext.get_party_account_type(party_type) == 'Receivable':
+ if account:
+ root_type = frappe.get_cached_value("Account", account, "root_type")
+ party_account_type = "Receivable" if root_type == "Asset" else "Payable"
+ else:
+ party_account_type = erpnext.get_party_account_type(party_type)
+
+ if party_account_type == 'Receivable':
dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
else:
dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
payment_dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
- invoice = 'Sales Invoice' if erpnext.get_party_account_type(party_type) == 'Receivable' else 'Purchase Invoice'
held_invoices = get_held_invoices(party_type, party)
invoice_list = frappe.db.sql("""
@@ -665,7 +670,6 @@
group by voucher_type, voucher_no
order by posting_date, name""".format(
dr_or_cr=dr_or_cr,
- invoice = invoice,
condition=condition or ""
), {
"party_type": party_type,
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 546f374..40f1e1e 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -33,7 +33,7 @@
self.make_asset_movement()
if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.asset_category):
self.make_gl_entries()
-
+
def before_cancel(self):
self.cancel_auto_gen_movement()
@@ -43,7 +43,7 @@
self.set_status()
delete_gl_entries(voucher_type='Asset', voucher_no=self.name)
self.db_set('booked_fixed_asset', 0)
-
+
def validate_asset_and_reference(self):
if self.purchase_invoice or self.purchase_receipt:
reference_doc = 'Purchase Invoice' if self.purchase_invoice else 'Purchase Receipt'
@@ -51,8 +51,8 @@
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))
@@ -125,15 +125,17 @@
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:
+ movements = frappe.db.sql(
+ """SELECT asm.name, asm.docstatus
+ FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
+ WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""", self.name, as_dict=1)
+ if len(movements) > 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 = frappe.get_doc('Asset Movement', movements[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
@@ -202,7 +204,7 @@
if has_pro_rata and n==0:
depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount,
self.available_for_use_date, d.depreciation_start_date)
-
+
# For first depr schedule date will be the start date
# so monthly schedule date is calculated by removing month difference between use date and start date
monthly_schedule_date = add_months(d.depreciation_start_date, - months + 1)
@@ -260,7 +262,7 @@
else:
date = add_months(monthly_schedule_date, r)
amount = depreciation_amount / month_range
-
+
self.append("schedules", {
"schedule_date": date,
"depreciation_amount": amount,
@@ -650,31 +652,18 @@
def make_asset_movement(assets, purpose=None):
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.purpose = purpose
- prev_reference_docname = ''
-
+ asset_movement.quantity = len(assets)
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'),
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js
index 89977e2..06d8879 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.js
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.js
@@ -31,6 +31,13 @@
name: ["in", ["Purchase Receipt", "Purchase Invoice"]]
}
};
+ }),
+ frm.set_query("asset", "assets", () => {
+ return {
+ filters: {
+ status: ["not in", ["Draft"]]
+ }
+ }
})
},
@@ -76,50 +83,6 @@
});
});
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: {} }));
- }
}
});
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.json b/erpnext/assets/doctype/asset_movement/asset_movement.json
index e62d684..3472ab5 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.json
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.json
@@ -9,12 +9,12 @@
"purpose",
"column_break_4",
"transaction_date",
+ "section_break_10",
+ "assets",
"reference",
"reference_doctype",
"column_break_9",
"reference_name",
- "section_break_10",
- "assets",
"amended_from"
],
"fields": [
@@ -47,6 +47,7 @@
"fieldtype": "Column Break"
},
{
+ "collapsible": 1,
"fieldname": "reference",
"fieldtype": "Section Break",
"label": "Reference"
@@ -54,18 +55,16 @@
{
"fieldname": "reference_doctype",
"fieldtype": "Link",
- "label": "Reference Document",
+ "label": "Reference Document Type",
"no_copy": 1,
- "options": "DocType",
- "reqd": 1
+ "options": "DocType"
},
{
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"label": "Reference Document Name",
"no_copy": 1,
- "options": "reference_doctype",
- "reqd": 1
+ "options": "reference_doctype"
},
{
"fieldname": "amended_from",
@@ -93,7 +92,7 @@
}
],
"is_submittable": 1,
- "modified": "2019-11-21 14:35:51.880332",
+ "modified": "2019-11-23 13:28:47.256935",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Movement",
diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py
index 714845d..4e1822b 100644
--- a/erpnext/assets/doctype/asset_movement/asset_movement.py
+++ b/erpnext/assets/doctype/asset_movement/asset_movement.py
@@ -22,7 +22,7 @@
if company != self.company:
frappe.throw(_("Asset {0} does not belong to company {1}").format(d.asset, self.company))
- if not(d.source_location or d.target_location or d.from_employee or d.to_employee):
+ if not (d.source_location or d.target_location or d.from_employee or d.to_employee):
frappe.throw(_("Either location or employee must be required"))
def validate_location(self):
diff --git a/erpnext/change_log/v12/v12_2_0.md b/erpnext/change_log/v12/v12_2_0.md
new file mode 100644
index 0000000..0ec0eec
--- /dev/null
+++ b/erpnext/change_log/v12/v12_2_0.md
@@ -0,0 +1,14 @@
+# Version 12.2.0 Release Notes
+
+### Accounting
+
+1. Fixed Asset
+ - "Enable CWIP" options moved to Asset Category from Asset Settings
+ - Removed Asset link from Purchase Receipt Item table
+ - Enhanced Asset master
+ - Asset Movement now handles movement of multiple assets
+ - Introduced monthly depreciation
+2. GL Entries for Landed Cost Voucher now posted directly against individual Charges account
+3. Optimization of BOM Update Tool
+4. Syncing of Stock and Account balance is enforced, in case of perpetual inventory
+5. Rendered email template in Email Campaign
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 3392850..d12643a 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -641,7 +641,10 @@
asset = frappe.get_doc('Asset', asset.name)
if delete_asset and is_auto_create_enabled:
# need to delete movements to delete assets otherwise throws link exists error
- movements = frappe.db.get_all('Asset Movement', filters={ 'reference_name': self.name })
+ movements = frappe.db.sql(
+ """SELECT asm.name
+ FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item
+ WHERE asm_item.parent=asm.name and asm_item.asset=%s""", asset.name, as_dict=1)
for movement in movements:
frappe.delete_doc('Asset Movement', movement.name, force=1)
frappe.delete_doc("Asset", asset.name, force=1)
@@ -652,8 +655,12 @@
asset.purchase_date = self.posting_date
asset.supplier = self.supplier
elif self.docstatus == 2:
- asset.set(field, None)
- asset.supplier = None
+ if asset.docstatus == 0:
+ asset.set(field, None)
+ asset.supplier = None
+ if asset.docstatus == 1 and delete_asset:
+ frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}.\
+ Please cancel the it to continue.').format(asset.name))
asset.flags.ignore_validate_update_after_submit = True
asset.flags.ignore_mandatory = True
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index f003627..5939150 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -140,32 +140,6 @@
"against": ",".join([d.default_account for d in self.expenses]),
"party_type": "Employee",
"party": self.employee,
- "against_voucher_type": self.doctype,
- "against_voucher": self.name
- })
- )
-
- gl_entry.append(
- self.get_gl_dict({
- "account": data.advance_account,
- "debit": data.allocated_amount,
- "debit_in_account_currency": data.allocated_amount,
- "against": self.payable_account,
- "party_type": "Employee",
- "party": self.employee,
- "against_voucher_type": self.doctype,
- "against_voucher": self.name
- })
- )
-
- gl_entry.append(
- self.get_gl_dict({
- "account": self.payable_account,
- "credit": data.allocated_amount,
- "credit_in_account_currency": data.allocated_amount,
- "against": data.advance_account,
- "party_type": "Employee",
- "party": self.employee,
"against_voucher_type": "Employee Advance",
"against_voucher": data.employee_advance
})
diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py
index 54fce8d..7083d69 100755
--- a/erpnext/projects/doctype/task/task.py
+++ b/erpnext/projects/doctype/task/task.py
@@ -7,7 +7,7 @@
import frappe
from frappe import _, throw
-from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate
+from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate, today
from frappe.utils.nestedset import NestedSet
from frappe.desk.form.assign_to import close_all_assignments, clear
from frappe.utils import date_diff
@@ -212,8 +212,11 @@
task.save()
def set_tasks_as_overdue():
- tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Completed']]})
+ tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Closed']]})
for task in tasks:
+ if frappe.db.get_value("Task", task.name, "status") in 'Pending Review':
+ if getdate(frappe.db.get_value("Task", task.name, "review_date")) < getdate(today()):
+ continue
frappe.get_doc("Task", task.name).update_status()
@frappe.whitelist()