mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-03 16:40:19 +00:00
Merge branch 'develop' into fix-consolidation-precision-error
This commit is contained in:
@@ -1109,17 +1109,17 @@ class AccountsController(TransactionBase):
|
||||
frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
|
||||
)
|
||||
|
||||
if self.doctype == "Purchase Invoice":
|
||||
dr_or_cr = "credit"
|
||||
rev_dr_cr = "debit"
|
||||
supplier_or_customer = self.supplier
|
||||
|
||||
else:
|
||||
dr_or_cr = "debit"
|
||||
rev_dr_cr = "credit"
|
||||
supplier_or_customer = self.customer
|
||||
|
||||
if enable_discount_accounting:
|
||||
if self.doctype == "Purchase Invoice":
|
||||
dr_or_cr = "credit"
|
||||
rev_dr_cr = "debit"
|
||||
supplier_or_customer = self.supplier
|
||||
|
||||
else:
|
||||
dr_or_cr = "debit"
|
||||
rev_dr_cr = "credit"
|
||||
supplier_or_customer = self.customer
|
||||
|
||||
for item in self.get("items"):
|
||||
if item.get("discount_amount") and item.get("discount_account"):
|
||||
discount_amount = item.discount_amount * item.qty
|
||||
@@ -1173,18 +1173,22 @@ class AccountsController(TransactionBase):
|
||||
)
|
||||
)
|
||||
|
||||
if self.get("discount_amount") and self.get("additional_discount_account"):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.additional_discount_account,
|
||||
"against": supplier_or_customer,
|
||||
dr_or_cr: self.discount_amount,
|
||||
"cost_center": self.cost_center,
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
if (
|
||||
(enable_discount_accounting or self.get("is_cash_or_non_trade_discount"))
|
||||
and self.get("additional_discount_account")
|
||||
and self.get("discount_amount")
|
||||
):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.additional_discount_account,
|
||||
"against": supplier_or_customer,
|
||||
dr_or_cr: self.discount_amount,
|
||||
"cost_center": self.cost_center,
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
|
||||
from erpnext.controllers.status_updater import get_allowance_for
|
||||
|
||||
@@ -36,6 +36,10 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class BatchExpiredError(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
|
||||
class StockController(AccountsController):
|
||||
def validate(self):
|
||||
super(StockController, self).validate()
|
||||
@@ -77,6 +81,10 @@ class StockController(AccountsController):
|
||||
def validate_serialized_batch(self):
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
|
||||
is_material_issue = False
|
||||
if self.doctype == "Stock Entry" and self.purpose == "Material Issue":
|
||||
is_material_issue = True
|
||||
|
||||
for d in self.get("items"):
|
||||
if hasattr(d, "serial_no") and hasattr(d, "batch_no") and d.serial_no and d.batch_no:
|
||||
serial_nos = frappe.get_all(
|
||||
@@ -93,6 +101,9 @@ class StockController(AccountsController):
|
||||
)
|
||||
)
|
||||
|
||||
if is_material_issue:
|
||||
continue
|
||||
|
||||
if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
|
||||
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
|
||||
|
||||
@@ -100,7 +111,8 @@ class StockController(AccountsController):
|
||||
frappe.throw(
|
||||
_("Row #{0}: The batch {1} has already expired.").format(
|
||||
d.idx, get_link_to_form("Batch", d.get("batch_no"))
|
||||
)
|
||||
),
|
||||
BatchExpiredError,
|
||||
)
|
||||
|
||||
def clean_serial_nos(self):
|
||||
@@ -310,7 +322,13 @@ class StockController(AccountsController):
|
||||
)
|
||||
if (
|
||||
self.doctype
|
||||
not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry")
|
||||
not in (
|
||||
"Purchase Receipt",
|
||||
"Purchase Invoice",
|
||||
"Stock Reconciliation",
|
||||
"Stock Entry",
|
||||
"Subcontracting Receipt",
|
||||
)
|
||||
and not is_expense_account
|
||||
):
|
||||
frappe.throw(
|
||||
@@ -374,9 +392,24 @@ class StockController(AccountsController):
|
||||
def update_inventory_dimensions(self, row, sl_dict) -> None:
|
||||
dimensions = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self)
|
||||
for dimension in dimensions:
|
||||
if dimension and row.get(dimension.source_fieldname):
|
||||
if not dimension:
|
||||
continue
|
||||
|
||||
if row.get(dimension.source_fieldname):
|
||||
sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
|
||||
|
||||
if not sl_dict.get(dimension.target_fieldname) and dimension.fetch_from_parent:
|
||||
sl_dict[dimension.target_fieldname] = self.get(dimension.fetch_from_parent)
|
||||
|
||||
# Get value based on doctype name
|
||||
if not sl_dict.get(dimension.target_fieldname):
|
||||
fieldname = frappe.get_cached_value(
|
||||
"DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname"
|
||||
)
|
||||
|
||||
if fieldname and self.get(fieldname):
|
||||
sl_dict[dimension.target_fieldname] = self.get(fieldname)
|
||||
|
||||
def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
|
||||
from erpnext.stock.stock_ledger import make_sl_entries
|
||||
|
||||
|
||||
@@ -490,7 +490,7 @@ class SubcontractingController(StockController):
|
||||
row.item_code,
|
||||
row.get(self.subcontract_data.order_field),
|
||||
) and transfer_item.qty > 0:
|
||||
qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0
|
||||
qty = flt(self.__get_qty_based_on_material_transfer(row, transfer_item))
|
||||
transfer_item.qty -= qty
|
||||
self.__add_supplied_item(row, transfer_item.get("item_details"), qty)
|
||||
|
||||
@@ -720,6 +720,25 @@ class SubcontractingController(StockController):
|
||||
sco_doc = frappe.get_doc("Subcontracting Order", sco)
|
||||
sco_doc.update_status()
|
||||
|
||||
def set_missing_values_in_additional_costs(self):
|
||||
self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
|
||||
|
||||
if self.total_additional_costs:
|
||||
if self.distribute_additional_costs_based_on == "Amount":
|
||||
total_amt = sum(flt(item.amount) for item in self.get("items"))
|
||||
for item in self.items:
|
||||
item.additional_cost_per_qty = (
|
||||
(item.amount * self.total_additional_costs) / total_amt
|
||||
) / item.qty
|
||||
else:
|
||||
total_qty = sum(flt(item.qty) for item in self.get("items"))
|
||||
additional_cost_per_qty = self.total_additional_costs / total_qty
|
||||
for item in self.items:
|
||||
item.additional_cost_per_qty = additional_cost_per_qty
|
||||
else:
|
||||
for item in self.items:
|
||||
item.additional_cost_per_qty = 0
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_current_stock(self):
|
||||
if self.doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
|
||||
@@ -730,7 +749,7 @@ class SubcontractingController(StockController):
|
||||
{"item_code": item.rm_item_code, "warehouse": self.supplier_warehouse},
|
||||
"actual_qty",
|
||||
)
|
||||
item.current_stock = flt(actual_qty) or 0
|
||||
item.current_stock = flt(actual_qty)
|
||||
|
||||
@property
|
||||
def sub_contracted_items(self):
|
||||
|
||||
@@ -37,6 +37,11 @@ class calculate_taxes_and_totals(object):
|
||||
self.set_discount_amount()
|
||||
self.apply_discount_amount()
|
||||
|
||||
# Update grand total as per cash and non trade discount
|
||||
if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
|
||||
self.doc.grand_total -= self.doc.discount_amount
|
||||
self.doc.base_grand_total -= self.doc.base_discount_amount
|
||||
|
||||
self.calculate_shipping_charges()
|
||||
|
||||
if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
@@ -500,9 +505,6 @@ class calculate_taxes_and_totals(object):
|
||||
else:
|
||||
self.doc.grand_total = flt(self.doc.net_total)
|
||||
|
||||
if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
|
||||
self.doc.grand_total -= self.doc.discount_amount
|
||||
|
||||
if self.doc.get("taxes"):
|
||||
self.doc.total_taxes_and_charges = flt(
|
||||
self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
|
||||
@@ -597,16 +599,16 @@ class calculate_taxes_and_totals(object):
|
||||
if not self.doc.apply_discount_on:
|
||||
frappe.throw(_("Please select Apply Discount On"))
|
||||
|
||||
self.doc.base_discount_amount = flt(
|
||||
self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
|
||||
)
|
||||
|
||||
if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
|
||||
"is_cash_or_non_trade_discount"
|
||||
):
|
||||
self.discount_amount_applied = True
|
||||
return
|
||||
|
||||
self.doc.base_discount_amount = flt(
|
||||
self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
|
||||
)
|
||||
|
||||
total_for_discount_amount = self.get_total_for_discount_amount()
|
||||
taxes = self.doc.get("taxes")
|
||||
net_total = 0
|
||||
|
||||
@@ -36,6 +36,36 @@ class TestSubcontractingController(FrappeTestCase):
|
||||
sco.remove_empty_rows()
|
||||
self.assertEqual((len_before - 1), len(sco.service_items))
|
||||
|
||||
def test_set_missing_values_in_additional_costs(self):
|
||||
sco = get_subcontracting_order(do_not_submit=1)
|
||||
|
||||
rate_without_additional_cost = sco.items[0].rate
|
||||
amount_without_additional_cost = sco.items[0].amount
|
||||
|
||||
additional_amount = 120
|
||||
sco.append(
|
||||
"additional_costs",
|
||||
{
|
||||
"expense_account": "Cost of Goods Sold - _TC",
|
||||
"description": "Test",
|
||||
"amount": additional_amount,
|
||||
},
|
||||
)
|
||||
sco.save()
|
||||
|
||||
additional_cost_per_qty = additional_amount / sco.items[0].qty
|
||||
|
||||
self.assertEqual(sco.items[0].additional_cost_per_qty, additional_cost_per_qty)
|
||||
self.assertEqual(rate_without_additional_cost + additional_cost_per_qty, sco.items[0].rate)
|
||||
self.assertEqual(amount_without_additional_cost + additional_amount, sco.items[0].amount)
|
||||
|
||||
sco.additional_costs = []
|
||||
sco.save()
|
||||
|
||||
self.assertEqual(sco.items[0].additional_cost_per_qty, 0)
|
||||
self.assertEqual(rate_without_additional_cost, sco.items[0].rate)
|
||||
self.assertEqual(amount_without_additional_cost, sco.items[0].amount)
|
||||
|
||||
def test_create_raw_materials_supplied(self):
|
||||
sco = get_subcontracting_order()
|
||||
sco.supplied_items = None
|
||||
|
||||
Reference in New Issue
Block a user