mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-30 20:18:27 +00:00
Merge pull request #32494 from frappe/version-13-hotfix
chore: release v13
This commit is contained in:
18
CODEOWNERS
18
CODEOWNERS
@@ -12,17 +12,13 @@ erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
|
||||
erpnext/support/ @nextchamp-saqib @deepeshgarg007
|
||||
pos* @nextchamp-saqib
|
||||
|
||||
erpnext/buying/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/e_commerce/ @marination
|
||||
erpnext/maintenance/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/manufacturing/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/portal/ @marination
|
||||
erpnext/quality_management/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/shopping_cart/ @marination
|
||||
erpnext/stock/ @marination @rohitwaghchaure @s-aga-r
|
||||
erpnext/buying/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
|
||||
erpnext/stock/ @rohitwaghchaure @s-aga-r
|
||||
|
||||
|
||||
erpnext/crm/ @NagariaHussain
|
||||
erpnext/education/ @rutwikhdev
|
||||
erpnext/healthcare/ @chillaranand
|
||||
erpnext/hr/ @ruchamahabal
|
||||
erpnext/non_profit/ @ruchamahabal
|
||||
@@ -30,7 +26,7 @@ erpnext/payroll @ruchamahabal
|
||||
erpnext/projects/ @ruchamahabal
|
||||
|
||||
erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
|
||||
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @marination rohitwaghchaure
|
||||
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure
|
||||
erpnext/public/ @nextchamp-saqib @marination
|
||||
|
||||
.github/ @ankush
|
||||
|
||||
@@ -357,7 +357,7 @@ class PaymentReconciliation(Document):
|
||||
def get_conditions(self, get_invoices=False, get_payments=False, get_return_invoices=False):
|
||||
condition = " and company = '{0}' ".format(self.company)
|
||||
|
||||
if self.get("cost_center") and (get_invoices or get_payments or get_return_invoices):
|
||||
if self.get("cost_center"):
|
||||
condition = " and cost_center = '{0}' ".format(self.cost_center)
|
||||
|
||||
if get_invoices:
|
||||
|
||||
@@ -186,8 +186,10 @@
|
||||
{
|
||||
"fetch_from": "bank_account.bank",
|
||||
"fieldname": "bank",
|
||||
"fieldtype": "Read Only",
|
||||
"label": "Bank"
|
||||
"fieldtype": "Link",
|
||||
"label": "Bank",
|
||||
"options": "Bank",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "bank_account.bank_account_no",
|
||||
@@ -366,10 +368,11 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-18 12:24:14.178853",
|
||||
"modified": "2022-09-30 16:19:43.680025",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Request",
|
||||
"naming_rule": "By \"Naming Series\" field",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -401,5 +404,6 @@
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import unittest
|
||||
import frappe
|
||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||
from frappe.model.naming import make_autoname
|
||||
from frappe.utils import add_days, flt, getdate, nowdate
|
||||
from frappe.utils import add_days, flt, getdate, nowdate, today
|
||||
from six import iteritems
|
||||
|
||||
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
|
||||
@@ -3396,6 +3396,37 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled
|
||||
)
|
||||
|
||||
def test_batch_expiry_for_sales_invoice_return(self):
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
item = make_item(
|
||||
"_Test Batch Item For Return Check",
|
||||
{
|
||||
"is_purchase_item": 1,
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "TBIRC.#####",
|
||||
},
|
||||
)
|
||||
|
||||
pr = make_purchase_receipt(qty=1, item_code=item.name)
|
||||
|
||||
batch_no = pr.items[0].batch_no
|
||||
si = create_sales_invoice(qty=1, item_code=item.name, update_stock=1, batch_no=batch_no)
|
||||
|
||||
si.load_from_db()
|
||||
batch_no = si.items[0].batch_no
|
||||
self.assertTrue(batch_no)
|
||||
|
||||
frappe.db.set_value("Batch", batch_no, "expiry_date", add_days(today(), -1))
|
||||
|
||||
return_si = make_return_doc(si.doctype, si.name)
|
||||
return_si.save().submit()
|
||||
|
||||
self.assertTrue(return_si.docstatus == 1)
|
||||
|
||||
|
||||
def get_sales_invoice_for_e_invoice():
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
@@ -3666,6 +3697,7 @@ def create_sales_invoice(**args):
|
||||
"serial_no": args.serial_no,
|
||||
"conversion_factor": 1,
|
||||
"incoming_rate": args.incoming_rate or 0,
|
||||
"batch_no": args.batch_no or None,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -326,6 +326,9 @@ def get_advance_vouchers(
|
||||
"party": ["in", parties],
|
||||
}
|
||||
|
||||
if party_type == "Customer":
|
||||
filters.update({"against_voucher": ["is", "not set"]})
|
||||
|
||||
if company:
|
||||
filters["company"] = company
|
||||
if from_date and to_date:
|
||||
|
||||
@@ -370,7 +370,7 @@ def get_conditions(filters):
|
||||
where parent=`tabSales Invoice`.name
|
||||
and ifnull(`tab{table}`.{field}, '') = %({field})s)"""
|
||||
|
||||
conditions += get_sales_invoice_item_field_condition("mode_of_payments", "Sales Invoice Payment")
|
||||
conditions += get_sales_invoice_item_field_condition("mode_of_payment", "Sales Invoice Payment")
|
||||
conditions += get_sales_invoice_item_field_condition("cost_center")
|
||||
conditions += get_sales_invoice_item_field_condition("warehouse")
|
||||
conditions += get_sales_invoice_item_field_condition("brand")
|
||||
|
||||
@@ -100,6 +100,7 @@ def execute():
|
||||
"mode_of_payment": loan.mode_of_payment,
|
||||
"loan_account": loan.loan_account,
|
||||
"payment_account": loan.payment_account,
|
||||
"disbursement_account": loan.payment_account,
|
||||
"interest_income_account": loan.interest_income_account,
|
||||
"penalty_income_account": loan.penalty_income_account,
|
||||
},
|
||||
@@ -190,6 +191,7 @@ def create_loan_type(loan, loan_type_name, penalty_account):
|
||||
loan_type_doc.company = loan.company
|
||||
loan_type_doc.mode_of_payment = loan.mode_of_payment
|
||||
loan_type_doc.payment_account = loan.payment_account
|
||||
loan_type_doc.disbursement_account = loan.payment_account
|
||||
loan_type_doc.loan_account = loan.loan_account
|
||||
loan_type_doc.interest_income_account = loan.interest_income_account
|
||||
loan_type_doc.penalty_income_account = penalty_account
|
||||
|
||||
@@ -257,9 +257,16 @@ def get_regional_address_details(party_details, doctype, company):
|
||||
|
||||
update_party_details(party_details, doctype)
|
||||
|
||||
customer_gst_category = frappe.get_value(
|
||||
"Customer", party_details.customer, ["gst_category", "export_type"]
|
||||
)
|
||||
|
||||
party_details.place_of_supply = get_place_of_supply(party_details, doctype)
|
||||
|
||||
if is_internal_transfer(party_details, doctype):
|
||||
if is_internal_transfer(party_details, doctype) or customer_gst_category == (
|
||||
"SEZ",
|
||||
"Without Payment of Tax",
|
||||
):
|
||||
party_details.taxes_and_charges = ""
|
||||
party_details.taxes = []
|
||||
return party_details
|
||||
@@ -603,6 +610,10 @@ def get_ewb_data(dt, dn):
|
||||
|
||||
data = get_address_details(data, doc, company_address, billing_address, dispatch_address)
|
||||
|
||||
if is_intrastate_transfer_eway_bill(data):
|
||||
data.docType = "CHL"
|
||||
data.subSupplyType = 8
|
||||
|
||||
data.itemList = []
|
||||
data.totalValue = doc.total
|
||||
|
||||
@@ -645,6 +656,10 @@ def get_ewb_data(dt, dn):
|
||||
return data
|
||||
|
||||
|
||||
def is_intrastate_transfer_eway_bill(data):
|
||||
return data.fromGstin == data.toGstin
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def generate_ewb_json(dt, dn):
|
||||
dn = json.loads(dn)
|
||||
|
||||
@@ -6,7 +6,7 @@ import json
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils import cstr, flt, nowdate, nowtime
|
||||
from frappe.utils import add_days, cstr, flt, nowdate, nowtime, today
|
||||
|
||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||
from erpnext.accounts.utils import get_balance_on
|
||||
@@ -1091,6 +1091,36 @@ class TestDeliveryNote(FrappeTestCase):
|
||||
frappe.db.exists("GL Entry", {"voucher_no": dn.name, "voucher_type": dn.doctype})
|
||||
)
|
||||
|
||||
def test_batch_expiry_for_delivery_note(self):
|
||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
|
||||
item = make_item(
|
||||
"_Test Batch Item For Return Check",
|
||||
{
|
||||
"is_purchase_item": 1,
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "TBIRC.#####",
|
||||
},
|
||||
)
|
||||
|
||||
pi = make_purchase_receipt(qty=1, item_code=item.name)
|
||||
|
||||
dn = create_delivery_note(qty=1, item_code=item.name, batch_no=pi.items[0].batch_no)
|
||||
|
||||
dn.load_from_db()
|
||||
batch_no = dn.items[0].batch_no
|
||||
self.assertTrue(batch_no)
|
||||
|
||||
frappe.db.set_value("Batch", batch_no, "expiry_date", add_days(today(), -1))
|
||||
|
||||
return_dn = make_return_doc(dn.doctype, dn.name)
|
||||
return_dn.save().submit()
|
||||
|
||||
self.assertTrue(return_dn.docstatus == 1)
|
||||
|
||||
|
||||
def create_delivery_note(**args):
|
||||
dn = frappe.new_doc("Delivery Note")
|
||||
@@ -1117,6 +1147,7 @@ def create_delivery_note(**args):
|
||||
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
|
||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||
"serial_no": args.serial_no,
|
||||
"batch_no": args.batch_no or None,
|
||||
"target_warehouse": args.target_warehouse,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -10,6 +10,31 @@ frappe.ui.form.on("Item", {
|
||||
frm.add_fetch('attribute', 'to_range', 'to_range');
|
||||
frm.add_fetch('attribute', 'increment', 'increment');
|
||||
frm.add_fetch('tax_type', 'tax_rate', 'tax_rate');
|
||||
|
||||
frm.make_methods = {
|
||||
'Sales Order': () => {
|
||||
open_form(frm, "Sales Order", "Sales Order Item", "items");
|
||||
},
|
||||
'Delivery Note': () => {
|
||||
open_form(frm, "Delivery Note", "Delivery Note Item", "items");
|
||||
},
|
||||
'Sales Invoice': () => {
|
||||
open_form(frm, "Sales Invoice", "Sales Invoice Item", "items");
|
||||
},
|
||||
'Purchase Order': () => {
|
||||
open_form(frm, "Purchase Order", "Purchase Order Item", "items");
|
||||
},
|
||||
'Purchase Receipt': () => {
|
||||
open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items");
|
||||
},
|
||||
'Purchase Invoice': () => {
|
||||
open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items");
|
||||
},
|
||||
'Material Request': () => {
|
||||
open_form(frm, "Material Request", "Material Request Item", "items");
|
||||
},
|
||||
};
|
||||
|
||||
},
|
||||
onload: function(frm) {
|
||||
erpnext.item.setup_queries(frm);
|
||||
@@ -813,3 +838,17 @@ frappe.ui.form.on("UOM Conversion Detail", {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function open_form(frm, doctype, child_doctype, parentfield) {
|
||||
frappe.model.with_doctype(doctype, () => {
|
||||
let new_doc = frappe.model.get_new_doc(doctype);
|
||||
|
||||
let new_child_doc = frappe.model.add_child(new_doc, child_doctype, parentfield);
|
||||
new_child_doc.item_code = frm.doc.name;
|
||||
new_child_doc.item_name = frm.doc.item_name;
|
||||
new_child_doc.uom = frm.doc.stock_uom;
|
||||
new_child_doc.description = frm.doc.description;
|
||||
|
||||
frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ class PickList(Document):
|
||||
frappe.throw("Row #{0}: Item Code is Mandatory".format(item.idx))
|
||||
item_code = item.item_code
|
||||
reference = item.sales_order_item or item.material_request_item
|
||||
key = (item_code, item.uom, item.warehouse, reference)
|
||||
key = (item_code, item.uom, item.warehouse, item.batch_no, reference)
|
||||
|
||||
item.idx = None
|
||||
item.name = None
|
||||
|
||||
@@ -153,7 +153,9 @@ class StockLedgerEntry(Document):
|
||||
|
||||
def validate_batch(self):
|
||||
if self.batch_no and self.voucher_type != "Stock Entry":
|
||||
if self.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and self.actual_qty < 0:
|
||||
if (self.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and self.actual_qty < 0) or (
|
||||
self.voucher_type in ["Delivery Note", "Sales Invoice"] and self.actual_qty > 0
|
||||
):
|
||||
return
|
||||
|
||||
expiry_date = frappe.db.get_value("Batch", self.batch_no, "expiry_date")
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<b class="caret"></b>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||
{% if doc.doctype == 'Purchase Order' %}
|
||||
{% if doc.doctype == 'Purchase Order' and show_make_pi_button %}
|
||||
<a class="dropdown-item" href="/api/method/erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice_from_portal?purchase_order_name={{ doc.name }}" data-action="make_purchase_invoice">{{ _("Make Purchase Invoice") }}</a>
|
||||
{% endif %}
|
||||
<a class="dropdown-item" href='/printview?doctype={{ doc.doctype}}&name={{ doc.name }}&format={{ print_format }}'
|
||||
|
||||
@@ -53,6 +53,9 @@ def get_context(context):
|
||||
)
|
||||
context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
|
||||
|
||||
# show Make Purchase Invoice button based on permission
|
||||
context.show_make_pi_button = frappe.has_permission("Purchase Invoice", "create")
|
||||
|
||||
|
||||
def get_attachments(dt, dn):
|
||||
return frappe.get_all(
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
.time-slot.selected {
|
||||
color: white;
|
||||
background: #5e64ff;
|
||||
background: var(--primary-color);
|
||||
}
|
||||
|
||||
.time-slot.selected .text-muted {
|
||||
|
||||
Reference in New Issue
Block a user