Merge branch 'develop' into patch-2
This commit is contained in:
commit
5dcb723fc7
@ -43,9 +43,9 @@ erpnext.asset.set_accumulated_depreciation = function(frm) {
|
||||
if(frm.doc.depreciation_method != "Manual") return;
|
||||
|
||||
var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
|
||||
$.each(frm.doc.schedules || [], function(i, row) {
|
||||
|
||||
$.each(frm.doc.depreciation_schedule || [], function(i, row) {
|
||||
accumulated_depreciation += flt(row.depreciation_amount);
|
||||
frappe.model.set_value(row.doctype, row.name,
|
||||
"accumulated_depreciation_amount", accumulated_depreciation);
|
||||
frappe.model.set_value(row.doctype, row.name, "accumulated_depreciation_amount", accumulated_depreciation);
|
||||
})
|
||||
};
|
||||
|
@ -10,7 +10,9 @@
|
||||
"asset",
|
||||
"naming_series",
|
||||
"column_break_2",
|
||||
"gross_purchase_amount",
|
||||
"opening_accumulated_depreciation",
|
||||
"number_of_depreciations_booked",
|
||||
"finance_book",
|
||||
"finance_book_id",
|
||||
"depreciation_details_section",
|
||||
@ -148,18 +150,36 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "opening_accumulated_depreciation",
|
||||
"fieldname": "opening_accumulated_depreciation",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Opening Accumulated Depreciation",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "gross_purchase_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Gross Purchase Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "number_of_depreciations_booked",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 1,
|
||||
"label": "Number of Depreciations Booked",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-01-16 21:08:21.421260",
|
||||
"modified": "2023-02-26 16:37:23.734806",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Depreciation Schedule",
|
||||
|
@ -4,7 +4,15 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import add_days, add_months, cint, flt, get_last_day, is_last_day_of_the_month
|
||||
from frappe.utils import (
|
||||
add_days,
|
||||
add_months,
|
||||
cint,
|
||||
flt,
|
||||
get_last_day,
|
||||
getdate,
|
||||
is_last_day_of_the_month,
|
||||
)
|
||||
|
||||
|
||||
class AssetDepreciationSchedule(Document):
|
||||
@ -83,15 +91,58 @@ class AssetDepreciationSchedule(Document):
|
||||
date_of_return=None,
|
||||
update_asset_finance_book_row=True,
|
||||
):
|
||||
have_asset_details_been_modified = self.have_asset_details_been_modified(asset_doc)
|
||||
not_manual_depr_or_have_manual_depr_details_been_modified = (
|
||||
self.not_manual_depr_or_have_manual_depr_details_been_modified(row)
|
||||
)
|
||||
|
||||
self.set_draft_asset_depr_schedule_details(asset_doc, row)
|
||||
|
||||
if self.should_prepare_depreciation_schedule(
|
||||
have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified
|
||||
):
|
||||
self.make_depr_schedule(asset_doc, row, date_of_disposal, update_asset_finance_book_row)
|
||||
self.set_accumulated_depreciation(row, date_of_disposal, date_of_return)
|
||||
|
||||
def have_asset_details_been_modified(self, asset_doc):
|
||||
return (
|
||||
asset_doc.gross_purchase_amount != self.gross_purchase_amount
|
||||
or asset_doc.opening_accumulated_depreciation != self.opening_accumulated_depreciation
|
||||
or asset_doc.number_of_depreciations_booked != self.number_of_depreciations_booked
|
||||
)
|
||||
|
||||
def not_manual_depr_or_have_manual_depr_details_been_modified(self, row):
|
||||
return (
|
||||
self.depreciation_method != "Manual"
|
||||
or row.total_number_of_depreciations != self.total_number_of_depreciations
|
||||
or row.frequency_of_depreciation != self.frequency_of_depreciation
|
||||
or getdate(row.depreciation_start_date) != self.get("depreciation_schedule")[0].schedule_date
|
||||
or row.expected_value_after_useful_life != self.expected_value_after_useful_life
|
||||
)
|
||||
|
||||
def should_prepare_depreciation_schedule(
|
||||
self, have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified
|
||||
):
|
||||
if not self.get("depreciation_schedule"):
|
||||
return True
|
||||
|
||||
old_asset_depr_schedule_doc = self.get_doc_before_save()
|
||||
|
||||
if self.docstatus != 0 and not old_asset_depr_schedule_doc:
|
||||
return True
|
||||
|
||||
if have_asset_details_been_modified or not_manual_depr_or_have_manual_depr_details_been_modified:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def set_draft_asset_depr_schedule_details(self, asset_doc, row):
|
||||
self.asset = asset_doc.name
|
||||
self.finance_book = row.finance_book
|
||||
self.finance_book_id = row.idx
|
||||
self.opening_accumulated_depreciation = asset_doc.opening_accumulated_depreciation
|
||||
self.number_of_depreciations_booked = asset_doc.number_of_depreciations_booked
|
||||
self.gross_purchase_amount = asset_doc.gross_purchase_amount
|
||||
self.depreciation_method = row.depreciation_method
|
||||
self.total_number_of_depreciations = row.total_number_of_depreciations
|
||||
self.frequency_of_depreciation = row.frequency_of_depreciation
|
||||
@ -102,7 +153,7 @@ class AssetDepreciationSchedule(Document):
|
||||
def make_depr_schedule(
|
||||
self, asset_doc, row, date_of_disposal, update_asset_finance_book_row=True
|
||||
):
|
||||
if row.depreciation_method != "Manual" and not self.get("depreciation_schedule"):
|
||||
if not self.get("depreciation_schedule"):
|
||||
self.depreciation_schedule = []
|
||||
|
||||
if not asset_doc.available_for_use_date:
|
||||
@ -293,7 +344,9 @@ class AssetDepreciationSchedule(Document):
|
||||
ignore_booked_entry=False,
|
||||
):
|
||||
straight_line_idx = [
|
||||
d.idx for d in self.get("depreciation_schedule") if d.depreciation_method == "Straight Line"
|
||||
d.idx
|
||||
for d in self.get("depreciation_schedule")
|
||||
if d.depreciation_method == "Straight Line" or d.depreciation_method == "Manual"
|
||||
]
|
||||
|
||||
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
|
||||
|
@ -27,7 +27,13 @@ def get_details_of_draft_or_submitted_depreciable_assets():
|
||||
|
||||
records = (
|
||||
frappe.qb.from_(asset)
|
||||
.select(asset.name, asset.opening_accumulated_depreciation, asset.docstatus)
|
||||
.select(
|
||||
asset.name,
|
||||
asset.opening_accumulated_depreciation,
|
||||
asset.gross_purchase_amount,
|
||||
asset.number_of_depreciations_booked,
|
||||
asset.docstatus,
|
||||
)
|
||||
.where(asset.calculate_depreciation == 1)
|
||||
.where(asset.docstatus < 2)
|
||||
).run(as_dict=True)
|
||||
|
@ -309,9 +309,12 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
|
||||
make_work_order() {
|
||||
var me = this;
|
||||
this.frm.call({
|
||||
doc: this.frm.doc,
|
||||
method: 'get_work_order_items',
|
||||
me.frm.call({
|
||||
method: "erpnext.selling.doctype.sales_order.sales_order.get_work_order_items",
|
||||
args: {
|
||||
sales_order: this.frm.docname,
|
||||
},
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if(!r.message) {
|
||||
frappe.msgprint({
|
||||
@ -321,14 +324,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
});
|
||||
return;
|
||||
}
|
||||
else if(!r.message) {
|
||||
frappe.msgprint({
|
||||
title: __('Work Order not created'),
|
||||
message: __('Work Order already created for all items with BOM'),
|
||||
indicator: 'orange'
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
else {
|
||||
const fields = [{
|
||||
label: 'Items',
|
||||
fieldtype: 'Table',
|
||||
@ -429,9 +425,9 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
make_raw_material_request() {
|
||||
var me = this;
|
||||
this.frm.call({
|
||||
doc: this.frm.doc,
|
||||
method: 'get_work_order_items',
|
||||
method: "erpnext.selling.doctype.sales_order.sales_order.get_work_order_items",
|
||||
args: {
|
||||
sales_order: this.frm.docname,
|
||||
for_raw_material_request: 1
|
||||
},
|
||||
callback: function(r) {
|
||||
@ -450,6 +446,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
}
|
||||
|
||||
make_raw_material_request_dialog(r) {
|
||||
var me = this;
|
||||
var fields = [
|
||||
{fieldtype:'Check', fieldname:'include_exploded_items',
|
||||
label: __('Include Exploded Items')},
|
||||
|
@ -6,11 +6,12 @@ import json
|
||||
|
||||
import frappe
|
||||
import frappe.utils
|
||||
from frappe import _
|
||||
from frappe import _, qb
|
||||
from frappe.contacts.doctype.address.address import get_company_address
|
||||
from frappe.desk.notifications import clear_doctype_notifications
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.model.utils import get_fetch_values
|
||||
from frappe.query_builder.functions import Sum
|
||||
from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate, strip_html
|
||||
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
|
||||
@ -414,51 +415,6 @@ class SalesOrder(SellingController):
|
||||
self.indicator_color = "green"
|
||||
self.indicator_title = _("Paid")
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_work_order_items(self, for_raw_material_request=0):
|
||||
"""Returns items with BOM that already do not have a linked work order"""
|
||||
items = []
|
||||
item_codes = [i.item_code for i in self.items]
|
||||
product_bundle_parents = [
|
||||
pb.new_item_code
|
||||
for pb in frappe.get_all(
|
||||
"Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"]
|
||||
)
|
||||
]
|
||||
|
||||
for table in [self.items, self.packed_items]:
|
||||
for i in table:
|
||||
bom = get_default_bom(i.item_code)
|
||||
stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
|
||||
|
||||
if not for_raw_material_request:
|
||||
total_work_order_qty = flt(
|
||||
frappe.db.sql(
|
||||
"""select sum(qty) from `tabWork Order`
|
||||
where production_item=%s and sales_order=%s and sales_order_item = %s and docstatus<2""",
|
||||
(i.item_code, self.name, i.name),
|
||||
)[0][0]
|
||||
)
|
||||
pending_qty = stock_qty - total_work_order_qty
|
||||
else:
|
||||
pending_qty = stock_qty
|
||||
|
||||
if pending_qty and i.item_code not in product_bundle_parents:
|
||||
items.append(
|
||||
dict(
|
||||
name=i.name,
|
||||
item_code=i.item_code,
|
||||
description=i.description,
|
||||
bom=bom or "",
|
||||
warehouse=i.warehouse,
|
||||
pending_qty=pending_qty,
|
||||
required_qty=pending_qty if for_raw_material_request else 0,
|
||||
sales_order_item=i.name,
|
||||
)
|
||||
)
|
||||
|
||||
return items
|
||||
|
||||
def on_recurring(self, reference_doc, auto_repeat_doc):
|
||||
def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date):
|
||||
delivery_date = auto_repeat_doc.get_next_schedule_date(schedule_date=ref_doc_delivery_date)
|
||||
@ -1350,3 +1306,57 @@ def update_produced_qty_in_so_item(sales_order, sales_order_item):
|
||||
return
|
||||
|
||||
frappe.db.set_value("Sales Order Item", sales_order_item, "produced_qty", total_produced_qty)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_work_order_items(sales_order, for_raw_material_request=0):
|
||||
"""Returns items with BOM that already do not have a linked work order"""
|
||||
if sales_order:
|
||||
so = frappe.get_doc("Sales Order", sales_order)
|
||||
|
||||
wo = qb.DocType("Work Order")
|
||||
|
||||
items = []
|
||||
item_codes = [i.item_code for i in so.items]
|
||||
product_bundle_parents = [
|
||||
pb.new_item_code
|
||||
for pb in frappe.get_all(
|
||||
"Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"]
|
||||
)
|
||||
]
|
||||
|
||||
for table in [so.items, so.packed_items]:
|
||||
for i in table:
|
||||
bom = get_default_bom(i.item_code)
|
||||
stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
|
||||
|
||||
if not for_raw_material_request:
|
||||
total_work_order_qty = flt(
|
||||
qb.from_(wo)
|
||||
.select(Sum(wo.qty))
|
||||
.where(
|
||||
(wo.production_item == i.item_code)
|
||||
& (wo.sales_order == so.name) * (wo.sales_order_item == i.name)
|
||||
& (wo.docstatus.lte(2))
|
||||
)
|
||||
.run()[0][0]
|
||||
)
|
||||
pending_qty = stock_qty - total_work_order_qty
|
||||
else:
|
||||
pending_qty = stock_qty
|
||||
|
||||
if pending_qty and i.item_code not in product_bundle_parents:
|
||||
items.append(
|
||||
dict(
|
||||
name=i.name,
|
||||
item_code=i.item_code,
|
||||
description=i.description,
|
||||
bom=bom or "",
|
||||
warehouse=i.warehouse,
|
||||
pending_qty=pending_qty,
|
||||
required_qty=pending_qty if for_raw_material_request else 0,
|
||||
sales_order_item=i.name,
|
||||
)
|
||||
)
|
||||
|
||||
return items
|
||||
|
@ -1217,6 +1217,8 @@ class TestSalesOrder(FrappeTestCase):
|
||||
self.assertTrue(si.get("payment_schedule"))
|
||||
|
||||
def test_make_work_order(self):
|
||||
from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
|
||||
|
||||
# Make a new Sales Order
|
||||
so = make_sales_order(
|
||||
**{
|
||||
@ -1230,7 +1232,7 @@ class TestSalesOrder(FrappeTestCase):
|
||||
# Raise Work Orders
|
||||
po_items = []
|
||||
so_item_name = {}
|
||||
for item in so.get_work_order_items():
|
||||
for item in get_work_order_items(so.name):
|
||||
po_items.append(
|
||||
{
|
||||
"warehouse": item.get("warehouse"),
|
||||
@ -1448,6 +1450,7 @@ class TestSalesOrder(FrappeTestCase):
|
||||
|
||||
from erpnext.controllers.item_variant import create_variant
|
||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||
from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
|
||||
|
||||
make_item( # template item
|
||||
"Test-WO-Tshirt",
|
||||
@ -1487,7 +1490,7 @@ class TestSalesOrder(FrappeTestCase):
|
||||
]
|
||||
}
|
||||
)
|
||||
wo_items = so.get_work_order_items()
|
||||
wo_items = get_work_order_items(so.name)
|
||||
|
||||
self.assertEqual(wo_items[0].get("item_code"), "Test-WO-Tshirt-R")
|
||||
self.assertEqual(wo_items[0].get("bom"), red_var_bom.name)
|
||||
@ -1497,6 +1500,8 @@ class TestSalesOrder(FrappeTestCase):
|
||||
self.assertEqual(wo_items[1].get("bom"), template_bom.name)
|
||||
|
||||
def test_request_for_raw_materials(self):
|
||||
from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
|
||||
|
||||
item = make_item(
|
||||
"_Test Finished Item",
|
||||
{
|
||||
@ -1529,7 +1534,7 @@ class TestSalesOrder(FrappeTestCase):
|
||||
so = make_sales_order(**{"item_list": [{"item_code": item.item_code, "qty": 1, "rate": 1000}]})
|
||||
so.submit()
|
||||
mr_dict = frappe._dict()
|
||||
items = so.get_work_order_items(1)
|
||||
items = get_work_order_items(so.name, 1)
|
||||
mr_dict["items"] = items
|
||||
mr_dict["include_exploded_items"] = 0
|
||||
mr_dict["ignore_existing_ordered_qty"] = 1
|
||||
|
Loading…
Reference in New Issue
Block a user