Merge pull request #34327 from frappe/version-14-hotfix

chore: release v14
This commit is contained in:
Deepesh Garg
2023-03-07 19:54:30 +05:30
committed by GitHub
19 changed files with 331 additions and 160 deletions

View File

@@ -137,7 +137,8 @@
"fieldname": "finance_book",
"fieldtype": "Link",
"label": "Finance Book",
"options": "Finance Book"
"options": "Finance Book",
"read_only": 1
},
{
"fieldname": "2_add_edit_gl_entries",
@@ -538,7 +539,7 @@
"idx": 176,
"is_submittable": 1,
"links": [],
"modified": "2023-01-17 12:53:53.280620",
"modified": "2023-03-01 14:58:59.286591",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@@ -495,26 +495,22 @@ def get_amount(ref_doc, payment_account=None):
"""get amount based on doctype"""
dt = ref_doc.doctype
if dt in ["Sales Order", "Purchase Order"]:
grand_total = flt(ref_doc.rounded_total) - flt(ref_doc.advance_paid)
grand_total = flt(ref_doc.rounded_total) or flt(ref_doc.grand_total)
elif dt in ["Sales Invoice", "Purchase Invoice"]:
if ref_doc.party_account_currency == ref_doc.currency:
grand_total = flt(ref_doc.outstanding_amount)
else:
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
elif dt == "POS Invoice":
for pay in ref_doc.payments:
if pay.type == "Phone" and pay.account == payment_account:
grand_total = pay.amount
break
elif dt == "Fees":
grand_total = ref_doc.outstanding_amount
if grand_total > 0:
return grand_total
else:
frappe.throw(_("Payment Entry is already created"))

View File

@@ -45,7 +45,10 @@ class TestPaymentRequest(unittest.TestCase):
frappe.get_doc(method).insert(ignore_permissions=True)
def test_payment_request_linkings(self):
so_inr = make_sales_order(currency="INR")
so_inr = make_sales_order(currency="INR", do_not_save=True)
so_inr.disable_rounded_total = 1
so_inr.save()
pr = make_payment_request(
dt="Sales Order",
dn=so_inr.name,

View File

@@ -1485,11 +1485,17 @@ class PurchaseInvoice(BuyingController):
if po_details:
updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
adjust_incoming_rate = frappe.db.get_single_value(
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate"
)
for pr in set(updated_pr):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
pr_doc = frappe.get_doc("Purchase Receipt", pr)
update_billing_percentage(pr_doc, update_modified=update_modified)
update_billing_percentage(
pr_doc, update_modified=update_modified, adjust_incoming_rate=adjust_incoming_rate
)
def get_pr_details_billed_amt(self):
# Get billed amount based on purchase receipt item reference (pr_detail) in purchase invoice

View File

@@ -1523,6 +1523,94 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
company.enable_provisional_accounting_for_non_stock_items = 0
company.save()
def test_adjust_incoming_rate(self):
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
frappe.db.set_single_value(
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1
)
# Increase the cost of the item
pr = make_purchase_receipt(qty=1, rate=100)
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 100)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 150
pi.save()
pi.submit()
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 150)
# Reduce the cost of the item
pr = make_purchase_receipt(qty=1, rate=100)
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 100)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 50
pi.save()
pi.submit()
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 50)
frappe.db.set_single_value(
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0
)
# Don't adjust incoming rate
pr = make_purchase_receipt(qty=1, rate=100)
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 100)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 50
pi.save()
pi.submit()
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 100)
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
def test_item_less_defaults(self):
pi = frappe.new_doc("Purchase Invoice")

View File

@@ -38,8 +38,11 @@
{% if(data[i].posting_date) { %}
<td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td>
<td>{%= data[i].voucher_type %}
<br>{%= data[i].voucher_no %}</td>
<td>
<br>{%= data[i].voucher_no %}
</td>
{% var longest_word = cstr(data[i].remarks).split(" ").reduce((longest, word) => word.length > longest.length ? word : longest, ""); %}
<td {% if longest_word.length > 45 %} class="overflow-wrap-anywhere" {% endif %}>
<span>
{% if(!(filters.party || filters.account)) { %}
{%= data[i].party || data[i].account %}
<br>
@@ -49,11 +52,14 @@
{% if(data[i].bill_no) { %}
<br>{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %}
{% } %}
</td>
<td style="text-align: right">
{%= format_currency(data[i].debit, filters.presentation_currency || data[i].account_currency) %}</td>
<td style="text-align: right">
{%= format_currency(data[i].credit, filters.presentation_currency || data[i].account_currency) %}</td>
</span>
</td>
<td style="text-align: right">
{%= format_currency(data[i].debit, filters.presentation_currency) %}
</td>
<td style="text-align: right">
{%= format_currency(data[i].credit, filters.presentation_currency) %}
</td>
{% } else { %}
<td></td>
<td></td>

View File

@@ -18,6 +18,7 @@
"pr_required",
"column_break_12",
"maintain_same_rate",
"set_landed_cost_based_on_purchase_invoice_rate",
"allow_multiple_items",
"bill_for_rejected_quantity_in_purchase_invoice",
"disable_last_purchase_rate",
@@ -147,6 +148,14 @@
"fieldname": "show_pay_button",
"fieldtype": "Check",
"label": "Show Pay Button in Purchase Order Portal"
},
{
"default": "0",
"depends_on": "eval: !doc.maintain_same_rate",
"description": "Users can enable the checkbox If they want to adjust the incoming rate (set using purchase receipt) based on the purchase invoice rate.",
"fieldname": "set_landed_cost_based_on_purchase_invoice_rate",
"fieldtype": "Check",
"label": "Set Landed Cost Based on Purchase Invoice Rate"
}
],
"icon": "fa fa-cog",
@@ -154,7 +163,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-02-15 14:42:10.200679",
"modified": "2023-02-28 15:41:32.686805",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",

View File

@@ -21,3 +21,10 @@ class BuyingSettings(Document):
self.get("supp_master_name") == "Naming Series",
hide_name_field=False,
)
def before_save(self):
self.check_maintain_same_rate()
def check_maintain_same_rate(self):
if self.maintain_same_rate:
self.set_landed_cost_based_on_purchase_invoice_rate = 0

View File

@@ -269,7 +269,10 @@ class BuyingController(SubcontractingController):
) / qty_in_stock_uom
else:
item.valuation_rate = (
item.base_net_amount + item.item_tax_amount + flt(item.landed_cost_voucher_amount)
item.base_net_amount
+ item.item_tax_amount
+ flt(item.landed_cost_voucher_amount)
+ flt(item.get("rate_difference_with_purchase_invoice"))
) / qty_in_stock_uom
else:
item.valuation_rate = 0.0

View File

@@ -87,6 +87,9 @@ class SellingController(StockController):
)
if not self.meta.get_field("sales_team"):
party_details.pop("sales_team")
else:
self.set("sales_team", party_details.get("sales_team"))
self.update_if_missing(party_details)
elif lead:
@@ -139,7 +142,7 @@ class SellingController(StockController):
self.in_words = money_in_words(amount, self.currency)
def calculate_commission(self):
if not self.meta.get_field("commission_rate"):
if not self.meta.get_field("commission_rate") or self.docstatus.is_submitted():
return
self.round_floats_in(self, ("amount_eligible_for_commission", "commission_rate"))

View File

@@ -368,7 +368,7 @@ auto_cancel_exempted_doctypes = [
scheduler_events = {
"cron": {
"0/5 * * * *": [
"0/15 * * * *": [
"erpnext.manufacturing.doctype.bom_update_log.bom_update_log.resume_bom_cost_update_jobs",
],
"0/30 * * * *": [

View File

@@ -212,7 +212,7 @@ def resume_bom_cost_update_jobs():
["name", "boms_updated", "status"],
)
incomplete_level = any(row.get("status") == "Pending" for row in bom_batches)
if not bom_batches or incomplete_level:
if not bom_batches or not incomplete_level:
continue
# Prep parent BOMs & updated processed BOMs for next level
@@ -252,6 +252,9 @@ def get_processed_current_boms(
current_boms = []
for row in bom_batches:
if not row.boms_updated:
continue
boms_updated = json.loads(row.boms_updated)
current_boms.extend(boms_updated)
boms_updated_dict = {bom: True for bom in boms_updated}

View File

@@ -488,7 +488,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
() => {
var d = locals[cdt][cdn];
me.add_taxes_from_item_tax_template(d.item_tax_rate);
if (d.free_item_data) {
if (d.free_item_data && d.free_item_data.length > 0) {
me.apply_product_discount(d);
}
},
@@ -1884,11 +1884,13 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
get_advances() {
if(!this.frm.is_return) {
var me = this;
return this.frm.call({
method: "set_advances",
doc: this.frm.doc,
callback: function(r, rt) {
refresh_field("advances");
me.frm.dirty();
}
})
}

View File

@@ -253,7 +253,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran
}
calculate_commission() {
if(!this.frm.fields_dict.commission_rate) return;
if(!this.frm.fields_dict.commission_rate || this.frm.doc.docstatus === 1) return;
if(this.frm.doc.commission_rate > 100) {
this.frm.set_value("commission_rate", 100);

View File

@@ -293,6 +293,7 @@ class PurchaseReceipt(BuyingController):
get_purchase_document_details,
)
stock_rbnb = None
if erpnext.is_perpetual_inventory_enabled(self.company):
stock_rbnb = self.get_company_default("stock_received_but_not_billed")
landed_cost_entries = get_item_account_wise_additional_cost(self.name)
@@ -450,6 +451,21 @@ class PurchaseReceipt(BuyingController):
item=d,
)
if d.rate_difference_with_purchase_invoice and stock_rbnb:
account_currency = get_account_currency(stock_rbnb)
self.add_gl_entry(
gl_entries=gl_entries,
account=stock_rbnb,
cost_center=d.cost_center,
debit=0.0,
credit=flt(d.rate_difference_with_purchase_invoice),
remarks=_("Adjustment based on Purchase Invoice rate"),
against_account=warehouse_account_name,
account_currency=account_currency,
project=d.project,
item=d,
)
# sub-contracting warehouse
if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
self.add_gl_entry(
@@ -470,6 +486,7 @@ class PurchaseReceipt(BuyingController):
+ flt(d.landed_cost_voucher_amount)
+ flt(d.rm_supp_cost)
+ flt(d.item_tax_amount)
+ flt(d.rate_difference_with_purchase_invoice)
)
divisional_loss = flt(
@@ -765,7 +782,7 @@ class PurchaseReceipt(BuyingController):
updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
for pr in set(updated_pr):
pr_doc = self if (pr == self.name) else frappe.get_cached_doc("Purchase Receipt", pr)
pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr)
update_billing_percentage(pr_doc, update_modified=update_modified)
self.load_from_db()
@@ -881,7 +898,7 @@ def get_billed_amount_against_po(po_items):
return {d.po_detail: flt(d.billed_amt) for d in query}
def update_billing_percentage(pr_doc, update_modified=True):
def update_billing_percentage(pr_doc, update_modified=True, adjust_incoming_rate=False):
# Reload as billed amount was set in db directly
pr_doc.load_from_db()
@@ -897,6 +914,12 @@ def update_billing_percentage(pr_doc, update_modified=True):
total_amount += total_billable_amount
total_billed_amount += flt(item.billed_amt)
if adjust_incoming_rate:
adjusted_amt = 0.0
if item.billed_amt and item.amount:
adjusted_amt = flt(item.billed_amt) - flt(item.amount)
item.db_set("rate_difference_with_purchase_invoice", adjusted_amt, update_modified=False)
percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6)
pr_doc.db_set("per_billed", percent_billed)
@@ -906,6 +929,26 @@ def update_billing_percentage(pr_doc, update_modified=True):
pr_doc.set_status(update=True)
pr_doc.notify_update()
if adjust_incoming_rate:
adjust_incoming_rate_for_pr(pr_doc)
def adjust_incoming_rate_for_pr(doc):
doc.update_valuation_rate(reset_outgoing_rate=False)
for item in doc.get("items"):
item.db_update()
doc.docstatus = 2
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
doc.make_gl_entries_on_cancel()
# update stock & gl entries for submit state of PR
doc.docstatus = 1
doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True)
doc.make_gl_entries()
doc.repost_future_sle_and_gle()
def get_item_wise_returned_qty(pr_doc):
items = [d.name for d in pr_doc.items]

View File

@@ -69,6 +69,7 @@
"item_tax_amount",
"rm_supp_cost",
"landed_cost_voucher_amount",
"rate_difference_with_purchase_invoice",
"billed_amt",
"warehouse_and_reference",
"warehouse",
@@ -1007,12 +1008,20 @@
"fieldtype": "Check",
"label": "Has Item Scanned",
"read_only": 1
},
{
"fieldname": "rate_difference_with_purchase_invoice",
"fieldtype": "Currency",
"label": "Rate Difference with Purchase Invoice",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-01-18 15:48:58.114923",
"modified": "2023-02-28 15:43:04.470104",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",

View File

@@ -397,6 +397,7 @@ class StockReconciliation(StockController):
"voucher_type": self.doctype,
"voucher_no": self.name,
"voucher_detail_no": row.name,
"actual_qty": 0,
"company": self.company,
"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
"is_cancelled": 1 if self.docstatus == 2 else 0,
@@ -423,6 +424,8 @@ class StockReconciliation(StockController):
data.valuation_rate = flt(row.valuation_rate)
data.stock_value_difference = -1 * flt(row.amount_difference)
self.update_inventory_dimensions(row, data)
return data
def make_sle_on_cancel(self):

View File

@@ -8,6 +8,7 @@ import frappe
from frappe import _, throw
from frappe.model import child_table_fields, default_fields
from frappe.model.meta import get_field_precision
from frappe.query_builder.functions import CombineDatetime, IfNull, Sum
from frappe.utils import add_days, add_months, cint, cstr, flt, getdate
from erpnext import get_company_currency
@@ -526,12 +527,8 @@ def get_barcode_data(items_list):
itemwise_barcode = {}
for item in items_list:
barcodes = frappe.db.sql(
"""
select barcode from `tabItem Barcode` where parent = %s
""",
item.item_code,
as_dict=1,
barcodes = frappe.db.get_all(
"Item Barcode", filters={"parent": item.item_code}, fields="barcode"
)
for barcode in barcodes:
@@ -891,34 +888,36 @@ def get_item_price(args, item_code, ignore_party=False):
:param item_code: str, Item Doctype field item_code
"""
args["item_code"] = item_code
conditions = """where item_code=%(item_code)s
and price_list=%(price_list)s
and ifnull(uom, '') in ('', %(uom)s)"""
conditions += "and ifnull(batch_no, '') in ('', %(batch_no)s)"
ip = frappe.qb.DocType("Item Price")
query = (
frappe.qb.from_(ip)
.select(ip.name, ip.price_list_rate, ip.uom)
.where(
(ip.item_code == item_code)
& (ip.price_list == args.get("price_list"))
& (IfNull(ip.uom, "").isin(["", args.get("uom")]))
& (IfNull(ip.batch_no, "").isin(["", args.get("batch_no")]))
)
.orderby(ip.valid_from, order=frappe.qb.desc)
.orderby(IfNull(ip.batch_no, ""), order=frappe.qb.desc)
.orderby(ip.uom, order=frappe.qb.desc)
)
if not ignore_party:
if args.get("customer"):
conditions += " and customer=%(customer)s"
query = query.where(ip.customer == args.get("customer"))
elif args.get("supplier"):
conditions += " and supplier=%(supplier)s"
query = query.where(ip.supplier == args.get("supplier"))
else:
conditions += "and (customer is null or customer = '') and (supplier is null or supplier = '')"
query = query.where((IfNull(ip.customer, "") == "") & (IfNull(ip.supplier, "") == ""))
if args.get("transaction_date"):
conditions += """ and %(transaction_date)s between
ifnull(valid_from, '2000-01-01') and ifnull(valid_upto, '2500-12-31')"""
query = query.where(
(IfNull(ip.valid_from, "2000-01-01") <= args["transaction_date"])
& (IfNull(ip.valid_upto, "2500-12-31") >= args["transaction_date"])
)
return frappe.db.sql(
""" select name, price_list_rate, uom
from `tabItem Price` {conditions}
order by valid_from desc, ifnull(batch_no, '') desc, uom desc """.format(
conditions=conditions
),
args,
)
return query.run()
def get_price_list_rate_for(args, item_code):
@@ -1091,91 +1090,68 @@ def get_pos_profile(company, pos_profile=None, user=None):
if not user:
user = frappe.session["user"]
condition = "pfu.user = %(user)s AND pfu.default=1"
if user and company:
condition = "pfu.user = %(user)s AND pf.company = %(company)s AND pfu.default=1"
pf = frappe.qb.DocType("POS Profile")
pfu = frappe.qb.DocType("POS Profile User")
pos_profile = frappe.db.sql(
"""SELECT pf.*
FROM
`tabPOS Profile` pf LEFT JOIN `tabPOS Profile User` pfu
ON
pf.name = pfu.parent
WHERE
{cond} AND pf.disabled = 0
""".format(
cond=condition
),
{"user": user, "company": company},
as_dict=1,
query = (
frappe.qb.from_(pf)
.left_join(pfu)
.on(pf.name == pfu.parent)
.select(pf.star)
.where((pfu.user == user) & (pfu.default == 1))
)
if company:
query = query.where(pf.company == company)
pos_profile = query.run(as_dict=True)
if not pos_profile and company:
pos_profile = frappe.db.sql(
"""SELECT pf.*
FROM
`tabPOS Profile` pf LEFT JOIN `tabPOS Profile User` pfu
ON
pf.name = pfu.parent
WHERE
pf.company = %(company)s AND pf.disabled = 0
""",
{"company": company},
as_dict=1,
)
pos_profile = (
frappe.qb.from_(pf)
.left_join(pfu)
.on(pf.name == pfu.parent)
.select(pf.star)
.where((pf.company == company) & (pf.disabled == 0))
).run(as_dict=True)
return pos_profile and pos_profile[0] or None
def get_serial_nos_by_fifo(args, sales_order=None):
if frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo"):
return "\n".join(
frappe.db.sql_list(
"""select name from `tabSerial No`
where item_code=%(item_code)s and warehouse=%(warehouse)s and
sales_order=IF(%(sales_order)s IS NULL, sales_order, %(sales_order)s)
order by timestamp(purchase_date, purchase_time)
asc limit %(qty)s""",
{
"item_code": args.item_code,
"warehouse": args.warehouse,
"qty": abs(cint(args.stock_qty)),
"sales_order": sales_order,
},
)
sn = frappe.qb.DocType("Serial No")
query = (
frappe.qb.from_(sn)
.select(sn.name)
.where((sn.item_code == args.item_code) & (sn.warehouse == args.warehouse))
.orderby(CombineDatetime(sn.purchase_date, sn.purchase_time))
.limit(abs(cint(args.stock_qty)))
)
if sales_order:
query = query.where(sn.sales_order == sales_order)
if args.batch_no:
query = query.where(sn.batch_no == args.batch_no)
def get_serial_no_batchwise(args, sales_order=None):
if frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo"):
return "\n".join(
frappe.db.sql_list(
"""select name from `tabSerial No`
where item_code=%(item_code)s and warehouse=%(warehouse)s and
sales_order=IF(%(sales_order)s IS NULL, sales_order, %(sales_order)s)
and batch_no=IF(%(batch_no)s IS NULL, batch_no, %(batch_no)s) order
by timestamp(purchase_date, purchase_time) asc limit %(qty)s""",
{
"item_code": args.item_code,
"warehouse": args.warehouse,
"batch_no": args.batch_no,
"qty": abs(cint(args.stock_qty)),
"sales_order": sales_order,
},
)
)
serial_nos = query.run(as_list=True)
serial_nos = [s[0] for s in serial_nos]
return "\n".join(serial_nos)
@frappe.whitelist()
def get_conversion_factor(item_code, uom):
variant_of = frappe.db.get_value("Item", item_code, "variant_of", cache=True)
filters = {"parent": item_code, "uom": uom}
if variant_of:
filters["parent"] = ("in", (item_code, variant_of))
conversion_factor = frappe.db.get_value("UOM Conversion Detail", filters, "conversion_factor")
if not conversion_factor:
stock_uom = frappe.db.get_value("Item", item_code, "stock_uom")
conversion_factor = get_uom_conv_factor(uom, stock_uom)
return {"conversion_factor": conversion_factor or 1.0}
@@ -1217,12 +1193,16 @@ def get_bin_details(item_code, warehouse, company=None, include_child_warehouses
def get_company_total_stock(item_code, company):
return frappe.db.sql(
"""SELECT sum(actual_qty) from
(`tabBin` INNER JOIN `tabWarehouse` ON `tabBin`.warehouse = `tabWarehouse`.name)
WHERE `tabWarehouse`.company = %s and `tabBin`.item_code = %s""",
(company, item_code),
)[0][0]
bin = frappe.qb.DocType("Bin")
wh = frappe.qb.DocType("Warehouse")
return (
frappe.qb.from_(bin)
.inner_join(wh)
.on(bin.warehouse == wh.name)
.select(Sum(bin.actual_qty))
.where((wh.company == company) & (bin.item_code == item_code))
).run()[0][0]
@frappe.whitelist()
@@ -1231,6 +1211,7 @@ def get_serial_no_details(item_code, warehouse, stock_qty, serial_no):
{"item_code": item_code, "warehouse": warehouse, "stock_qty": stock_qty, "serial_no": serial_no}
)
serial_no = get_serial_no(args)
return {"serial_no": serial_no}
@@ -1250,6 +1231,7 @@ def get_bin_details_and_serial_nos(
bin_details_and_serial_nos.update(
get_serial_no_details(item_code, warehouse, stock_qty, serial_no)
)
return bin_details_and_serial_nos
@@ -1264,6 +1246,7 @@ def get_batch_qty_and_serial_no(batch_no, stock_qty, warehouse, item_code, has_s
)
serial_no = get_serial_no(args)
batch_qty_and_serial_no.update({"serial_no": serial_no})
return batch_qty_and_serial_no
@@ -1336,7 +1319,6 @@ def apply_price_list(args, as_doc=False):
def apply_price_list_on_item(args):
item_doc = frappe.db.get_value("Item", args.item_code, ["name", "variant_of"], as_dict=1)
item_details = get_price_list_rate(args, item_doc)
item_details.update(get_pricing_rule_for_item(args))
return item_details
@@ -1420,12 +1402,12 @@ def get_valuation_rate(item_code, company, warehouse=None):
) or {"valuation_rate": 0}
elif not item.get("is_stock_item"):
valuation_rate = frappe.db.sql(
"""select sum(base_net_amount) / sum(qty*conversion_factor)
from `tabPurchase Invoice Item`
where item_code = %s and docstatus=1""",
item_code,
)
pi_item = frappe.qb.DocType("Purchase Invoice Item")
valuation_rate = (
frappe.qb.from_(pi_item)
.select((Sum(pi_item.base_net_amount) / Sum(pi_item.qty * pi_item.conversion_factor)))
.where((pi_item.docstatus == 1) & (pi_item.item_code == item_code))
).run()
if valuation_rate:
return {"valuation_rate": valuation_rate[0][0] or 0.0}
@@ -1451,7 +1433,7 @@ def get_serial_no(args, serial_nos=None, sales_order=None):
if args.get("warehouse") and args.get("stock_qty") and args.get("item_code"):
has_serial_no = frappe.get_value("Item", {"item_code": args.item_code}, "has_serial_no")
if args.get("batch_no") and has_serial_no == 1:
return get_serial_no_batchwise(args, sales_order)
return get_serial_nos_by_fifo(args, sales_order)
elif has_serial_no == 1:
args = json.dumps(
{
@@ -1483,31 +1465,35 @@ def get_blanket_order_details(args):
args = frappe._dict(json.loads(args))
blanket_order_details = None
condition = ""
if args.item_code:
if args.customer and args.doctype == "Sales Order":
condition = " and bo.customer=%(customer)s"
elif args.supplier and args.doctype == "Purchase Order":
condition = " and bo.supplier=%(supplier)s"
if args.blanket_order:
condition += " and bo.name =%(blanket_order)s"
if args.transaction_date:
condition += " and bo.to_date>=%(transaction_date)s"
blanket_order_details = frappe.db.sql(
"""
select boi.rate as blanket_order_rate, bo.name as blanket_order
from `tabBlanket Order` bo, `tabBlanket Order Item` boi
where bo.company=%(company)s and boi.item_code=%(item_code)s
and bo.docstatus=1 and bo.name = boi.parent {0}
""".format(
condition
),
args,
as_dict=True,
if args.item_code:
bo = frappe.qb.DocType("Blanket Order")
bo_item = frappe.qb.DocType("Blanket Order Item")
query = (
frappe.qb.from_(bo)
.from_(bo_item)
.select(bo_item.rate.as_("blanket_order_rate"), bo.name.as_("blanket_order"))
.where(
(bo.company == args.company)
& (bo_item.item_code == args.item_code)
& (bo.docstatus == 1)
& (bo.name == bo_item.parent)
)
)
if args.customer and args.doctype == "Sales Order":
query = query.where(bo.customer == args.customer)
elif args.supplier and args.doctype == "Purchase Order":
query = query.where(bo.supplier == args.supplier)
if args.blanket_order:
query = query.where(bo.name == args.blanket_order)
if args.transaction_date:
query = query.where(bo.to_date >= args.transaction_date)
blanket_order_details = query.run(as_dict=True)
blanket_order_details = blanket_order_details[0] if blanket_order_details else ""
return blanket_order_details
@@ -1517,10 +1503,10 @@ def get_so_reservation_for_item(args):
if get_reserved_qty_for_so(args.get("against_sales_order"), args.get("item_code")):
reserved_so = args.get("against_sales_order")
elif args.get("against_sales_invoice"):
sales_order = frappe.db.sql(
"""select sales_order from `tabSales Invoice Item` where
parent=%s and item_code=%s""",
(args.get("against_sales_invoice"), args.get("item_code")),
sales_order = frappe.db.get_all(
"Sales Invoice Item",
filters={"parent": args.get("against_sales_invoice"), "item_code": args.get("item_code")},
fields="sales_order",
)
if sales_order and sales_order[0]:
if get_reserved_qty_for_so(sales_order[0][0], args.get("item_code")):
@@ -1532,13 +1518,14 @@ def get_so_reservation_for_item(args):
def get_reserved_qty_for_so(sales_order, item_code):
reserved_qty = frappe.db.sql(
"""select sum(qty) from `tabSales Order Item`
where parent=%s and item_code=%s and ensure_delivery_based_on_produced_serial_no=1
""",
(sales_order, item_code),
reserved_qty = frappe.db.get_value(
"Sales Order Item",
filters={
"parent": sales_order,
"item_code": item_code,
"ensure_delivery_based_on_produced_serial_no": 1,
},
fieldname="sum(qty)",
)
if reserved_qty and reserved_qty[0][0]:
return reserved_qty[0][0]
else:
return 0
return reserved_qty or 0

View File

@@ -9916,3 +9916,5 @@ Cost and Freight,Kosten und Fracht,
Delivered at Place,Geliefert benannter Ort,
Delivered at Place Unloaded,Geliefert benannter Ort entladen,
Delivered Duty Paid,Geliefert verzollt,
Discount Validity,Frist für den Rabatt,
Discount Validity Based On,Frist für den Rabatt berechnet sich nach,
Can't render this file because it is too large.