Merge branch 'develop' into iff-invoicing
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 52a5be0..f6d76e5 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -1,5 +1,4 @@
{
- "actions": [],
"autoname": "hash",
"creation": "2013-05-22 12:43:10",
"doctype": "DocType",
@@ -82,6 +81,7 @@
"item_tax_rate",
"bom",
"include_exploded_items",
+ "purchase_invoice_item",
"col_break6",
"purchase_order",
"po_detail",
@@ -769,12 +769,21 @@
"collapsible": 1,
"fieldname": "col_break7",
"fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval:parent.update_stock == 1",
+ "fieldname": "purchase_invoice_item",
+ "fieldtype": "Data",
+ "ignore_user_permissions": 1,
+ "label": "Purchase Invoice Item",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
}
],
"idx": 1,
"istable": 1,
- "links": [],
- "modified": "2020-04-22 10:37:35.103176",
+ "modified": "2020-08-20 11:48:01.398356",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index 004d358..fb3dd6a 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -1,5 +1,4 @@
{
- "actions": [],
"autoname": "hash",
"creation": "2013-06-04 11:02:19",
"doctype": "DocType",
@@ -87,6 +86,7 @@
"edit_references",
"sales_order",
"so_detail",
+ "sales_invoice_item",
"column_break_74",
"delivery_note",
"dn_detail",
@@ -790,12 +790,22 @@
"fieldtype": "Link",
"label": "Project",
"options": "Project"
- }
+ },
+ {
+ "depends_on": "eval:parent.update_stock == 1",
+ "fieldname": "sales_invoice_item",
+ "fieldtype": "Data",
+ "ignore_user_permissions": 1,
+ "label": "Sales Invoice Item",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ }
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-07-18 12:24:41.749986",
+ "modified": "2020-08-20 11:24:41.749986",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index c2c7207..219871b 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -378,7 +378,7 @@
if filters and filters.get('presentation_currency') != d.default_currency:
currency_info['company'] = d.name
currency_info['company_currency'] = d.default_currency
- convert_to_presentation_currency(gl_entries, currency_info)
+ convert_to_presentation_currency(gl_entries, currency_info, filters.get('company'))
for entry in gl_entries:
key = entry.account_number or entry.account_name
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index d5b8cdb..1b65a31 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -423,7 +423,7 @@
distributed_cost_center_query=distributed_cost_center_query), gl_filters, as_dict=True) #nosec
if filters and filters.get('presentation_currency'):
- convert_to_presentation_currency(gl_entries, get_currency(filters))
+ convert_to_presentation_currency(gl_entries, get_currency(filters), filters.get('company'))
for entry in gl_entries:
gl_entries_by_account.setdefault(entry.account, []).append(entry)
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index fcd36e4..0a72f6a 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -180,7 +180,7 @@
filters, as_dict=1)
if filters.get('presentation_currency'):
- return convert_to_presentation_currency(gl_entries, currency_map)
+ return convert_to_presentation_currency(gl_entries, currency_map, filters.get('company'))
else:
return gl_entries
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index 4a9af49..9de8d19 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -6,10 +6,6 @@
from frappe.utils import cint, get_datetime_str, formatdate, flt
__exchange_rates = {}
-P_OR_L_ACCOUNTS = list(
- sum(frappe.get_list('Account', fields=['name'], or_filters=[{'root_type': 'Income'}, {'root_type': 'Expense'}], as_list=True), ())
-)
-
def get_currency(filters):
"""
@@ -73,18 +69,7 @@
return rate
-
-def is_p_or_l_account(account_name):
- """
- Check if the given `account name` is an `Account` with `root_type` of either 'Income'
- or 'Expense'.
- :param account_name:
- :return: Boolean
- """
- return account_name in P_OR_L_ACCOUNTS
-
-
-def convert_to_presentation_currency(gl_entries, currency_info):
+def convert_to_presentation_currency(gl_entries, currency_info, company):
"""
Take a list of GL Entries and change the 'debit' and 'credit' values to currencies
in `currency_info`.
@@ -96,6 +81,9 @@
presentation_currency = currency_info['presentation_currency']
company_currency = currency_info['company_currency']
+ pl_accounts = [d.name for d in frappe.get_list('Account',
+ filters={'report_type': 'Profit and Loss', 'company': company})]
+
for entry in gl_entries:
account = entry['account']
debit = flt(entry['debit'])
@@ -107,7 +95,7 @@
if account_currency != presentation_currency:
value = debit or credit
- date = currency_info['report_date'] if not is_p_or_l_account(account) else entry['posting_date']
+ date = entry['posting_date'] if account in pl_accounts else currency_info['report_date']
converted_value = convert(value, presentation_currency, company_currency, date)
if entry.get('debit'):
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 89b48f0..f982700 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -559,9 +559,19 @@
"serial_no": cstr(d.serial_no).strip()
})
if self.is_return:
- original_incoming_rate = frappe.db.get_value("Stock Ledger Entry",
- {"voucher_type": "Purchase Receipt", "voucher_no": self.return_against,
- "item_code": d.item_code}, "incoming_rate")
+ filters = {
+ "voucher_type": self.doctype,
+ "voucher_no": self.return_against,
+ "item_code": d.item_code
+ }
+
+ if (self.doctype == "Purchase Invoice" and self.update_stock
+ and d.get("purchase_invoice_item")):
+ filters["voucher_detail_no"] = d.purchase_invoice_item
+ elif self.doctype == "Purchase Receipt" and d.get("purchase_receipt_item"):
+ filters["voucher_detail_no"] = d.purchase_receipt_item
+
+ original_incoming_rate = frappe.db.get_value("Stock Ledger Entry", filters, "incoming_rate")
sle.update({
"outgoing_rate": original_incoming_rate
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 3f127a2..a03dee1 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -281,6 +281,8 @@
target_doc.rejected_warehouse = source_doc.rejected_warehouse
target_doc.po_detail = source_doc.po_detail
target_doc.pr_detail = source_doc.pr_detail
+ target_doc.purchase_invoice_item = source_doc.name
+
elif doctype == "Delivery Note":
target_doc.against_sales_order = source_doc.against_sales_order
target_doc.against_sales_invoice = source_doc.against_sales_invoice
@@ -296,6 +298,7 @@
target_doc.so_detail = source_doc.so_detail
target_doc.dn_detail = source_doc.dn_detail
target_doc.expense_account = source_doc.expense_account
+ target_doc.sales_invoice_item = source_doc.name
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index b696ac3..17f3ae5 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -217,7 +217,9 @@
'target_warehouse': p.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
- 'allow_zero_valuation': d.allow_zero_valuation_rate
+ 'allow_zero_valuation': d.allow_zero_valuation_rate,
+ 'sales_invoice_item': d.get("sales_invoice_item"),
+ 'delivery_note_item': d.get("dn_detail")
}))
else:
il.append(frappe._dict({
@@ -233,7 +235,9 @@
'target_warehouse': d.target_warehouse,
'company': self.company,
'voucher_type': self.doctype,
- 'allow_zero_valuation': d.allow_zero_valuation_rate
+ 'allow_zero_valuation': d.allow_zero_valuation_rate,
+ 'sales_invoice_item': d.get("sales_invoice_item"),
+ 'delivery_note_item': d.get("dn_detail")
}))
return il
@@ -302,7 +306,11 @@
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
return_rate = 0
if cint(self.is_return) and self.return_against and self.docstatus==1:
- return_rate = self.get_incoming_rate_for_return(d.item_code, self.return_against)
+ against_document_no = (d.get("sales_invoice_item")
+ if self.doctype == "Sales Invoice" else d.get("delivery_note_item"))
+
+ return_rate = self.get_incoming_rate_for_return(d.item_code,
+ self.return_against, against_document_no)
# On cancellation or if return entry submission, make stock ledger entry for
# target warehouse first, to update serial no values properly
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index e8483da..394883d 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -301,14 +301,19 @@
return serialized_items
- def get_incoming_rate_for_return(self, item_code, against_document):
+ def get_incoming_rate_for_return(self, item_code, against_document, against_document_no=None):
incoming_rate = 0.0
+ cond = ''
if against_document and item_code:
+ if against_document_no:
+ cond = " and voucher_detail_no = %s" %(frappe.db.escape(against_document_no))
+
incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty)
from `tabStock Ledger Entry`
where voucher_type = %s and voucher_no = %s
- and item_code = %s limit 1""",
+ and item_code = %s {0} limit 1""".format(cond),
(self.doctype, against_document, item_code))
+
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
return incoming_rate
diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py
index e152850..6096053 100644
--- a/erpnext/crm/doctype/opportunity/opportunity.py
+++ b/erpnext/crm/doctype/opportunity/opportunity.py
@@ -325,7 +325,7 @@
doc.save()
@frappe.whitelist()
-def make_opportunity_from_communication(communication, ignore_communication_links=False):
+def make_opportunity_from_communication(communication, company, ignore_communication_links=False):
from erpnext.crm.doctype.lead.lead import make_lead_from_communication
doc = frappe.get_doc("Communication", communication)
@@ -337,6 +337,7 @@
opportunity = frappe.get_doc({
"doctype": "Opportunity",
+ "company": company,
"opportunity_from": opportunity_from,
"party_name": lead
}).insert(ignore_permissions=True)
diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py
index 3d68bc8..c397a3f 100644
--- a/erpnext/hr/doctype/job_offer/job_offer.py
+++ b/erpnext/hr/doctype/job_offer/job_offer.py
@@ -3,6 +3,7 @@
from __future__ import unicode_literals
import frappe
+from frappe.utils import cint
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from frappe import _
@@ -24,8 +25,7 @@
check_vacancies = frappe.get_single("HR Settings").check_vacancies
if staffing_plan and check_vacancies:
job_offers = self.get_job_offer(staffing_plan.from_date, staffing_plan.to_date)
-
- if not staffing_plan.get("vacancies") or staffing_plan.vacancies - len(job_offers) <= 0:
+ if not staffing_plan.get("vacancies") or cint(staffing_plan.vacancies) - len(job_offers) <= 0:
error_variable = 'for ' + frappe.bold(self.designation)
if staffing_plan.get("parent"):
error_variable = frappe.bold(get_link_to_form("Staffing Plan", staffing_plan.parent))
@@ -65,7 +65,7 @@
AND %s between sp.from_date and sp.to_date
""", (designation, company, offer_date), as_dict=1)
- return frappe._dict(detail[0]) if detail else None
+ return frappe._dict(detail[0]) if (detail and detail[0].parent) else None
@frappe.whitelist()
def make_employee(source_name, target_doc=None):
diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js
index 9432d42..26e5ab8 100644
--- a/erpnext/public/js/communication.js
+++ b/erpnext/public/js/communication.js
@@ -7,7 +7,7 @@
},
setup_custom_buttons: (frm) => {
- let confirm_msg = "Are you sure you want to create {0} from this email";
+ let confirm_msg = "Are you sure you want to create {0} from this email?";
if(frm.doc.reference_doctype !== "Issue") {
frm.add_custom_button(__("Issue"), () => {
frappe.confirm(__(confirm_msg, [__("Issue")]), () => {
@@ -62,17 +62,36 @@
},
make_opportunity_from_communication: (frm) => {
- return frappe.call({
- method: "erpnext.crm.doctype.opportunity.opportunity.make_opportunity_from_communication",
- args: {
- communication: frm.doc.name
- },
- freeze: true,
- callback: (r) => {
- if(r.message) {
- frm.reload_doc()
+ const fields = [{
+ fieldtype: 'Link',
+ label: __('Select a Company'),
+ fieldname: 'company',
+ options: 'Company',
+ reqd: 1,
+ default: frappe.defaults.get_user_default("Company")
+ }];
+
+ frappe.prompt(fields, data => {
+ frappe.call({
+ method: "erpnext.crm.doctype.opportunity.opportunity.make_opportunity_from_communication",
+ args: {
+ communication: frm.doc.name,
+ company: data.company
+ },
+ freeze: true,
+ callback: (r) => {
+ if(r.message) {
+ frm.reload_doc();
+ frappe.show_alert({
+ message: __("Opportunity {0} created",
+ ['<a href="#Form/Opportunity/'+r.message+'">' + r.message + '</a>']),
+ indicator: 'green'
+ });
+ }
}
- }
- })
+ });
+ },
+ 'Create an Opportunity',
+ 'Create');
}
-});
\ No newline at end of file
+});
diff --git a/erpnext/public/less/products.less b/erpnext/public/less/products.less
index 79f57b3..5e744ce 100644
--- a/erpnext/public/less/products.less
+++ b/erpnext/public/less/products.less
@@ -22,6 +22,8 @@
}
.filter-options {
+ margin-left: -5px;
+ padding-left: 5px;
max-height: 300px;
overflow: auto;
}
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 844e34b..69e47a4 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -726,9 +726,6 @@
if country != 'India':
return gl_entries
- if not doc.total_taxes_and_charges:
- return gl_entries
-
if doc.reverse_charge == 'Y':
gst_accounts = get_gst_accounts(doc.company)
gst_account_list = gst_accounts.get('cgst_account') + gst_accounts.get('sgst_account') \
@@ -738,6 +735,7 @@
if tax.category not in ("Total", "Valuation and Total"):
continue
+ dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit"
if flt(tax.base_tax_amount_after_discount_amount) and tax.account_head in gst_account_list:
account_currency = get_account_currency(tax.account_head)
@@ -747,8 +745,8 @@
"cost_center": tax.cost_center,
"posting_date": doc.posting_date,
"against": doc.supplier,
- "credit": tax.base_tax_amount_after_discount_amount,
- "credits_in_account_currency": tax.base_tax_amount_after_discount_amount \
+ dr_or_cr: tax.base_tax_amount_after_discount_amount,
+ dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
if account_currency==doc.company_currency \
else tax.tax_amount_after_discount_amount
}, account_currency, item=tax)
diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json
index 7b30af2..581e14c 100644
--- a/erpnext/selling/desk_page/retail/retail.json
+++ b/erpnext/selling/desk_page/retail/retail.json
@@ -3,7 +3,7 @@
{
"hidden": 0,
"label": "Retail Operations",
- "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point-of-Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"POS Profile\"\n ],\n \"description\": \"Point of Sale\",\n \"label\": \"POS\",\n \"name\": \"pos\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"description\": \"Cashier Closing\",\n \"label\": \"Cashier Closing\",\n \"name\": \"Cashier Closing\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Setup mode of POS (Online / Offline)\",\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point of Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"POS Profile\"\n ],\n \"description\": \"Point of Sale\",\n \"label\": \"Point of Sale\",\n \"name\": \"point-of-sale\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"description\": \"Setup mode of POS (Online / Offline)\",\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Cashier Closing\",\n \"label\": \"Cashier Closing\",\n \"name\": \"Cashier Closing\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]"
}
],
"category": "Domains",
@@ -14,10 +14,11 @@
"docstatus": 0,
"doctype": "Desk Page",
"extends_another_page": 0,
+ "hide_custom": 0,
"idx": 0,
"is_standard": 1,
"label": "Retail",
- "modified": "2020-04-26 22:42:39.346750",
+ "modified": "2020-08-20 18:00:07.515691",
"modified_by": "Administrator",
"module": "Selling",
"name": "Retail",
@@ -25,5 +26,27 @@
"pin_to_bottom": 0,
"pin_to_top": 0,
"restrict_to_domain": "Retail",
- "shortcuts": []
+ "shortcuts": [
+ {
+ "color": "#9deca2",
+ "doc_view": "",
+ "format": "{} Active",
+ "label": "Point of Sale Profile",
+ "link_to": "POS Profile",
+ "stats_filter": "{\n \"disabled\": 0\n}",
+ "type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "label": "Point of Sale",
+ "link_to": "point-of-sale",
+ "type": "Page"
+ },
+ {
+ "doc_view": "",
+ "label": "POS Settings",
+ "link_to": "POS Settings",
+ "type": "DocType"
+ }
+ ]
}
\ No newline at end of file
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index aa9fbc0..50f9d84 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -7,6 +7,7 @@
from erpnext.accounts.doctype.cash_flow_mapper.default_cash_flow_mapper import DEFAULT_MAPPERS
from .default_success_action import get_default_success_action
from frappe import _
+from frappe.utils import cint
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from erpnext.setup.default_energy_point_rules import get_default_energy_point_rules
@@ -29,8 +30,8 @@
def check_setup_wizard_not_completed():
- if frappe.db.get_default('desktop:home_page') != 'setup-wizard':
- message = """ERPNext can only be installed on a fresh site where the setup wizard is not completed.
+ if cint(frappe.db.get_single_value('System Settings', 'setup_complete') or 0):
+ message = """ERPNext can only be installed on a fresh site where the setup wizard is not completed.
You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall"""
frappe.throw(message)
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index d97b9e8..52e5e55 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -18,6 +18,28 @@
set_perpetual_inventory(0)
frappe.db.set_value("Buying Settings", None, "allow_multiple_items", 1)
+ def test_reverse_purchase_receipt_sle(self):
+
+ frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 0)
+
+ pr = make_purchase_receipt(qty=0.5)
+
+ sl_entry = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
+ "voucher_no": pr.name}, ['actual_qty'])
+
+ self.assertEqual(len(sl_entry), 1)
+ self.assertEqual(sl_entry[0].actual_qty, 0.5)
+
+ pr.cancel()
+
+ sl_entry_cancelled = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt",
+ "voucher_no": pr.name}, ['actual_qty'], order_by='creation')
+
+ self.assertEqual(len(sl_entry_cancelled), 2)
+ self.assertEqual(sl_entry_cancelled[1].actual_qty, -0.5)
+
+ frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 1)
+
def test_make_purchase_invoice(self):
pr = make_purchase_receipt(do_not_save=True)
self.assertRaises(frappe.ValidationError, make_purchase_invoice, pr.name)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index e1b3730..f4490f1 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -31,7 +31,7 @@
sle['posting_time'] = now_datetime().strftime('%H:%M:%S.%f')
if cancel:
- sle['actual_qty'] = -flt(sle.get('actual_qty'), 0)
+ sle['actual_qty'] = -flt(sle.get('actual_qty'))
if sle['actual_qty'] < 0 and not sle.get('outgoing_rate'):
sle['outgoing_rate'] = get_incoming_outgoing_rate_for_cancel(sle.item_code,