fix: conflicts
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 7faf792..d661bcb 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -58,7 +58,7 @@
(is_supplier_payment and supplier.hold_type in ['All', 'Payments']):
if not supplier.release_date or getdate(nowdate()) <= supplier.release_date:
frappe.msgprint(
- _('{0} is blocked so this transaction cannot proceed'.format(supplier_name)), raise_exception=1)
+ _('{0} is blocked so this transaction cannot proceed').format(supplier_name), raise_exception=1)
def validate(self):
if not self.get('is_return'):
@@ -926,7 +926,7 @@
frappe.throw(
_("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row"))
elif not tax.row_id:
- frappe.throw(_("Please specify a valid Row ID for row {0} in table {1}".format(tax.idx, _(tax.doctype))))
+ frappe.throw(_("Please specify a valid Row ID for row {0} in table {1}").format(tax.idx, _(tax.doctype)))
elif tax.row_id and cint(tax.row_id) >= cint(tax.idx):
frappe.throw(_("Cannot refer row number greater than or equal to current row number for this Charge type"))
@@ -1135,6 +1135,7 @@
child_item.reqd_by_date = p_doctype.delivery_date
child_item.uom = item.stock_uom
child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0
+ child_item.warehouse = p_doctype.set_warehouse or p_doctype.items[0].warehouse
return child_item
@@ -1173,7 +1174,7 @@
if parent.doctype == "Purchase Order" and flt(d.received_qty):
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code))
-
+
if flt(d.billed_amt):
frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code))
@@ -1199,6 +1200,8 @@
child_item = set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, d.get("item_code"))
else:
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
+ if flt(child_item.get("rate")) == flt(d.get("rate")) and flt(child_item.get("qty")) == flt(d.get("qty")):
+ continue
if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty):
frappe.throw(_("Cannot set quantity less than delivered quantity"))
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 75b896b..8d3db8d 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -43,6 +43,7 @@
self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items()
self.validate_warehouse()
+ self.validate_from_warehouse()
self.set_supplier_address()
if self.doctype=="Purchase Invoice":
@@ -115,6 +116,14 @@
if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]:
d.db_set('cost_center', lc_voucher_data[0][1])
+ def validate_from_warehouse(self):
+ for item in self.get('items'):
+ if item.get('from_warehouse') and (item.get('from_warehouse') == item.get('warehouse')):
+ frappe.throw(_("Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same").format(item.idx))
+
+ if item.get('from_warehouse') and self.get('is_subcontracted') == 'Yes':
+ frappe.throw(_("Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor").format(item.idx))
+
def set_supplier_address(self):
address_dict = {
'supplier_address': 'address_display',
@@ -168,7 +177,7 @@
if item.item_code and item.qty and item.item_code in stock_and_asset_items:
item_proportion = flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount \
else flt(item.qty) / stock_and_asset_items_qty
-
+
if i == (last_item_idx - 1):
item.item_tax_amount = flt(valuation_amount_adjustment,
self.precision("item_tax_amount", item))
@@ -500,8 +509,8 @@
item_row = item_row.as_dict()
for fieldname in field_list:
if flt(item_row[fieldname]) < 0:
- frappe.throw(_("Row #{0}: {1} can not be negative for item {2}".format(item_row['idx'],
- frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code'])))
+ frappe.throw(_("Row #{0}: {1} can not be negative for item {2}").format(item_row['idx'],
+ frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code']))
def check_for_on_hold_or_closed_status(self, ref_doctype, ref_fieldname):
for d in self.get("items"):
@@ -521,6 +530,16 @@
pr_qty = flt(d.qty) * flt(d.conversion_factor)
if pr_qty:
+
+ if d.from_warehouse and ((not cint(self.is_return) and self.docstatus==1)
+ or (cint(self.is_return) and self.docstatus==2)):
+ from_warehouse_sle = self.get_sl_entries(d, {
+ "actual_qty": -1 * pr_qty,
+ "warehouse": d.from_warehouse
+ })
+
+ sl_entries.append(from_warehouse_sle)
+
sle = self.get_sl_entries(d, {
"actual_qty": flt(pr_qty),
"serial_no": cstr(d.serial_no).strip()
@@ -541,6 +560,15 @@
})
sl_entries.append(sle)
+ if d.from_warehouse and ((not cint(self.is_return) and self.docstatus==2)
+ or (cint(self.is_return) and self.docstatus==1)):
+ from_warehouse_sle = self.get_sl_entries(d, {
+ "actual_qty": -1 * pr_qty,
+ "warehouse": d.from_warehouse
+ })
+
+ sl_entries.append(from_warehouse_sle)
+
if flt(d.rejected_qty) != 0:
sl_entries.append(self.get_sl_entries(d, {
"warehouse": d.rejected_warehouse,
@@ -699,7 +727,7 @@
if delete_asset and is_auto_create_enabled:
# need to delete movements to delete assets otherwise throws link exists error
movements = frappe.db.sql(
- """SELECT asm.name
+ """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:
@@ -872,9 +900,9 @@
items = ", ".join([d for d in invalid_items])
if len(invalid_items) > 1:
- error_message = _("Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master".format(items, message))
+ error_message = _("Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master").format(items, message)
else:
- error_message = _("Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master".format(items, message))
+ error_message = _("Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master").format(items, message)
frappe.throw(error_message)
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 9a9f3d1..2b21ee8 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -180,7 +180,7 @@
last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
- if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
+ if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom) and not self.get('is_internal_customer'):
throw_message(it.item_name, last_purchase_rate_in_sales_uom, "last purchase rate")
last_valuation_rate = frappe.db.sql("""
@@ -190,7 +190,8 @@
""", (it.item_code, it.warehouse))
if last_valuation_rate:
last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1)
- if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom):
+ if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom) \
+ and not self.get('is_internal_customer'):
throw_message(it.name, last_valuation_rate_in_sales_uom, "valuation rate")
@@ -300,7 +301,7 @@
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_sales_return(d.item_code, self.return_against)
+ return_rate = self.get_incoming_rate_for_return(d.item_code, self.return_against)
# 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 14ee23b..b10d8be 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -72,7 +72,7 @@
if sle_list:
for sle in sle_list:
if warehouse_account.get(sle.warehouse):
- # from warehouse account
+ # from warehouse account/ target warehouse account
self.check_expense_account(item_row)
@@ -96,7 +96,7 @@
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
- # to target warehouse / expense account
+ # expense account
gl_list.append(self.get_gl_dict({
"account": item_row.expense_account,
"against": warehouse_account[sle.warehouse]["account"],
@@ -288,7 +288,7 @@
return serialized_items
- def get_incoming_rate_for_sales_return(self, item_code, against_document):
+ def get_incoming_rate_for_return(self, item_code, against_document):
incoming_rate = 0.0
if against_document and item_code:
incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty)
@@ -306,6 +306,16 @@
warehouses = list(set([d.warehouse for d in
self.get("items") if getattr(d, "warehouse", None)]))
+ target_warehouses = list(set([d.target_warehouse for d in
+ self.get("items") if getattr(d, "target_warehouse", None)]))
+
+ warehouses.extend(target_warehouses)
+
+ from_warehouse = list(set([d.from_warehouse for d in
+ self.get("items") if getattr(d, "from_warehouse", None)]))
+
+ warehouses.extend(from_warehouse)
+
for w in warehouses:
validate_warehouse_company(w, self.company)
@@ -419,7 +429,7 @@
for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
from `tabStock Ledger Entry` sle
where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) {condition}
- order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc""".format(condition=condition),
+ order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
tuple([posting_date, posting_time] + values), as_dict=True):
future_stock_vouchers.append([d.voucher_type, d.voucher_no])
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index b52a07d..95e661a 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -514,7 +514,7 @@
if self.doc.doctype == "Sales Invoice":
self.calculate_paid_amount()
- if self.doc.is_return and self.doc.return_against: return
+ if self.doc.is_return and self.doc.return_against and not self.doc.get('is_pos'): return
self.doc.round_floats_in(self.doc, ["grand_total", "total_advance", "write_off_amount"])
self._set_in_company_currency(self.doc, ['write_off_amount'])
@@ -532,7 +532,7 @@
self.doc.round_floats_in(self.doc, ["paid_amount"])
change_amount = 0
- if self.doc.doctype == "Sales Invoice":
+ if self.doc.doctype == "Sales Invoice" and not self.doc.get('is_return'):
self.calculate_write_off_amount()
self.calculate_change_amount()
change_amount = self.doc.change_amount \
@@ -544,6 +544,9 @@
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) + flt(change_amount),
self.doc.precision("outstanding_amount"))
+ if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'):
+ self.update_paid_amount_for_return(total_amount_to_pay)
+
def calculate_paid_amount(self):
paid_amount = base_paid_amount = 0.0
@@ -614,6 +617,27 @@
def set_item_wise_tax_breakup(self):
self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
+ def update_paid_amount_for_return(self, total_amount_to_pay):
+ default_mode_of_payment = frappe.db.get_value('Sales Invoice Payment',
+ {'parent': self.doc.pos_profile, 'default': 1},
+ ['mode_of_payment', 'type', 'account'], as_dict=1)
+
+ self.doc.payments = []
+
+ if default_mode_of_payment:
+ self.doc.append('payments', {
+ 'mode_of_payment': default_mode_of_payment.mode_of_payment,
+ 'type': default_mode_of_payment.type,
+ 'account': default_mode_of_payment.account,
+ 'amount': total_amount_to_pay
+ })
+ else:
+ self.doc.is_pos = 0
+ self.doc.pos_profile = ''
+
+ self.calculate_paid_amount()
+
+
def get_itemised_tax_breakup_html(doc):
if not doc.taxes:
return