Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 37 additions & 21 deletions landms/landms/doctype/land_acquisition/land_acquisition.py
Original file line number Diff line number Diff line change
Expand Up @@ -972,35 +972,51 @@ def _finish(log, cost_to_save=None):
continue

try:
sr = frappe.get_doc({
"doctype": "Stock Reconciliation",
"purpose": "Stock Reconciliation",
"company": la.company,
"posting_date": today(),
"expense_account": lud_account,
"cost_center": cost_center,
"items": [{
"item_code": item_code,
"warehouse": warehouse,
"qty": 1,
"valuation_rate": new_plot_cost,
"serial_no": plot.serial_no or plot.name,
"use_serial_batch_fields": 1,
}],
})
sr.insert(ignore_permissions=True)
sr.submit()

frappe.db.set_value("Plot Master", plot.name, {
"allocated_cost": new_plot_cost,
"cost_per_sqm": flt(cost_per_sqm),
}, update_modified=False)

if plot.serial_no:
frappe.db.set_value("Serial No", plot.serial_no, "purchase_rate", new_plot_cost, update_modified=False)

# Update original stock entry SLE + Serial and Batch Bundle
# so Repost and future DN outgoing rates use the correct cost
if plot.stock_entry:
sle_name = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_no": plot.stock_entry, "is_cancelled": 0},
"name"
)
if sle_name:
frappe.db.set_value("Stock Ledger Entry", sle_name, {
"incoming_rate": new_plot_cost,
"stock_value_difference": new_plot_cost,
}, update_modified=False)

# Update the inward Serial and Batch Bundle avg_rate
bundle_name = frappe.db.get_value(
"Stock Entry Detail",
{"parent": plot.stock_entry, "serial_no": plot.serial_no},
"serial_and_batch_bundle"
)
if bundle_name:
frappe.db.set_value("Serial and Batch Bundle", bundle_name, {
"avg_rate": new_plot_cost,
"total_amount": new_plot_cost,
}, update_modified=False)
frappe.db.sql("""
UPDATE `tabSerial and Batch Entry`
SET incoming_rate=%s
WHERE parent=%s AND serial_no=%s
""", (new_plot_cost, bundle_name, plot.serial_no))

item_codes_touched.add(item_code)
updated.append(f"{plot.name} ({old_plot_cost:.0f} → {new_plot_cost:.0f})")

except Exception:
frappe.log_error(frappe.get_traceback(), f"Plot cost recalc SR failed: {plot.name}")
failures.append(f"{plot.name} — stock reconciliation failed (see Error Log)")
frappe.log_error(frappe.get_traceback(), f"Plot cost recalc failed: {plot.name}")
failures.append(f"{plot.name} — cost update failed (see Error Log)")

# Repost Item Valuation — one per unique item code
for ic in item_codes_touched:
Expand Down
51 changes: 49 additions & 2 deletions landms/landms/doctype/plot_handover/plot_handover.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import frappe
from frappe.model.document import Document
from frappe.utils import get_fullname, today
from frappe.utils import flt, get_fullname, today

from landms.landms.doctype.land_acquisition.land_acquisition import sync_land_acquisition_plot_summary
from landms.landms.doctype.plot_master.plot_master import get_plot_item_code
Expand Down Expand Up @@ -95,6 +95,8 @@ def _ensure_delivery_note(self):
if not plot.serial_no:
frappe.throw(f"Plot {plot.name} is missing its Serial No.")

self._cancel_blocking_stock_reconciliations(plot.serial_no)

if contract.sales_order and frappe.db.exists("Sales Order", contract.sales_order):
dn = self._make_delivery_note_from_sales_order(contract.sales_order, plot)
else:
Expand Down Expand Up @@ -125,11 +127,13 @@ def _make_delivery_note_from_sales_order(self, sales_order_name, plot):
if not dn.items:
frappe.throw(f"Sales Order {sales_order_name} has no deliverable rows for handover.")

inv_warehouse = frappe.db.get_single_value("LandMS Settings", "plot_inventory_warehouse")
for row in dn.items:
row.qty = 1
row.serial_no = plot.serial_no
row.use_serial_batch_fields = 1
row.warehouse = row.warehouse or frappe.db.get_single_value("LandMS Settings", "plot_inventory_warehouse")
row.warehouse = inv_warehouse
row.incoming_rate = flt(plot.allocated_cost)

return dn

Expand All @@ -147,6 +151,7 @@ def _make_delivery_note_direct(self, contract, plot, settings):
"item_code": item_code,
"qty": 1,
"rate": plot.selling_price,
"incoming_rate": flt(plot.allocated_cost),
"warehouse": settings.plot_inventory_warehouse,
"serial_no": plot.serial_no,
"use_serial_batch_fields": 1,
Expand All @@ -156,6 +161,48 @@ def _make_delivery_note_direct(self, contract, plot, settings):
}
)

def _cancel_blocking_stock_reconciliations(self, serial_no):
# Cancel ALL SRs for this LA newest-first to avoid chained dependency errors.
# A single SR can reference multiple plot serials so we must clear the whole LA.
la = frappe.db.get_value("Plot Master", self.plot, "land_acquisition")
if not la:
return

sr_names = frappe.db.sql("""
SELECT DISTINCT sr.name
FROM `tabStock Reconciliation` sr
JOIN `tabStock Reconciliation Item` sri ON sri.parent = sr.name
WHERE sri.serial_no IN (
SELECT serial_no FROM `tabPlot Master`
WHERE land_acquisition = %s AND docstatus = 1 AND serial_no IS NOT NULL
) AND sr.docstatus = 1
ORDER BY sr.posting_date DESC, sr.name DESC
""", la, pluck="name")

if not sr_names:
return

for name in sr_names:
try:
sr = frappe.get_doc("Stock Reconciliation", name)
sr.flags.ignore_permissions = True
sr.cancel()
frappe.db.commit()
except Exception:
frappe.log_error(frappe.get_traceback(), f"Could not cancel blocking SR {name} for serial {serial_no}")

# Reset any plot serials that are stuck in Delivered state after SR cancellations
plot_serials = frappe.db.sql("""
SELECT serial_no FROM `tabPlot Master`
WHERE land_acquisition = %s AND docstatus = 1
AND serial_no IS NOT NULL AND status NOT IN ('Delivered', 'Title Closed')
""", la, pluck="serial_no")

for sn in plot_serials:
sn_status = frappe.db.get_value("Serial No", sn, "status")
if sn_status == "Delivered":
frappe.db.set_value("Serial No", sn, "status", "Active")

def _cancel_delivery_note(self):
if not self.delivery_note or not frappe.db.exists("Delivery Note", self.delivery_note):
return
Expand Down
Loading