Merge branch 'develop' into leave-opening-balance
This commit is contained in:
commit
211916bfce
@ -232,7 +232,7 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
|
||||
}), transaction.currency, company_account)
|
||||
|
||||
if total_amount > transaction.unallocated_amount:
|
||||
frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
|
||||
frappe.throw(_("The sum total of amounts of all selected vouchers should be less than the unallocated amount of the bank transaction"))
|
||||
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
|
||||
|
||||
for voucher in vouchers:
|
||||
|
@ -110,8 +110,13 @@ def get_paid_amount(payment_entry, currency, bank_account):
|
||||
paid_amount_field = "paid_amount"
|
||||
if payment_entry.payment_document == 'Payment Entry':
|
||||
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
|
||||
paid_amount_field = ("base_paid_amount"
|
||||
if doc.paid_to_account_currency == currency else "paid_amount")
|
||||
|
||||
if doc.payment_type == 'Receive':
|
||||
paid_amount_field = ("received_amount"
|
||||
if doc.paid_to_account_currency == currency else "base_received_amount")
|
||||
elif doc.payment_type == 'Pay':
|
||||
paid_amount_field = ("paid_amount"
|
||||
if doc.paid_to_account_currency == currency else "base_paid_amount")
|
||||
|
||||
return frappe.db.get_value(payment_entry.payment_document,
|
||||
payment_entry.payment_entry, paid_amount_field)
|
||||
|
@ -42,7 +42,11 @@ from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timeshe
|
||||
from erpnext.setup.doctype.company.company import update_company_current_month_sales
|
||||
from erpnext.stock.doctype.batch.batch import set_batch_nos
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no, get_serial_nos
|
||||
from erpnext.stock.doctype.serial_no.serial_no import (
|
||||
get_delivery_note_serial_no,
|
||||
get_serial_nos,
|
||||
update_serial_nos_after_submit,
|
||||
)
|
||||
from erpnext.stock.utils import calculate_mapped_packed_items_return
|
||||
|
||||
form_grid_templates = {
|
||||
@ -226,6 +230,9 @@ class SalesInvoice(SellingController):
|
||||
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||
if self.update_stock == 1:
|
||||
self.update_stock_ledger()
|
||||
if self.is_return and self.update_stock:
|
||||
update_serial_nos_after_submit(self, "items")
|
||||
|
||||
|
||||
# this sequence because outstanding may get -ve
|
||||
self.make_gl_entries()
|
||||
@ -1255,14 +1262,14 @@ class SalesInvoice(SellingController):
|
||||
def update_billing_status_in_dn(self, update_modified=True):
|
||||
updated_delivery_notes = []
|
||||
for d in self.get("items"):
|
||||
if d.so_detail:
|
||||
updated_delivery_notes += update_billed_amount_based_on_so(d.so_detail, update_modified)
|
||||
elif d.dn_detail:
|
||||
if d.dn_detail:
|
||||
billed_amt = frappe.db.sql("""select sum(amount) from `tabSales Invoice Item`
|
||||
where dn_detail=%s and docstatus=1""", d.dn_detail)
|
||||
billed_amt = billed_amt and billed_amt[0][0] or 0
|
||||
frappe.db.set_value("Delivery Note Item", d.dn_detail, "billed_amt", billed_amt, update_modified=update_modified)
|
||||
updated_delivery_notes.append(d.delivery_note)
|
||||
elif d.so_detail:
|
||||
updated_delivery_notes += update_billed_amount_based_on_so(d.so_detail, update_modified)
|
||||
|
||||
for dn in set(updated_delivery_notes):
|
||||
frappe.get_doc("Delivery Note", dn).update_billing_percentage(update_modified=update_modified)
|
||||
|
@ -2615,6 +2615,12 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
frappe.db.set_value('Accounts Settings', None, 'acc_frozen_upto', None)
|
||||
|
||||
def test_standalone_serial_no_return(self):
|
||||
si = create_sales_invoice(item_code="_Test Serialized Item With Series", update_stock=True, is_return=True, qty=-1)
|
||||
si.reload()
|
||||
self.assertTrue(si.items[0].serial_no)
|
||||
|
||||
|
||||
def get_sales_invoice_for_e_invoice():
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
si.naming_series = 'INV-2020-.#####'
|
||||
|
@ -71,8 +71,7 @@ class ShippingRule(Document):
|
||||
if doc.currency != doc.company_currency:
|
||||
shipping_amount = flt(shipping_amount / doc.conversion_rate, 2)
|
||||
|
||||
if shipping_amount:
|
||||
self.add_shipping_rule_to_tax_table(doc, shipping_amount)
|
||||
self.add_shipping_rule_to_tax_table(doc, shipping_amount)
|
||||
|
||||
def get_shipping_amount_from_rules(self, value):
|
||||
for condition in self.get("conditions"):
|
||||
|
@ -17,8 +17,8 @@ class TestRequestedItemsToOrderAndReceive(FrappeTestCase):
|
||||
def setUp(self) -> None:
|
||||
create_item("Test MR Report Item")
|
||||
self.setup_material_request() # to order and receive
|
||||
self.setup_material_request(order=True) # to receive (ordered)
|
||||
self.setup_material_request(order=True, receive=True) # complete (ordered & received)
|
||||
self.setup_material_request(order=True, days=1) # to receive (ordered)
|
||||
self.setup_material_request(order=True, receive=True, days=2) # complete (ordered & received)
|
||||
|
||||
self.filters = frappe._dict(
|
||||
company="_Test Company", from_date=today(), to_date=add_days(today(), 30),
|
||||
@ -32,9 +32,8 @@ class TestRequestedItemsToOrderAndReceive(FrappeTestCase):
|
||||
data = get_data(self.filters)
|
||||
self.assertEqual(len(data), 2) # MRs today should be fetched
|
||||
|
||||
self.filters.from_date = add_days(today(), 1)
|
||||
data = get_data(self.filters)
|
||||
self.assertEqual(len(data), 0) # MRs today should not be fetched as from date is tomorrow
|
||||
data = get_data(self.filters.update({"from_date": add_days(today(), 10)}))
|
||||
self.assertEqual(len(data), 0) # MRs today should not be fetched as from date is in future
|
||||
|
||||
def test_ordered_received_material_requests(self):
|
||||
data = get_data(self.filters)
|
||||
@ -44,19 +43,19 @@ class TestRequestedItemsToOrderAndReceive(FrappeTestCase):
|
||||
self.assertEqual(data[0].ordered_qty, 0.0)
|
||||
self.assertEqual(data[1].ordered_qty, 57.0)
|
||||
|
||||
def setup_material_request(self, order=False, receive=False):
|
||||
def setup_material_request(self, order=False, receive=False, days=0):
|
||||
po = None
|
||||
test_records = frappe.get_test_records('Material Request')
|
||||
|
||||
mr = frappe.copy_doc(test_records[0])
|
||||
mr.transaction_date = today()
|
||||
mr.schedule_date = add_days(today(), 1)
|
||||
mr.transaction_date = add_days(today(), days)
|
||||
mr.schedule_date = add_days(mr.transaction_date, 1)
|
||||
for row in mr.items:
|
||||
row.item_code = "Test MR Report Item"
|
||||
row.item_name = "Test MR Report Item"
|
||||
row.description = "Test MR Report Item"
|
||||
row.uom = "Nos"
|
||||
row.schedule_date = add_days(today(), 1)
|
||||
row.schedule_date = mr.schedule_date
|
||||
mr.submit()
|
||||
|
||||
if order or receive:
|
||||
|
@ -208,10 +208,15 @@ def get_already_returned_items(doc):
|
||||
|
||||
return items
|
||||
|
||||
def get_returned_qty_map_for_row(row_name, doctype):
|
||||
def get_returned_qty_map_for_row(return_against, party, row_name, doctype):
|
||||
child_doctype = doctype + " Item"
|
||||
reference_field = "dn_detail" if doctype == "Delivery Note" else frappe.scrub(child_doctype)
|
||||
|
||||
if doctype in ('Purchase Receipt', 'Purchase Invoice'):
|
||||
party_type = 'supplier'
|
||||
else:
|
||||
party_type = 'customer'
|
||||
|
||||
fields = [
|
||||
"sum(abs(`tab{0}`.qty)) as qty".format(child_doctype),
|
||||
"sum(abs(`tab{0}`.stock_qty)) as stock_qty".format(child_doctype)
|
||||
@ -226,9 +231,12 @@ def get_returned_qty_map_for_row(row_name, doctype):
|
||||
if doctype == "Purchase Receipt":
|
||||
fields += ["sum(abs(`tab{0}`.received_stock_qty)) as received_stock_qty".format(child_doctype)]
|
||||
|
||||
# Used retrun against and supplier and is_retrun because there is an index added for it
|
||||
data = frappe.db.get_list(doctype,
|
||||
fields = fields,
|
||||
filters = [
|
||||
[doctype, "return_against", "=", return_against],
|
||||
[doctype, party_type, "=", party],
|
||||
[doctype, "docstatus", "=", 1],
|
||||
[doctype, "is_return", "=", 1],
|
||||
[child_doctype, reference_field, "=", row_name]
|
||||
@ -307,7 +315,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
target_doc.serial_no = '\n'.join(serial_nos)
|
||||
|
||||
if doctype == "Purchase Receipt":
|
||||
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
|
||||
returned_qty_map = get_returned_qty_map_for_row(source_parent.name, source_parent.supplier, source_doc.name, doctype)
|
||||
target_doc.received_qty = -1 * flt(source_doc.received_qty - (returned_qty_map.get('received_qty') or 0))
|
||||
target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get('rejected_qty') or 0))
|
||||
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
|
||||
@ -321,7 +329,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
target_doc.purchase_receipt_item = source_doc.name
|
||||
|
||||
elif doctype == "Purchase Invoice":
|
||||
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
|
||||
returned_qty_map = get_returned_qty_map_for_row(source_parent.name, source_parent.supplier, source_doc.name, doctype)
|
||||
target_doc.received_qty = -1 * flt(source_doc.received_qty - (returned_qty_map.get('received_qty') or 0))
|
||||
target_doc.rejected_qty = -1 * flt(source_doc.rejected_qty - (returned_qty_map.get('rejected_qty') or 0))
|
||||
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
|
||||
@ -335,7 +343,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
target_doc.purchase_invoice_item = source_doc.name
|
||||
|
||||
elif doctype == "Delivery Note":
|
||||
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
|
||||
returned_qty_map = get_returned_qty_map_for_row(source_parent.name, source_parent.customer, source_doc.name, doctype)
|
||||
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
|
||||
target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
|
||||
|
||||
@ -348,7 +356,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
if default_warehouse_for_sales_return:
|
||||
target_doc.warehouse = default_warehouse_for_sales_return
|
||||
elif doctype == "Sales Invoice" or doctype == "POS Invoice":
|
||||
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
|
||||
returned_qty_map = get_returned_qty_map_for_row(source_parent.name, source_parent.customer, source_doc.name, doctype)
|
||||
target_doc.qty = -1 * flt(source_doc.qty - (returned_qty_map.get('qty') or 0))
|
||||
target_doc.stock_qty = -1 * flt(source_doc.stock_qty - (returned_qty_map.get('stock_qty') or 0))
|
||||
|
||||
|
@ -37,6 +37,8 @@ class calculate_taxes_and_totals(object):
|
||||
self.set_discount_amount()
|
||||
self.apply_discount_amount()
|
||||
|
||||
self.calculate_shipping_charges()
|
||||
|
||||
if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
|
||||
self.calculate_total_advance()
|
||||
|
||||
@ -50,7 +52,6 @@ class calculate_taxes_and_totals(object):
|
||||
self.initialize_taxes()
|
||||
self.determine_exclusive_rate()
|
||||
self.calculate_net_total()
|
||||
self.calculate_shipping_charges()
|
||||
self.calculate_taxes()
|
||||
self.manipulate_grand_total_for_inclusive_tax()
|
||||
self.calculate_totals()
|
||||
@ -276,6 +277,8 @@ class calculate_taxes_and_totals(object):
|
||||
shipping_rule = frappe.get_doc("Shipping Rule", self.doc.shipping_rule)
|
||||
shipping_rule.apply(self.doc)
|
||||
|
||||
self._calculate()
|
||||
|
||||
def calculate_taxes(self):
|
||||
rounding_adjustment_computed = self.doc.get('is_consolidated') and self.doc.get('rounding_adjustment')
|
||||
if not rounding_adjustment_computed:
|
||||
|
@ -214,6 +214,7 @@ class Lead(SellingController):
|
||||
})
|
||||
|
||||
contact.insert(ignore_permissions=True)
|
||||
contact.reload() # load changes by hooks on contact
|
||||
|
||||
return contact
|
||||
|
||||
|
@ -32,6 +32,8 @@
|
||||
"monthly_repayment_amount",
|
||||
"repayment_start_date",
|
||||
"is_term_loan",
|
||||
"accounting_dimensions_section",
|
||||
"cost_center",
|
||||
"account_info",
|
||||
"mode_of_payment",
|
||||
"disbursement_account",
|
||||
@ -366,12 +368,23 @@
|
||||
"options": "Account",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "accounting_dimensions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Accounting Dimensions"
|
||||
},
|
||||
{
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-01-25 16:29:16.325501",
|
||||
"modified": "2022-03-10 11:50:31.957360",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Loan Management",
|
||||
"name": "Loan",
|
||||
|
@ -25,6 +25,7 @@ class Loan(AccountsController):
|
||||
self.set_loan_amount()
|
||||
self.validate_loan_amount()
|
||||
self.set_missing_fields()
|
||||
self.validate_cost_center()
|
||||
self.validate_accounts()
|
||||
self.check_sanctioned_amount_limit()
|
||||
self.validate_repay_from_salary()
|
||||
@ -45,6 +46,13 @@ class Loan(AccountsController):
|
||||
frappe.throw(_("Account {0} does not belongs to company {1}").format(frappe.bold(self.get(fieldname)),
|
||||
frappe.bold(self.company)))
|
||||
|
||||
def validate_cost_center(self):
|
||||
if not self.cost_center and self.rate_of_interest != 0:
|
||||
self.cost_center = frappe.db.get_value('Company', self.company, 'cost_center')
|
||||
|
||||
if not self.cost_center:
|
||||
frappe.throw(_('Cost center is mandatory for loans having rate of interest greater than 0'))
|
||||
|
||||
def on_submit(self):
|
||||
self.link_loan_security_pledge()
|
||||
|
||||
|
@ -6,7 +6,6 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import add_days, cint, date_diff, flt, get_datetime, getdate, nowdate
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
from erpnext.controllers.accounts_controller import AccountsController
|
||||
|
||||
@ -41,6 +40,8 @@ class LoanInterestAccrual(AccountsController):
|
||||
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||
gle_map = []
|
||||
|
||||
cost_center = frappe.db.get_value('Loan', self.loan, 'cost_center')
|
||||
|
||||
if self.interest_amount:
|
||||
gle_map.append(
|
||||
self.get_gl_dict({
|
||||
@ -54,7 +55,7 @@ class LoanInterestAccrual(AccountsController):
|
||||
"against_voucher": self.loan,
|
||||
"remarks": _("Interest accrued from {0} to {1} against loan: {2}").format(
|
||||
self.last_accrual_date, self.posting_date, self.loan),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"cost_center": cost_center,
|
||||
"posting_date": self.posting_date
|
||||
})
|
||||
)
|
||||
@ -69,7 +70,7 @@ class LoanInterestAccrual(AccountsController):
|
||||
"against_voucher": self.loan,
|
||||
"remarks": ("Interest accrued from {0} to {1} against loan: {2}").format(
|
||||
self.last_accrual_date, self.posting_date, self.loan),
|
||||
"cost_center": erpnext.get_default_cost_center(self.company),
|
||||
"cost_center": cost_center,
|
||||
"posting_date": self.posting_date
|
||||
})
|
||||
)
|
||||
|
@ -2,6 +2,7 @@
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
import functools
|
||||
import re
|
||||
from collections import deque
|
||||
from operator import itemgetter
|
||||
from typing import List
|
||||
@ -103,25 +104,33 @@ class BOM(WebsiteGenerator):
|
||||
)
|
||||
|
||||
def autoname(self):
|
||||
names = frappe.db.sql_list("""select name from `tabBOM` where item=%s""", self.item)
|
||||
# ignore amended documents while calculating current index
|
||||
existing_boms = frappe.get_all(
|
||||
"BOM",
|
||||
filters={"item": self.item, "amended_from": ["is", "not set"]},
|
||||
pluck="name"
|
||||
)
|
||||
|
||||
if names:
|
||||
# name can be BOM/ITEM/001, BOM/ITEM/001-1, BOM-ITEM-001, BOM-ITEM-001-1
|
||||
|
||||
# split by item
|
||||
names = [name.split(self.item, 1) for name in names]
|
||||
names = [d[-1][1:] for d in filter(lambda x: len(x) > 1 and x[-1], names)]
|
||||
|
||||
# split by (-) if cancelled
|
||||
if names:
|
||||
names = [cint(name.split('-')[-1]) for name in names]
|
||||
idx = max(names) + 1
|
||||
else:
|
||||
idx = 1
|
||||
if existing_boms:
|
||||
index = self.get_next_version_index(existing_boms)
|
||||
else:
|
||||
idx = 1
|
||||
index = 1
|
||||
|
||||
prefix = self.doctype
|
||||
suffix = "%.3i" % index # convert index to string (1 -> "001")
|
||||
bom_name = f"{prefix}-{self.item}-{suffix}"
|
||||
|
||||
if len(bom_name) <= 140:
|
||||
name = bom_name
|
||||
else:
|
||||
# since max characters for name is 140, remove enough characters from the
|
||||
# item name to fit the prefix, suffix and the separators
|
||||
truncated_length = 140 - (len(prefix) + len(suffix) + 2)
|
||||
truncated_item_name = self.item[:truncated_length]
|
||||
# if a partial word is found after truncate, remove the extra characters
|
||||
truncated_item_name = truncated_item_name.rsplit(" ", 1)[0]
|
||||
name = f"{prefix}-{truncated_item_name}-{suffix}"
|
||||
|
||||
name = 'BOM-' + self.item + ('-%.3i' % idx)
|
||||
if frappe.db.exists("BOM", name):
|
||||
conflicting_bom = frappe.get_doc("BOM", name)
|
||||
|
||||
@ -134,6 +143,26 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
self.name = name
|
||||
|
||||
@staticmethod
|
||||
def get_next_version_index(existing_boms: List[str]) -> int:
|
||||
# split by "/" and "-"
|
||||
delimiters = ["/", "-"]
|
||||
pattern = "|".join(map(re.escape, delimiters))
|
||||
bom_parts = [re.split(pattern, bom_name) for bom_name in existing_boms]
|
||||
|
||||
# filter out BOMs that do not follow the following formats: BOM/ITEM/001, BOM-ITEM-001
|
||||
valid_bom_parts = list(filter(lambda x: len(x) > 1 and x[-1], bom_parts))
|
||||
|
||||
# extract the current index from the BOM parts
|
||||
if valid_bom_parts:
|
||||
# handle cancelled and submitted documents
|
||||
indexes = [cint(part[-1]) for part in valid_bom_parts]
|
||||
index = max(indexes) + 1
|
||||
else:
|
||||
index = 1
|
||||
|
||||
return index
|
||||
|
||||
def validate(self):
|
||||
self.route = frappe.scrub(self.name).replace('_', '-')
|
||||
|
||||
@ -192,12 +221,13 @@ class BOM(WebsiteGenerator):
|
||||
if self.routing:
|
||||
self.set("operations", [])
|
||||
fields = ["sequence_id", "operation", "workstation", "description",
|
||||
"time_in_mins", "batch_size", "operating_cost", "idx", "hour_rate"]
|
||||
"time_in_mins", "batch_size", "operating_cost", "idx", "hour_rate",
|
||||
"set_cost_based_on_bom_qty", "fixed_time"]
|
||||
|
||||
for row in frappe.get_all("BOM Operation", fields = fields,
|
||||
filters = {'parenttype': 'Routing', 'parent': self.routing}, order_by="sequence_id, idx"):
|
||||
child = self.append('operations', row)
|
||||
child.hour_rate = flt(row.hour_rate / self.conversion_rate, 2)
|
||||
child.hour_rate = flt(row.hour_rate / self.conversion_rate, child.precision("hour_rate"))
|
||||
|
||||
def set_bom_material_details(self):
|
||||
for item in self.get("items"):
|
||||
|
@ -432,6 +432,69 @@ class TestBOM(FrappeTestCase):
|
||||
self.assertEqual(bom.transfer_material_against, "Work Order")
|
||||
bom.delete()
|
||||
|
||||
def test_bom_name_length(self):
|
||||
""" test >140 char names"""
|
||||
bom_tree = {
|
||||
"x" * 140 : {
|
||||
" ".join(["abc"] * 35): {}
|
||||
}
|
||||
}
|
||||
create_nested_bom(bom_tree, prefix="")
|
||||
|
||||
def test_version_index(self):
|
||||
|
||||
bom = frappe.new_doc("BOM")
|
||||
|
||||
version_index_test_cases = [
|
||||
(1, []),
|
||||
(1, ["BOM#XYZ"]),
|
||||
(2, ["BOM/ITEM/001"]),
|
||||
(2, ["BOM-ITEM-001"]),
|
||||
(3, ["BOM-ITEM-001", "BOM-ITEM-002"]),
|
||||
(4, ["BOM-ITEM-001", "BOM-ITEM-002", "BOM-ITEM-003"]),
|
||||
]
|
||||
|
||||
for expected_index, existing_boms in version_index_test_cases:
|
||||
with self.subTest():
|
||||
self.assertEqual(expected_index, bom.get_next_version_index(existing_boms),
|
||||
msg=f"Incorrect index for {existing_boms}")
|
||||
|
||||
def test_bom_versioning(self):
|
||||
bom_tree = {
|
||||
frappe.generate_hash(length=10) : {
|
||||
frappe.generate_hash(length=10): {}
|
||||
}
|
||||
}
|
||||
bom = create_nested_bom(bom_tree, prefix="")
|
||||
self.assertEqual(int(bom.name.split("-")[-1]), 1)
|
||||
original_bom_name = bom.name
|
||||
|
||||
bom.cancel()
|
||||
bom.reload()
|
||||
self.assertEqual(bom.name, original_bom_name)
|
||||
|
||||
# create a new amendment
|
||||
amendment = frappe.copy_doc(bom)
|
||||
amendment.docstatus = 0
|
||||
amendment.amended_from = bom.name
|
||||
|
||||
amendment.save()
|
||||
amendment.submit()
|
||||
amendment.reload()
|
||||
|
||||
self.assertNotEqual(amendment.name, bom.name)
|
||||
# `origname-001-1` version
|
||||
self.assertEqual(int(amendment.name.split("-")[-1]), 1)
|
||||
self.assertEqual(int(amendment.name.split("-")[-2]), 1)
|
||||
|
||||
# create a new version
|
||||
version = frappe.copy_doc(amendment)
|
||||
version.docstatus = 0
|
||||
version.amended_from = None
|
||||
version.save()
|
||||
self.assertNotEqual(amendment.name, version.name)
|
||||
self.assertEqual(int(version.name.split("-")[-1]), 2)
|
||||
|
||||
|
||||
def get_default_bom(item_code="_Test FG Item 2"):
|
||||
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
|
||||
|
@ -66,7 +66,8 @@
|
||||
"label": "Hour Rate",
|
||||
"oldfieldname": "hour_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "currency"
|
||||
"options": "currency",
|
||||
"precision": "2"
|
||||
},
|
||||
{
|
||||
"description": "In minutes",
|
||||
@ -186,7 +187,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-12-15 03:00:00.473173",
|
||||
"modified": "2022-03-10 06:19:08.462027",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Operation",
|
||||
|
@ -357,4 +357,5 @@ erpnext.patches.v13_0.set_work_order_qty_in_so_from_mr
|
||||
erpnext.patches.v13_0.update_accounts_in_loan_docs
|
||||
erpnext.patches.v14_0.update_batch_valuation_flag
|
||||
erpnext.patches.v14_0.delete_non_profit_doctypes
|
||||
erpnext.patches.v14_0.update_employee_advance_status
|
||||
erpnext.patches.v14_0.update_employee_advance_status
|
||||
erpnext.patches.v13_0.add_cost_center_in_loans
|
16
erpnext/patches/v13_0/add_cost_center_in_loans.py
Normal file
16
erpnext/patches/v13_0/add_cost_center_in_loans.py
Normal file
@ -0,0 +1,16 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc('loan_management', 'doctype', 'loan')
|
||||
loan = frappe.qb.DocType('Loan')
|
||||
|
||||
for company in frappe.get_all('Company', pluck='name'):
|
||||
default_cost_center = frappe.db.get_value('Company', company, 'cost_center')
|
||||
frappe.qb.update(
|
||||
loan
|
||||
).set(
|
||||
loan.cost_center, default_cost_center
|
||||
).where(
|
||||
loan.company == company
|
||||
).run()
|
@ -39,6 +39,8 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
|
||||
this._calculate_taxes_and_totals();
|
||||
this.calculate_discount_amount();
|
||||
|
||||
this.calculate_shipping_charges();
|
||||
|
||||
// Advance calculation applicable to Sales /Purchase Invoice
|
||||
if(in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype)
|
||||
&& this.frm.doc.docstatus < 2 && !this.frm.doc.is_return) {
|
||||
@ -81,7 +83,6 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
|
||||
this.initialize_taxes();
|
||||
this.determine_exclusive_rate();
|
||||
this.calculate_net_total();
|
||||
this.calculate_shipping_charges();
|
||||
this.calculate_taxes();
|
||||
this.manipulate_grand_total_for_inclusive_tax();
|
||||
this.calculate_totals();
|
||||
@ -275,6 +276,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
|
||||
frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]);
|
||||
if (frappe.meta.get_docfield(this.frm.doc.doctype, "shipping_rule", this.frm.doc.name)) {
|
||||
this.shipping_rule();
|
||||
this._calculate_taxes_and_totals();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,8 @@ class GSTR3BReport(Document):
|
||||
|
||||
def get_inward_nil_exempt(self, state):
|
||||
inward_nil_exempt = frappe.db.sql("""
|
||||
SELECT p.place_of_supply, sum(i.base_amount) as base_amount, i.is_nil_exempt, i.is_non_gst
|
||||
SELECT p.place_of_supply, p.supplier_address,
|
||||
i.base_amount, i.is_nil_exempt, i.is_non_gst
|
||||
FROM `tabPurchase Invoice` p , `tabPurchase Invoice Item` i
|
||||
WHERE p.docstatus = 1 and p.name = i.parent
|
||||
and p.is_opening = 'No'
|
||||
@ -135,7 +136,7 @@ class GSTR3BReport(Document):
|
||||
and (i.is_nil_exempt = 1 or i.is_non_gst = 1 or p.gst_category = 'Registered Composition') and
|
||||
month(p.posting_date) = %s and year(p.posting_date) = %s
|
||||
and p.company = %s and p.company_gstin = %s
|
||||
GROUP BY p.place_of_supply, i.is_nil_exempt, i.is_non_gst""",
|
||||
""",
|
||||
(self.month_no, self.year, self.company, self.gst_details.get("gstin")), as_dict=1)
|
||||
|
||||
inward_nil_exempt_details = {
|
||||
@ -149,18 +150,24 @@ class GSTR3BReport(Document):
|
||||
}
|
||||
}
|
||||
|
||||
address_state_map = get_address_state_map()
|
||||
|
||||
for d in inward_nil_exempt:
|
||||
if d.place_of_supply:
|
||||
if (d.is_nil_exempt == 1 or d.get('gst_category') == 'Registered Composition') \
|
||||
and state == d.place_of_supply.split("-")[1]:
|
||||
inward_nil_exempt_details["gst"]["intra"] += d.base_amount
|
||||
elif (d.is_nil_exempt == 1 or d.get('gst_category') == 'Registered Composition') \
|
||||
and state != d.place_of_supply.split("-")[1]:
|
||||
inward_nil_exempt_details["gst"]["inter"] += d.base_amount
|
||||
elif d.is_non_gst == 1 and state == d.place_of_supply.split("-")[1]:
|
||||
inward_nil_exempt_details["non_gst"]["intra"] += d.base_amount
|
||||
elif d.is_non_gst == 1 and state != d.place_of_supply.split("-")[1]:
|
||||
inward_nil_exempt_details["non_gst"]["inter"] += d.base_amount
|
||||
if not d.place_of_supply:
|
||||
d.place_of_supply = "00-" + cstr(state)
|
||||
|
||||
supplier_state = address_state_map.get(d.supplier_address) or state
|
||||
|
||||
if (d.is_nil_exempt == 1 or d.get('gst_category') == 'Registered Composition') \
|
||||
and cstr(supplier_state) == cstr(d.place_of_supply.split("-")[1]):
|
||||
inward_nil_exempt_details["gst"]["intra"] += d.base_amount
|
||||
elif (d.is_nil_exempt == 1 or d.get('gst_category') == 'Registered Composition') \
|
||||
and cstr(supplier_state) != cstr(d.place_of_supply.split("-")[1]):
|
||||
inward_nil_exempt_details["gst"]["inter"] += d.base_amount
|
||||
elif d.is_non_gst == 1 and cstr(supplier_state) == cstr(d.place_of_supply.split("-")[1]):
|
||||
inward_nil_exempt_details["non_gst"]["intra"] += d.base_amount
|
||||
elif d.is_non_gst == 1 and cstr(supplier_state) != cstr(d.place_of_supply.split("-")[1]):
|
||||
inward_nil_exempt_details["non_gst"]["inter"] += d.base_amount
|
||||
|
||||
return inward_nil_exempt_details
|
||||
|
||||
@ -419,6 +426,11 @@ class GSTR3BReport(Document):
|
||||
|
||||
return ",".join(missing_field_invoices)
|
||||
|
||||
def get_address_state_map():
|
||||
return frappe._dict(
|
||||
frappe.get_all('Address', fields=['name', 'gst_state'], as_list=1)
|
||||
)
|
||||
|
||||
def get_json(template):
|
||||
file_path = os.path.join(os.path.dirname(__file__), '{template}.json'.format(template=template))
|
||||
with open(file_path, 'r') as f:
|
||||
|
@ -1477,6 +1477,28 @@ class TestSalesOrder(FrappeTestCase):
|
||||
self.assertEqual(so.items[0].work_order_qty, wo.produced_qty)
|
||||
self.assertEqual(mr.status, "Manufactured")
|
||||
|
||||
def test_sales_order_with_shipping_rule(self):
|
||||
from erpnext.accounts.doctype.shipping_rule.test_shipping_rule import create_shipping_rule
|
||||
shipping_rule = create_shipping_rule(shipping_rule_type = "Selling", shipping_rule_name = "Shipping Rule - Sales Invoice Test")
|
||||
sales_order = make_sales_order(do_not_save=True)
|
||||
sales_order.shipping_rule = shipping_rule.name
|
||||
|
||||
sales_order.items[0].qty = 1
|
||||
sales_order.save()
|
||||
self.assertEqual(sales_order.taxes[0].tax_amount, 50)
|
||||
|
||||
sales_order.items[0].qty = 2
|
||||
sales_order.save()
|
||||
self.assertEqual(sales_order.taxes[0].tax_amount, 100)
|
||||
|
||||
sales_order.items[0].qty = 3
|
||||
sales_order.save()
|
||||
self.assertEqual(sales_order.taxes[0].tax_amount, 200)
|
||||
|
||||
sales_order.items[0].qty = 21
|
||||
sales_order.save()
|
||||
self.assertEqual(sales_order.taxes[0].tax_amount, 0)
|
||||
|
||||
def automatically_fetch_payment_terms(enable=1):
|
||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
||||
accounts_settings.automatically_fetch_payment_terms = enable
|
||||
|
@ -1315,7 +1315,7 @@
|
||||
"idx": 146,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-10-09 14:29:13.428984",
|
||||
"modified": "2022-03-10 14:29:13.428984",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
@ -13,7 +13,10 @@ from frappe.utils import cint, flt
|
||||
from erpnext.controllers.accounts_controller import get_taxes_and_charges
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from erpnext.stock.doctype.batch.batch import set_batch_nos
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no
|
||||
from erpnext.stock.doctype.serial_no.serial_no import (
|
||||
get_delivery_note_serial_no,
|
||||
update_serial_nos_after_submit,
|
||||
)
|
||||
from erpnext.stock.utils import calculate_mapped_packed_items_return
|
||||
|
||||
form_grid_templates = {
|
||||
@ -220,6 +223,9 @@ class DeliveryNote(SellingController):
|
||||
# Updating stock ledger should always be called after updating prevdoc status,
|
||||
# because updating reserved qty in bin depends upon updated delivered qty in SO
|
||||
self.update_stock_ledger()
|
||||
if self.is_return:
|
||||
update_serial_nos_after_submit(self, "items")
|
||||
|
||||
self.make_gl_entries()
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
@ -342,25 +348,21 @@ def update_billed_amount_based_on_so(so_detail, update_modified=True):
|
||||
from frappe.query_builder.functions import Sum
|
||||
|
||||
# Billed against Sales Order directly
|
||||
si = frappe.qb.DocType("Sales Invoice").as_("si")
|
||||
si_item = frappe.qb.DocType("Sales Invoice Item").as_("si_item")
|
||||
sum_amount = Sum(si_item.amount).as_("amount")
|
||||
|
||||
billed_against_so = frappe.qb.from_(si).from_(si_item).select(sum_amount).where(
|
||||
(si_item.parent == si.name) &
|
||||
billed_against_so = frappe.qb.from_(si_item).select(sum_amount).where(
|
||||
(si_item.so_detail == so_detail) &
|
||||
((si_item.dn_detail.isnull()) | (si_item.dn_detail == '')) &
|
||||
(si_item.docstatus == 1) &
|
||||
(si.update_stock == 0)
|
||||
(si_item.docstatus == 1)
|
||||
).run()
|
||||
billed_against_so = billed_against_so and billed_against_so[0][0] or 0
|
||||
|
||||
# Get all Delivery Note Item rows against the Sales Order Item row
|
||||
|
||||
dn = frappe.qb.DocType("Delivery Note").as_("dn")
|
||||
dn_item = frappe.qb.DocType("Delivery Note Item").as_("dn_item")
|
||||
|
||||
dn_details = frappe.qb.from_(dn).from_(dn_item).select(dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent, dn_item.stock_qty, dn_item.returned_qty).where(
|
||||
dn_details = frappe.qb.from_(dn).from_(dn_item).select(dn_item.name, dn_item.amount, dn_item.si_detail, dn_item.parent).where(
|
||||
(dn.name == dn_item.parent) &
|
||||
(dn_item.so_detail == so_detail) &
|
||||
(dn.docstatus == 1) &
|
||||
@ -385,11 +387,7 @@ def update_billed_amount_based_on_so(so_detail, update_modified=True):
|
||||
|
||||
# Distribute billed amount directly against SO between DNs based on FIFO
|
||||
if billed_against_so and billed_amt_agianst_dn < dnd.amount:
|
||||
if dnd.returned_qty:
|
||||
pending_to_bill = flt(dnd.amount) * (dnd.stock_qty - dnd.returned_qty) / dnd.stock_qty
|
||||
else:
|
||||
pending_to_bill = flt(dnd.amount)
|
||||
pending_to_bill -= billed_amt_agianst_dn
|
||||
pending_to_bill = flt(dnd.amount) - billed_amt_agianst_dn
|
||||
if pending_to_bill <= billed_against_so:
|
||||
billed_amt_agianst_dn += pending_to_bill
|
||||
billed_against_so -= pending_to_bill
|
||||
@ -798,3 +796,6 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
}, target_doc, set_missing_values)
|
||||
|
||||
return doclist
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("Delivery Note", ["customer", "is_return", "return_against"])
|
||||
|
@ -822,6 +822,11 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
|
||||
automatically_fetch_payment_terms(enable=0)
|
||||
|
||||
def test_standalone_serial_no_return(self):
|
||||
dn = create_delivery_note(item_code="_Test Serialized Item With Series", is_return=True, qty=-1)
|
||||
dn.reload()
|
||||
self.assertTrue(dn.items[0].serial_no)
|
||||
|
||||
def create_return_delivery_note(**args):
|
||||
args = frappe._dict(args)
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
|
@ -392,6 +392,7 @@ class Item(Document):
|
||||
self.validate_properties_before_merge(new_name)
|
||||
self.validate_duplicate_product_bundles_before_merge(old_name, new_name)
|
||||
self.validate_duplicate_website_item_before_merge(old_name, new_name)
|
||||
self.delete_old_bins(old_name)
|
||||
|
||||
def after_rename(self, old_name, new_name, merge):
|
||||
if merge:
|
||||
@ -420,6 +421,9 @@ class Item(Document):
|
||||
frappe.db.set_value(dt, d.name, "item_wise_tax_detail",
|
||||
json.dumps(item_wise_tax_detail), update_modified=False)
|
||||
|
||||
def delete_old_bins(self, old_name):
|
||||
frappe.db.delete("Bin", {"item_code": old_name})
|
||||
|
||||
def validate_duplicate_item_in_stock_reconciliation(self, old_name, new_name):
|
||||
records = frappe.db.sql(""" SELECT parent, COUNT(*) as records
|
||||
FROM `tabStock Reconciliation Item`
|
||||
@ -500,11 +504,11 @@ class Item(Document):
|
||||
existing_allow_negative_stock = frappe.db.get_value("Stock Settings", None, "allow_negative_stock")
|
||||
frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
|
||||
|
||||
repost_stock_for_warehouses = frappe.db.sql_list("""select distinct warehouse
|
||||
from tabBin where item_code=%s""", new_name)
|
||||
repost_stock_for_warehouses = frappe.get_all("Stock Ledger Entry",
|
||||
"warehouse", filters={"item_code": new_name}, pluck="warehouse", distinct=True)
|
||||
|
||||
# Delete all existing bins to avoid duplicate bins for the same item and warehouse
|
||||
frappe.db.sql("delete from `tabBin` where item_code=%s", new_name)
|
||||
frappe.db.delete("Bin", {"item_code": new_name})
|
||||
|
||||
for warehouse in repost_stock_for_warehouses:
|
||||
repost_stock(new_name, warehouse)
|
||||
|
@ -371,23 +371,24 @@ class TestItem(FrappeTestCase):
|
||||
variant.save()
|
||||
|
||||
def test_item_merging(self):
|
||||
create_item("Test Item for Merging 1")
|
||||
create_item("Test Item for Merging 2")
|
||||
old = create_item(frappe.generate_hash(length=20)).name
|
||||
new = create_item(frappe.generate_hash(length=20)).name
|
||||
|
||||
make_stock_entry(item_code="Test Item for Merging 1", target="_Test Warehouse - _TC",
|
||||
make_stock_entry(item_code=old, target="_Test Warehouse - _TC",
|
||||
qty=1, rate=100)
|
||||
make_stock_entry(item_code="Test Item for Merging 2", target="_Test Warehouse 1 - _TC",
|
||||
make_stock_entry(item_code=old, target="_Test Warehouse 1 - _TC",
|
||||
qty=1, rate=100)
|
||||
make_stock_entry(item_code=new, target="_Test Warehouse 1 - _TC",
|
||||
qty=1, rate=100)
|
||||
|
||||
frappe.rename_doc("Item", "Test Item for Merging 1", "Test Item for Merging 2", merge=True)
|
||||
frappe.rename_doc("Item", old, new, merge=True)
|
||||
|
||||
self.assertFalse(frappe.db.exists("Item", "Test Item for Merging 1"))
|
||||
self.assertFalse(frappe.db.exists("Item", old))
|
||||
|
||||
self.assertTrue(frappe.db.get_value("Bin",
|
||||
{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse - _TC"}))
|
||||
|
||||
{"item_code": new, "warehouse": "_Test Warehouse - _TC"}))
|
||||
self.assertTrue(frappe.db.get_value("Bin",
|
||||
{"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse 1 - _TC"}))
|
||||
{"item_code": new, "warehouse": "_Test Warehouse 1 - _TC"}))
|
||||
|
||||
def test_item_merging_with_product_bundle(self):
|
||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||
|
@ -214,6 +214,7 @@ frappe.ui.form.on('Material Request', {
|
||||
material_request_type: frm.doc.material_request_type,
|
||||
plc_conversion_rate: 1,
|
||||
rate: item.rate,
|
||||
uom: item.uom,
|
||||
conversion_factor: item.conversion_factor
|
||||
},
|
||||
overwrite_warehouse: overwrite_warehouse
|
||||
@ -392,6 +393,7 @@ frappe.ui.form.on("Material Request Item", {
|
||||
item_code: function(frm, doctype, name) {
|
||||
const item = locals[doctype][name];
|
||||
item.rate = 0;
|
||||
item.uom = '';
|
||||
set_schedule_date(frm);
|
||||
frm.events.get_item_data(frm, item, true);
|
||||
},
|
||||
|
@ -1165,7 +1165,7 @@
|
||||
"idx": 261,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-02-01 11:40:52.690984",
|
||||
"modified": "2022-03-10 11:40:52.690984",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt",
|
||||
|
@ -926,3 +926,6 @@ def get_item_account_wise_additional_cost(purchase_document):
|
||||
account.base_amount * item.get(based_on_field) / total_item_cost
|
||||
|
||||
return item_account_wise_cost
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("Purchase Receipt", ["supplier", "is_return", "return_against"])
|
||||
|
@ -769,8 +769,7 @@ class TestPurchaseReceipt(FrappeTestCase):
|
||||
update_purchase_receipt_status,
|
||||
)
|
||||
|
||||
pr = make_purchase_receipt(do_not_submit=True)
|
||||
pr.submit()
|
||||
pr = make_purchase_receipt()
|
||||
|
||||
update_purchase_receipt_status(pr.name, "Closed")
|
||||
self.assertEqual(
|
||||
|
@ -118,7 +118,8 @@ def repost(doc):
|
||||
doc.set_status('Failed')
|
||||
raise
|
||||
finally:
|
||||
frappe.db.commit()
|
||||
if not frappe.flags.in_test:
|
||||
frappe.db.commit()
|
||||
|
||||
def repost_sl_entries(doc):
|
||||
if doc.based_on == 'Transaction':
|
||||
|
@ -394,7 +394,7 @@ def update_serial_nos(sle, item_det):
|
||||
if not sle.is_cancelled and not sle.serial_no and cint(sle.actual_qty) > 0 \
|
||||
and item_det.has_serial_no == 1 and item_det.serial_no_series:
|
||||
serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty)
|
||||
frappe.db.set(sle, "serial_no", serial_nos)
|
||||
sle.db_set("serial_no", serial_nos)
|
||||
validate_serial_no(sle, item_det)
|
||||
if sle.serial_no:
|
||||
auto_make_serial_nos(sle)
|
||||
@ -516,13 +516,16 @@ def update_serial_nos_after_submit(controller, parentfield):
|
||||
if controller.doctype == "Stock Entry":
|
||||
warehouse = d.t_warehouse
|
||||
qty = d.transfer_qty
|
||||
elif controller.doctype in ("Sales Invoice", "Delivery Note"):
|
||||
warehouse = d.warehouse
|
||||
qty = d.stock_qty
|
||||
else:
|
||||
warehouse = d.warehouse
|
||||
qty = (d.qty if controller.doctype == "Stock Reconciliation"
|
||||
else d.stock_qty)
|
||||
for sle in stock_ledger_entries:
|
||||
if sle.voucher_detail_no==d.name:
|
||||
if not accepted_serial_nos_updated and qty and abs(sle.actual_qty)==qty \
|
||||
if not accepted_serial_nos_updated and qty and abs(sle.actual_qty) == abs(qty) \
|
||||
and sle.warehouse == warehouse and sle.serial_no != d.serial_no:
|
||||
d.serial_no = sle.serial_no
|
||||
frappe.db.set_value(d.doctype, d.name, "serial_no", sle.serial_no)
|
||||
|
@ -838,11 +838,13 @@ class update_entries_after(object):
|
||||
for warehouse, data in self.data.items():
|
||||
bin_name = get_or_make_bin(self.item_code, warehouse)
|
||||
|
||||
frappe.db.set_value('Bin', bin_name, {
|
||||
"valuation_rate": data.valuation_rate,
|
||||
updated_values = {
|
||||
"actual_qty": data.qty_after_transaction,
|
||||
"stock_value": data.stock_value
|
||||
})
|
||||
}
|
||||
if data.valuation_rate is not None:
|
||||
updated_values["valuation_rate"] = data.valuation_rate
|
||||
frappe.db.set_value('Bin', bin_name, updated_values)
|
||||
|
||||
|
||||
def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
|
||||
|
Loading…
x
Reference in New Issue
Block a user