Compare commits

...

46 Commits

Author SHA1 Message Date
Frappe PR Bot
cfe28663bc chore(release): Bumped to Version 14.18.1
## [14.18.1](https://github.com/frappe/erpnext/compare/v14.18.0...v14.18.1) (2023-03-07)

### Performance Improvements

* `update_completed_qty()` in `material_request.py` ([7a5f7d4](7a5f7d4920))
* Stock Entry (Material Transfer) ([59a415e](59a415eaa9))
2023-03-07 17:23:00 +00:00
Sagar Sharma
943599f3ac Merge pull request #34342 from frappe/mergify/bp/version-14/pr-34335
perf: Stock Entry (Material Transfer) (backport #34313) (backport #34335)
2023-03-07 22:51:22 +05:30
s-aga-r
7a5f7d4920 perf: update_completed_qty() in material_request.py
(cherry picked from commit 8ad9e99cea)
(cherry picked from commit b37712c038)
2023-03-07 16:14:55 +00:00
s-aga-r
59a415eaa9 perf: Stock Entry (Material Transfer)
(cherry picked from commit de18f98c5c)
(cherry picked from commit 1b514632d2)
2023-03-07 16:14:55 +00:00
Frappe PR Bot
0696128acc chore(release): Bumped to Version 14.18.0
# [14.18.0](https://github.com/frappe/erpnext/compare/v14.17.4...v14.18.0) (2023-03-07)

### Bug Fixes

* `Inventory Dimension` for `Stock Reconciliation` ([b08cdc0](b08cdc00f2))
* `rejected_serial_no` not getting copied from PR to PR(Return) ([3db8258](3db82587eb))
* `Serial No is mandatory` even if the `qty` is `0` ([aa6b891](aa6b891ef0))
* BOM Update log not completed ([235ecca](235ecca9fa))
* consumed qty validation for subcontracting receipt ([7eccf43](7eccf431fd))
* Default sales team not getting set ([#34284](https://github.com/frappe/erpnext/issues/34284)) ([64c758d](64c758d0c0))
* Do not calculate commission post submit ([#34267](https://github.com/frappe/erpnext/issues/34267)) ([480797e](480797e856))
* labels name ([5e9f1df](5e9f1dfbb3))
* **minor:** Dirty the form after clicking on Get advances button in Invoices ([#34323](https://github.com/frappe/erpnext/issues/34323)) ([0e9f9c3](0e9f9c31a0))
* Payment Request against sales order with disabled rounded total ([#34281](https://github.com/frappe/erpnext/issues/34281)) ([ca59c69](ca59c699cd))
* Performance improvement when adding a new item ([#34195](https://github.com/frappe/erpnext/issues/34195)) ([71a281f](71a281fb11))
* Resolve conflicts ([f6469d8](f6469d8398))
* Stock Reconciliation `actual_qty` ([d97c1bf](d97c1bf0f4))
* update inventory dimensions before returning sle ([ab73742](ab737424c2))
* Wrap unexpectedly long text in remark ([b13bf1e](b13bf1ebc5))

### Features

* adjust purchase receipt valuation rate as per purchase invoice rate ([db033c6](db033c6862))

### Reverts

* Revert "refactor: use renamed timezone utils (#34301)" ([a2e001a](a2e001a2da)), closes [#34301](https://github.com/frappe/erpnext/issues/34301) [#34301](https://github.com/frappe/erpnext/issues/34301)
2023-03-07 14:26:16 +00:00
Deepesh Garg
1cf79f05c5 Merge pull request #34327 from frappe/version-14-hotfix
chore: release v14
2023-03-07 19:54:30 +05:30
Deepesh Garg
a2e001a2da Revert "refactor: use renamed timezone utils (#34301)"
Revert "refactor: use renamed timezone utils (#34301)"

This reverts commit 164933aae8.
2023-03-07 17:56:57 +05:30
mergify[bot]
164933aae8 refactor: use renamed timezone utils (#34301)
refactor: use renamed timezone utils

https://github.com/frappe/frappe/pull/20253
(cherry picked from commit 502a37a864)

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-07 16:52:56 +05:30
mergify[bot]
0e9f9c31a0 fix(minor): Dirty the form after clicking on Get advances button in Invoices (#34323)
fix(minor): Dirty the form after clicking on Get advances button in Invoices (#34323)

fix(minor): Dirty form after clicking on Get advances button

(cherry picked from commit 2feb27e399)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2023-03-07 15:47:54 +05:30
mergify[bot]
71a281fb11 fix: Performance improvement when adding a new item (#34195)
fix: Performance improvement when adding a new item

(cherry picked from commit 49af5ba434)

Co-authored-by: HarryPaulo <paulo_fabris@hotmail.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-03-07 15:46:18 +05:30
mergify[bot]
64c758d0c0 fix: Default sales team not getting set (#34284)
fix: Default sales team not getting set (#34284)

(cherry picked from commit 7d0199d743)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-07 15:45:43 +05:30
mergify[bot]
480797e856 fix: Do not calculate commission post submit (#34267)
fix: Do not calculate commission post submit (#34267)

* fix: Do not calculate commision post submit

* chore: Update condition to match server side logic

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit 10632d75b0)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-07 15:44:15 +05:30
mergify[bot]
ca59c699cd fix: Payment Request against sales order with disabled rounded total (#34281)
fix: Payment Request against sales order with disabled rounded total (#34281)

* fix: Payment Request against sales order with disabled rounded total

* chore: Do not consider advance amount

(cherry picked from commit ea8e23384d)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-07 13:34:25 +05:30
mergify[bot]
9b84e1e39c chore: add german translations (#34167)
chore: add german translations (#34167)

* chore: add german translations

* Apply suggestions from code review

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit bbb6a62a7d)

Co-authored-by: Patrick Eissler <77415730+PatrickDenis-stack@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-03-07 11:45:51 +05:30
rohitwaghchaure
cfb93b6c58 Merge pull request #34316 from frappe/mergify/bp/version-14-hotfix/pr-34305
fix: BOM Update log not completed (backport #34305)
2023-03-07 08:39:09 +05:30
Rohit Waghchaure
235ecca9fa fix: BOM Update log not completed
(cherry picked from commit 2f157fa5d3)
2023-03-06 17:54:50 +00:00
Sagar Sharma
c49be03d0f Merge pull request #34300 from frappe/mergify/bp/version-14-hotfix/pr-34299
fix: Stock Reconciliation `actual_qty` (backport #34299)
2023-03-04 18:00:33 +05:30
s-aga-r
d97c1bf0f4 fix: Stock Reconciliation actual_qty
(cherry picked from commit 70de444b7b)
2023-03-04 12:05:49 +00:00
Sagar Sharma
2f74427132 Merge pull request #34294 from frappe/mergify/bp/version-14-hotfix/pr-34293
fix: `Inventory Dimension` for `Stock Reconciliation` (backport #34293)
2023-03-04 16:49:59 +05:30
s-aga-r
ab737424c2 fix: update inventory dimensions before returning sle 2023-03-04 16:07:59 +05:30
s-aga-r
b08cdc00f2 fix: Inventory Dimension for Stock Reconciliation
(cherry picked from commit 0e1b7760a8)
2023-03-03 20:43:56 +00:00
Deepesh Garg
829bbdd5c5 Merge pull request #34280 from frappe/mergify/bp/version-14-hotfix/pr-34258
chore: Make finance book read only (backport #34258)
2023-03-03 11:09:13 +05:30
Deepesh Garg
1cdf7e0988 chore: Make finance book read only
(cherry picked from commit 28dd1a25cb)
2023-03-02 11:18:42 +00:00
Sagar Sharma
fa1b25d0f2 Merge pull request #34278 from frappe/mergify/bp/version-14-hotfix/pr-34117
refactor: rewrite `get_item_details.py` queries in `QB` (backport #34117)
2023-03-02 15:25:12 +05:30
s-aga-r
731dc4cdd9 chore: Linters
(cherry picked from commit 58c027d4cc)
2023-03-02 09:25:38 +00:00
s-aga-r
dea5290d81 refactor: remove method get_serial_no_batchwise from get_item_details.py
(cherry picked from commit 35489fbbf9)
2023-03-02 09:25:38 +00:00
s-aga-r
1e086db7c7 refactor: rewrite get_item_details.py queries in QB
(cherry picked from commit 6b144baa69)
2023-03-02 09:25:38 +00:00
Frappe PR Bot
a59c580480 chore(release): Bumped to Version 14.17.4
## [14.17.4](https://github.com/frappe/erpnext/compare/v14.17.3...v14.17.4) (2023-03-02)

### Bug Fixes

* `rejected_serial_no` not getting copied from PR to PR(Return) ([9930adc](9930adcd28))
* `Serial No is mandatory` even if the `qty` is `0` ([7629caa](7629caa647))
2023-03-02 08:08:44 +00:00
Sagar Sharma
ba1cfa992d Merge pull request #34277 from frappe/mergify/bp/version-14/pr-34275
fix: `rejected_serial_no` not getting copied from PR to PR(Return) (backport #34273) (backport #34275)
2023-03-02 13:34:32 +05:30
s-aga-r
7629caa647 fix: Serial No is mandatory even if the qty is 0
(cherry picked from commit cb0b6de4b9)
(cherry picked from commit aa6b891ef0)
2023-03-02 07:35:46 +00:00
s-aga-r
9930adcd28 fix: rejected_serial_no not getting copied from PR to PR(Return)
(cherry picked from commit a9f0a11ce6)
(cherry picked from commit 3db82587eb)
2023-03-02 07:35:45 +00:00
Sagar Sharma
ab8ea2371b Merge pull request #34275 from frappe/mergify/bp/version-14-hotfix/pr-34273
fix: `rejected_serial_no` not getting copied from PR to PR(Return) (backport #34273)
2023-03-02 13:04:51 +05:30
s-aga-r
aa6b891ef0 fix: Serial No is mandatory even if the qty is 0
(cherry picked from commit cb0b6de4b9)
2023-03-02 07:08:15 +00:00
s-aga-r
3db82587eb fix: rejected_serial_no not getting copied from PR to PR(Return)
(cherry picked from commit a9f0a11ce6)
2023-03-02 07:08:14 +00:00
Suraj Shetty
da150e1a3c Merge pull request #34265 from frappe/mergify/bp/version-14-hotfix/pr-34262
fix(General Ledger): Wrap unexpectedly long word  (backport #34262)
2023-03-01 16:27:57 +05:30
Suraj Shetty
f6469d8398 fix: Resolve conflicts 2023-03-01 16:25:25 +05:30
Suraj Shetty
b13bf1ebc5 fix: Wrap unexpectedly long text in remark
(cherry picked from commit ba66a6714c)

# Conflicts:
#	erpnext/accounts/report/general_ledger/general_ledger.html
2023-03-01 10:53:38 +00:00
Frappe PR Bot
60a1e10b11 chore(release): Bumped to Version 14.17.3
## [14.17.3](https://github.com/frappe/erpnext/compare/v14.17.2...v14.17.3) (2023-03-01)

### Bug Fixes

* consumed qty validation for subcontracting receipt ([6ba9750](6ba97504ed))
2023-03-01 10:22:45 +00:00
rohitwaghchaure
1b578483f4 Merge pull request #34261 from frappe/mergify/bp/version-14/pr-34260
fix: consumed qty validation for subcontracting receipt (backport #34254) (backport #34260)
2023-03-01 15:41:54 +05:30
Rohit Waghchaure
6ba97504ed fix: consumed qty validation for subcontracting receipt
(cherry picked from commit b38fe24090)
(cherry picked from commit 7eccf431fd)
2023-03-01 10:10:02 +00:00
rohitwaghchaure
5e51ba2342 Merge pull request #34260 from frappe/mergify/bp/version-14-hotfix/pr-34254
fix: consumed qty validation for subcontracting receipt (backport #34254)
2023-03-01 15:39:29 +05:30
Rohit Waghchaure
7eccf431fd fix: consumed qty validation for subcontracting receipt
(cherry picked from commit b38fe24090)
2023-03-01 09:46:22 +00:00
rohitwaghchaure
0d6a2aed3e Merge pull request #34242 from frappe/mergify/bp/version-14-hotfix/pr-34235
feat: adjust purchase receipt valuation rate as per purchase invoice rate (backport #34235)
2023-02-28 22:11:21 +05:30
Rohit Waghchaure
5e9f1dfbb3 fix: labels name
(cherry picked from commit a8445da02a)
2023-02-28 12:05:58 +00:00
Rohit Waghchaure
3ea1c73c07 test: added test cases
(cherry picked from commit 8e86553717)
2023-02-28 12:05:57 +00:00
Rohit Waghchaure
db033c6862 feat: adjust purchase receipt valuation rate as per purchase invoice rate
(cherry picked from commit eab775ef32)
2023-02-28 12:05:56 +00:00
24 changed files with 431 additions and 209 deletions

View File

@@ -2,7 +2,7 @@ import inspect
import frappe
__version__ = "14.17.2"
__version__ = "14.18.1"
def get_default_company(user=None):

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

@@ -131,7 +131,7 @@ def validate_returned_items(doc):
)
elif ref.serial_no:
if not d.serial_no:
if d.qty and not d.serial_no:
frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
else:
serial_nos = get_serial_nos(d.serial_no)
@@ -400,6 +400,16 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
if serial_nos:
target_doc.serial_no = "\n".join(serial_nos)
if source_doc.get("rejected_serial_no"):
returned_serial_nos = get_returned_serial_nos(
source_doc, source_parent, serial_no_field="rejected_serial_no"
)
rejected_serial_nos = list(
set(get_serial_nos(source_doc.rejected_serial_no)) - set(returned_serial_nos)
)
if rejected_serial_nos:
target_doc.rejected_serial_no = "\n".join(rejected_serial_nos)
if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
returned_qty_map = get_returned_qty_map_for_row(
source_parent.name, source_parent.supplier, source_doc.name, doctype
@@ -610,7 +620,7 @@ def get_filters(
return filters
def get_returned_serial_nos(child_doc, parent_doc):
def get_returned_serial_nos(child_doc, parent_doc, serial_no_field="serial_no"):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
return_ref_field = frappe.scrub(child_doc.doctype)
@@ -619,7 +629,7 @@ def get_returned_serial_nos(child_doc, parent_doc):
serial_nos = []
fields = ["`{0}`.`serial_no`".format("tab" + child_doc.doctype)]
fields = [f"`{'tab' + child_doc.doctype}`.`{serial_no_field}`"]
filters = [
[parent_doc.doctype, "return_against", "=", parent_doc.name],
@@ -629,6 +639,6 @@ def get_returned_serial_nos(child_doc, parent_doc):
]
for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters):
serial_nos.extend(get_serial_nos(row.serial_no))
serial_nos.extend(get_serial_nos(row.get(serial_no_field)))
return serial_nos

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

@@ -561,7 +561,34 @@ class JobCard(Document):
)
def set_transferred_qty_in_job_card_item(self, ste_doc):
from frappe.query_builder.functions import Sum
def _get_job_card_items_transferred_qty(ste_doc):
from frappe.query_builder.functions import Sum
job_card_items_transferred_qty = {}
job_card_items = [
x.get("job_card_item") for x in ste_doc.get("items") if x.get("job_card_item")
]
if job_card_items:
se = frappe.qb.DocType("Stock Entry")
sed = frappe.qb.DocType("Stock Entry Detail")
query = (
frappe.qb.from_(sed)
.join(se)
.on(sed.parent == se.name)
.select(sed.job_card_item, Sum(sed.qty))
.where(
(sed.job_card_item.isin(job_card_items))
& (se.docstatus == 1)
& (se.purpose == "Material Transfer for Manufacture")
)
.groupby(sed.job_card_item)
)
job_card_items_transferred_qty = frappe._dict(query.run(as_list=True))
return job_card_items_transferred_qty
def _validate_over_transfer(row, transferred_qty):
"Block over transfer of items if not allowed in settings."
@@ -578,29 +605,23 @@ class JobCard(Document):
exc=JobCardOverTransferError,
)
for row in ste_doc.items:
if not row.job_card_item:
continue
sed = frappe.qb.DocType("Stock Entry Detail")
se = frappe.qb.DocType("Stock Entry")
transferred_qty = (
frappe.qb.from_(sed)
.join(se)
.on(sed.parent == se.name)
.select(Sum(sed.qty))
.where(
(sed.job_card_item == row.job_card_item)
& (se.docstatus == 1)
& (se.purpose == "Material Transfer for Manufacture")
)
).run()[0][0]
job_card_items_transferred_qty = _get_job_card_items_transferred_qty(ste_doc)
if job_card_items_transferred_qty:
allow_excess = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer")
if not allow_excess:
_validate_over_transfer(row, transferred_qty)
frappe.db.set_value("Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty))
for row in ste_doc.items:
if not row.job_card_item:
continue
transferred_qty = flt(job_card_items_transferred_qty.get(row.job_card_item))
if not allow_excess:
_validate_over_transfer(row, transferred_qty)
frappe.db.set_value(
"Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty)
)
def set_transferred_qty(self, update_status=False):
"Set total FG Qty in Job Card for which RM was transferred."

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

@@ -10,6 +10,7 @@ import json
import frappe
from frappe import _, msgprint
from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import Sum
from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, new_line_sep, nowdate
from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items
@@ -183,6 +184,34 @@ class MaterialRequest(BuyingController):
self.update_requested_qty()
self.update_requested_qty_in_production_plan()
def get_mr_items_ordered_qty(self, mr_items):
mr_items_ordered_qty = {}
mr_items = [d.name for d in self.get("items") if d.name in mr_items]
doctype = qty_field = None
if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"):
doctype = frappe.qb.DocType("Stock Entry Detail")
qty_field = doctype.transfer_qty
elif self.material_request_type == "Manufacture":
doctype = frappe.qb.DocType("Work Order")
qty_field = doctype.qty
if doctype and qty_field:
query = (
frappe.qb.from_(doctype)
.select(doctype.material_request_item, Sum(qty_field))
.where(
(doctype.material_request == self.name)
& (doctype.material_request_item.isin(mr_items))
& (doctype.docstatus == 1)
)
.groupby(doctype.material_request_item)
)
mr_items_ordered_qty = frappe._dict(query.run())
return mr_items_ordered_qty
def update_completed_qty(self, mr_items=None, update_modified=True):
if self.material_request_type == "Purchase":
return
@@ -190,18 +219,13 @@ class MaterialRequest(BuyingController):
if not mr_items:
mr_items = [d.name for d in self.get("items")]
mr_items_ordered_qty = self.get_mr_items_ordered_qty(mr_items)
mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance")
for d in self.get("items"):
if d.name in mr_items:
if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"):
d.ordered_qty = flt(
frappe.db.sql(
"""select sum(transfer_qty)
from `tabStock Entry Detail` where material_request = %s
and material_request_item = %s and docstatus = 1""",
(self.name, d.name),
)[0][0]
)
mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance")
d.ordered_qty = flt(mr_items_ordered_qty.get(d.name))
if mr_qty_allowance:
allowed_qty = d.qty + (d.qty * (mr_qty_allowance / 100))
@@ -220,14 +244,7 @@ class MaterialRequest(BuyingController):
)
elif self.material_request_type == "Manufacture":
d.ordered_qty = flt(
frappe.db.sql(
"""select sum(qty)
from `tabWork Order` where material_request = %s
and material_request_item = %s and docstatus = 1""",
(self.name, d.name),
)[0][0]
)
d.ordered_qty = flt(mr_items_ordered_qty.get(d.name))
frappe.db.set_value(d.doctype, d.name, "ordered_qty", d.ordered_qty)

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

@@ -191,14 +191,17 @@ class SubcontractingReceipt(SubcontractingController):
def validate_available_qty_for_consumption(self):
for item in self.get("supplied_items"):
precision = item.precision("consumed_qty")
if (
item.available_qty_for_consumption and item.available_qty_for_consumption < item.consumed_qty
item.available_qty_for_consumption
and flt(item.available_qty_for_consumption, precision) - flt(item.consumed_qty, precision) < 0
):
frappe.throw(
_(
"Row {0}: Consumed Qty must be less than or equal to Available Qty For Consumption in Consumed Items Table."
).format(item.idx)
)
msg = f"""Row {item.idx}: Consumed Qty {flt(item.consumed_qty, precision)}
must be less than or equal to Available Qty For Consumption
{flt(item.available_qty_for_consumption, precision)}
in Consumed Items Table."""
frappe.throw(_(msg))
def validate_items_qty(self):
for item in self.items:

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.