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

View File

@@ -495,26 +495,22 @@ def get_amount(ref_doc, payment_account=None):
"""get amount based on doctype""" """get amount based on doctype"""
dt = ref_doc.doctype dt = ref_doc.doctype
if dt in ["Sales Order", "Purchase Order"]: 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"]: elif dt in ["Sales Invoice", "Purchase Invoice"]:
if ref_doc.party_account_currency == ref_doc.currency: if ref_doc.party_account_currency == ref_doc.currency:
grand_total = flt(ref_doc.outstanding_amount) grand_total = flt(ref_doc.outstanding_amount)
else: else:
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
elif dt == "POS Invoice": elif dt == "POS Invoice":
for pay in ref_doc.payments: for pay in ref_doc.payments:
if pay.type == "Phone" and pay.account == payment_account: if pay.type == "Phone" and pay.account == payment_account:
grand_total = pay.amount grand_total = pay.amount
break break
elif dt == "Fees": elif dt == "Fees":
grand_total = ref_doc.outstanding_amount grand_total = ref_doc.outstanding_amount
if grand_total > 0: if grand_total > 0:
return grand_total return grand_total
else: else:
frappe.throw(_("Payment Entry is already created")) 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) frappe.get_doc(method).insert(ignore_permissions=True)
def test_payment_request_linkings(self): 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( pr = make_payment_request(
dt="Sales Order", dt="Sales Order",
dn=so_inr.name, dn=so_inr.name,

View File

@@ -1485,11 +1485,17 @@ class PurchaseInvoice(BuyingController):
if po_details: if po_details:
updated_pr += update_billed_amount_based_on_po(po_details, update_modified) 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): for pr in set(updated_pr):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
pr_doc = frappe.get_doc("Purchase Receipt", pr) 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): def get_pr_details_billed_amt(self):
# Get billed amount based on purchase receipt item reference (pr_detail) in purchase invoice # 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.enable_provisional_accounting_for_non_stock_items = 0
company.save() 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): def test_item_less_defaults(self):
pi = frappe.new_doc("Purchase Invoice") pi = frappe.new_doc("Purchase Invoice")

View File

@@ -38,8 +38,11 @@
{% if(data[i].posting_date) { %} {% if(data[i].posting_date) { %}
<td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td> <td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td>
<td>{%= data[i].voucher_type %} <td>{%= data[i].voucher_type %}
<br>{%= data[i].voucher_no %}</td> <br>{%= data[i].voucher_no %}
<td> </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)) { %} {% if(!(filters.party || filters.account)) { %}
{%= data[i].party || data[i].account %} {%= data[i].party || data[i].account %}
<br> <br>
@@ -49,11 +52,14 @@
{% if(data[i].bill_no) { %} {% if(data[i].bill_no) { %}
<br>{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %} <br>{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %}
{% } %} {% } %}
</td> </span>
<td style="text-align: right"> </td>
{%= format_currency(data[i].debit, filters.presentation_currency || data[i].account_currency) %}</td> <td style="text-align: right">
<td style="text-align: right"> {%= format_currency(data[i].debit, filters.presentation_currency) %}
{%= format_currency(data[i].credit, filters.presentation_currency || data[i].account_currency) %}</td> </td>
<td style="text-align: right">
{%= format_currency(data[i].credit, filters.presentation_currency) %}
</td>
{% } else { %} {% } else { %}
<td></td> <td></td>
<td></td> <td></td>

View File

@@ -18,6 +18,7 @@
"pr_required", "pr_required",
"column_break_12", "column_break_12",
"maintain_same_rate", "maintain_same_rate",
"set_landed_cost_based_on_purchase_invoice_rate",
"allow_multiple_items", "allow_multiple_items",
"bill_for_rejected_quantity_in_purchase_invoice", "bill_for_rejected_quantity_in_purchase_invoice",
"disable_last_purchase_rate", "disable_last_purchase_rate",
@@ -147,6 +148,14 @@
"fieldname": "show_pay_button", "fieldname": "show_pay_button",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Show Pay Button in Purchase Order Portal" "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", "icon": "fa fa-cog",
@@ -154,7 +163,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2023-02-15 14:42:10.200679", "modified": "2023-02-28 15:41:32.686805",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Buying Settings", "name": "Buying Settings",

View File

@@ -21,3 +21,10 @@ class BuyingSettings(Document):
self.get("supp_master_name") == "Naming Series", self.get("supp_master_name") == "Naming Series",
hide_name_field=False, 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 ) / qty_in_stock_uom
else: else:
item.valuation_rate = ( 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 ) / qty_in_stock_uom
else: else:
item.valuation_rate = 0.0 item.valuation_rate = 0.0

View File

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

View File

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

View File

@@ -212,7 +212,7 @@ def resume_bom_cost_update_jobs():
["name", "boms_updated", "status"], ["name", "boms_updated", "status"],
) )
incomplete_level = any(row.get("status") == "Pending" for row in bom_batches) 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 continue
# Prep parent BOMs & updated processed BOMs for next level # Prep parent BOMs & updated processed BOMs for next level
@@ -252,6 +252,9 @@ def get_processed_current_boms(
current_boms = [] current_boms = []
for row in bom_batches: for row in bom_batches:
if not row.boms_updated:
continue
boms_updated = json.loads(row.boms_updated) boms_updated = json.loads(row.boms_updated)
current_boms.extend(boms_updated) current_boms.extend(boms_updated)
boms_updated_dict = {bom: True for bom in 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]; var d = locals[cdt][cdn];
me.add_taxes_from_item_tax_template(d.item_tax_rate); 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); me.apply_product_discount(d);
} }
}, },
@@ -1884,11 +1884,13 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
get_advances() { get_advances() {
if(!this.frm.is_return) { if(!this.frm.is_return) {
var me = this;
return this.frm.call({ return this.frm.call({
method: "set_advances", method: "set_advances",
doc: this.frm.doc, doc: this.frm.doc,
callback: function(r, rt) { callback: function(r, rt) {
refresh_field("advances"); refresh_field("advances");
me.frm.dirty();
} }
}) })
} }

View File

@@ -253,7 +253,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran
} }
calculate_commission() { 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) { if(this.frm.doc.commission_rate > 100) {
this.frm.set_value("commission_rate", 100); this.frm.set_value("commission_rate", 100);

View File

@@ -293,6 +293,7 @@ class PurchaseReceipt(BuyingController):
get_purchase_document_details, get_purchase_document_details,
) )
stock_rbnb = None
if erpnext.is_perpetual_inventory_enabled(self.company): if erpnext.is_perpetual_inventory_enabled(self.company):
stock_rbnb = self.get_company_default("stock_received_but_not_billed") stock_rbnb = self.get_company_default("stock_received_but_not_billed")
landed_cost_entries = get_item_account_wise_additional_cost(self.name) landed_cost_entries = get_item_account_wise_additional_cost(self.name)
@@ -450,6 +451,21 @@ class PurchaseReceipt(BuyingController):
item=d, 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 # sub-contracting warehouse
if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse):
self.add_gl_entry( self.add_gl_entry(
@@ -470,6 +486,7 @@ class PurchaseReceipt(BuyingController):
+ flt(d.landed_cost_voucher_amount) + flt(d.landed_cost_voucher_amount)
+ flt(d.rm_supp_cost) + flt(d.rm_supp_cost)
+ flt(d.item_tax_amount) + flt(d.item_tax_amount)
+ flt(d.rate_difference_with_purchase_invoice)
) )
divisional_loss = flt( divisional_loss = flt(
@@ -765,7 +782,7 @@ class PurchaseReceipt(BuyingController):
updated_pr += update_billed_amount_based_on_po(po_details, update_modified) updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
for pr in set(updated_pr): 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) update_billing_percentage(pr_doc, update_modified=update_modified)
self.load_from_db() 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} 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 # Reload as billed amount was set in db directly
pr_doc.load_from_db() pr_doc.load_from_db()
@@ -897,6 +914,12 @@ def update_billing_percentage(pr_doc, update_modified=True):
total_amount += total_billable_amount total_amount += total_billable_amount
total_billed_amount += flt(item.billed_amt) 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) percent_billed = round(100 * (total_billed_amount / (total_amount or 1)), 6)
pr_doc.db_set("per_billed", percent_billed) 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.set_status(update=True)
pr_doc.notify_update() 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): def get_item_wise_returned_qty(pr_doc):
items = [d.name for d in pr_doc.items] items = [d.name for d in pr_doc.items]

View File

@@ -69,6 +69,7 @@
"item_tax_amount", "item_tax_amount",
"rm_supp_cost", "rm_supp_cost",
"landed_cost_voucher_amount", "landed_cost_voucher_amount",
"rate_difference_with_purchase_invoice",
"billed_amt", "billed_amt",
"warehouse_and_reference", "warehouse_and_reference",
"warehouse", "warehouse",
@@ -1007,12 +1008,20 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Has Item Scanned", "label": "Has Item Scanned",
"read_only": 1 "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, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2023-01-18 15:48:58.114923", "modified": "2023-02-28 15:43:04.470104",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt Item", "name": "Purchase Receipt Item",

View File

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

View File

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

View File

@@ -9916,3 +9916,5 @@ Cost and Freight,Kosten und Fracht,
Delivered at Place,Geliefert benannter Ort, Delivered at Place,Geliefert benannter Ort,
Delivered at Place Unloaded,Geliefert benannter Ort entladen, Delivered at Place Unloaded,Geliefert benannter Ort entladen,
Delivered Duty Paid,Geliefert verzollt, 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.