Merge pull request #37544 from frappe/version-13-hotfix

chore: release v13
This commit is contained in:
Deepesh Garg
2023-10-19 17:04:53 +05:30
committed by GitHub
13 changed files with 197 additions and 98 deletions

View File

@@ -4,12 +4,7 @@
import frappe import frappe
from frappe import _, msgprint, scrub from frappe import _, msgprint, scrub
from frappe.contacts.doctype.address.address import ( from frappe.contacts.doctype.address.address import get_company_address, get_default_address
get_address_display,
get_company_address,
get_default_address,
)
from frappe.contacts.doctype.contact.contact import get_contact_details
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.utils import get_fetch_values from frappe.model.utils import get_fetch_values
from frappe.utils import ( from frappe.utils import (
@@ -120,6 +115,7 @@ def _get_party_details(
party_address, party_address,
company_address, company_address,
shipping_address, shipping_address,
ignore_permissions=ignore_permissions,
) )
set_contact_details(party_details, party, party_type) set_contact_details(party_details, party, party_type)
set_other_values(party_details, party, party_type) set_other_values(party_details, party, party_type)
@@ -183,6 +179,8 @@ def set_address_details(
party_address=None, party_address=None,
company_address=None, company_address=None,
shipping_address=None, shipping_address=None,
*,
ignore_permissions=False
): ):
billing_address_field = ( billing_address_field = (
"customer_address" if party_type == "Lead" else party_type.lower() + "_address" "customer_address" if party_type == "Lead" else party_type.lower() + "_address"
@@ -195,13 +193,17 @@ def set_address_details(
get_fetch_values(doctype, billing_address_field, party_details[billing_address_field]) get_fetch_values(doctype, billing_address_field, party_details[billing_address_field])
) )
# address display # address display
party_details.address_display = get_address_display(party_details[billing_address_field]) party_details.address_display = render_address(
party_details[billing_address_field], check_permissions=not ignore_permissions
)
# shipping address # shipping address
if party_type in ["Customer", "Lead"]: if party_type in ["Customer", "Lead"]:
party_details.shipping_address_name = shipping_address or get_party_shipping_address( party_details.shipping_address_name = shipping_address or get_party_shipping_address(
party_type, party.name party_type, party.name
) )
party_details.shipping_address = get_address_display(party_details["shipping_address_name"]) party_details.shipping_address = render_address(
party_details["shipping_address_name"], check_permissions=not ignore_permissions
)
if doctype: if doctype:
party_details.update( party_details.update(
get_fetch_values(doctype, "shipping_address_name", party_details.shipping_address_name) get_fetch_values(doctype, "shipping_address_name", party_details.shipping_address_name)
@@ -224,7 +226,7 @@ def set_address_details(
party_details.update( party_details.update(
{ {
"shipping_address": shipping_address, "shipping_address": shipping_address,
"shipping_address_display": get_address_display(shipping_address), "shipping_address_display": render_address(shipping_address),
**get_fetch_values(doctype, "shipping_address", shipping_address), **get_fetch_values(doctype, "shipping_address", shipping_address),
} }
) )
@@ -235,7 +237,8 @@ def set_address_details(
{ {
"billing_address": party_details.company_address, "billing_address": party_details.company_address,
"billing_address_display": ( "billing_address_display": (
party_details.company_address_display or get_address_display(party_details.company_address) party_details.company_address_display
or render_address(party_details.company_address, check_permissions=True)
), ),
**get_fetch_values(doctype, "billing_address", party_details.company_address), **get_fetch_values(doctype, "billing_address", party_details.company_address),
} }
@@ -277,7 +280,34 @@ def set_contact_details(party_details, party, party_type):
} }
) )
else: else:
party_details.update(get_contact_details(party_details.contact_person)) fields = [
"name as contact_person",
"salutation",
"first_name",
"last_name",
"email_id as contact_email",
"mobile_no as contact_mobile",
"phone as contact_phone",
"designation as contact_designation",
"department as contact_department",
]
contact_details = frappe.db.get_value(
"Contact", party_details.contact_person, fields, as_dict=True
)
contact_details.contact_display = " ".join(
filter(
None,
[
contact_details.get("salutation"),
contact_details.get("first_name"),
contact_details.get("last_name"),
],
)
)
party_details.update(contact_details)
def set_other_values(party_details, party, party_type): def set_other_values(party_details, party, party_type):
@@ -938,3 +968,13 @@ def add_party_account(party_type, party, company, account):
doc.append("accounts", accounts) doc.append("accounts", accounts)
doc.save() doc.save()
def render_address(address, check_permissions=True):
try:
from frappe.contacts.doctype.address.address import render_address as _render
except ImportError:
# Older frappe versions where this function is not available
from frappe.contacts.doctype.address.address import get_address_display as _render
return frappe.call(_render, address, check_permissions=check_permissions)

View File

@@ -457,6 +457,8 @@ class GrossProfitGenerator(object):
new_row.qty += flt(row.qty) new_row.qty += flt(row.qty)
new_row.buying_amount += flt(row.buying_amount, self.currency_precision) new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
new_row.base_amount += flt(row.base_amount, self.currency_precision) new_row.base_amount += flt(row.base_amount, self.currency_precision)
if self.filters.get("group_by") == "Sales Person":
new_row.allocated_amount += flt(row.allocated_amount, self.currency_precision)
new_row = self.set_average_rate(new_row) new_row = self.set_average_rate(new_row)
self.grouped_data.append(new_row) self.grouped_data.append(new_row)
else: else:

View File

@@ -4,9 +4,9 @@
import frappe import frappe
from frappe import _, bold, throw from frappe import _, bold, throw
from frappe.contacts.doctype.address.address import get_address_display
from frappe.utils import cint, cstr, flt, get_link_to_form, nowtime from frappe.utils import cint, cstr, flt, get_link_to_form, nowtime
from erpnext.accounts.party import render_address
from erpnext.controllers.accounts_controller import get_taxes_and_charges from erpnext.controllers.accounts_controller import get_taxes_and_charges
from erpnext.controllers.sales_and_purchase_return import get_rate_for_return from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
from erpnext.controllers.stock_controller import StockController from erpnext.controllers.stock_controller import StockController
@@ -583,7 +583,9 @@ class SellingController(StockController):
for address_field, address_display_field in address_dict.items(): for address_field, address_display_field in address_dict.items():
if self.get(address_field): if self.get(address_field):
self.set(address_display_field, get_address_display(self.get(address_field))) self.set(
address_display_field, render_address(self.get(address_field), check_permissions=False)
)
def validate_for_duplicate_items(self): def validate_for_duplicate_items(self):
check_list, chk_dupl_itm = [], [] check_list, chk_dupl_itm = [], []

View File

@@ -4,6 +4,7 @@
import frappe import frappe
from frappe import _ from frappe import _
from frappe.query_builder.functions import Concat_ws, Date
def execute(filters=None): def execute(filters=None):
@@ -69,53 +70,41 @@ def get_columns():
def get_data(filters): def get_data(filters):
return frappe.db.sql( lead = frappe.qb.DocType("Lead")
""" address = frappe.qb.DocType("Address")
SELECT dynamic_link = frappe.qb.DocType("Dynamic Link")
`tabLead`.name,
`tabLead`.lead_name, query = (
`tabLead`.status, frappe.qb.from_(lead)
`tabLead`.lead_owner, .left_join(dynamic_link)
`tabLead`.territory, .on((lead.name == dynamic_link.link_name) & (dynamic_link.parenttype == "Address"))
`tabLead`.source, .left_join(address)
`tabLead`.email_id, .on(address.name == dynamic_link.parent)
`tabLead`.mobile_no, .select(
`tabLead`.phone, lead.name,
`tabLead`.owner, lead.lead_name,
`tabLead`.company, lead.status,
concat_ws(', ', lead.lead_owner,
trim(',' from `tabAddress`.address_line1), lead.territory,
trim(',' from tabAddress.address_line2) lead.source,
) AS address, lead.email_id,
`tabAddress`.state, lead.mobile_no,
`tabAddress`.pincode, lead.phone,
`tabAddress`.country lead.owner,
FROM lead.company,
`tabLead` left join `tabDynamic Link` on ( (Concat_ws(", ", address.address_line1, address.address_line2)).as_("address"),
`tabLead`.name = `tabDynamic Link`.link_name and address.state,
`tabDynamic Link`.parenttype = 'Address') address.pincode,
left join `tabAddress` on ( address.country,
`tabAddress`.name=`tabDynamic Link`.parent) )
WHERE .where(lead.company == filters.company)
company = %(company)s .where(Date(lead.creation).between(filters.from_date, filters.to_date))
AND DATE(`tabLead`.creation) BETWEEN %(from_date)s AND %(to_date)s
{conditions}
ORDER BY
`tabLead`.creation asc """.format(
conditions=get_conditions(filters)
),
filters,
as_dict=1,
) )
def get_conditions(filters):
conditions = []
if filters.get("territory"): if filters.get("territory"):
conditions.append(" and `tabLead`.territory=%(territory)s") query = query.where(lead.territory == filters.get("territory"))
if filters.get("status"): if filters.get("status"):
conditions.append(" and `tabLead`.status=%(status)s") query = query.where(lead.status == filters.get("status"))
return " ".join(conditions) if conditions else "" return query.run(as_dict=1)

View File

@@ -17,7 +17,6 @@ from erpnext.e_commerce.shopping_cart.cart import (
request_for_quotation, request_for_quotation,
update_cart, update_cart,
) )
from erpnext.tests.utils import create_test_contact_and_address
class TestShoppingCart(unittest.TestCase): class TestShoppingCart(unittest.TestCase):
@@ -28,7 +27,6 @@ class TestShoppingCart(unittest.TestCase):
def setUp(self): def setUp(self):
frappe.set_user("Administrator") frappe.set_user("Administrator")
create_test_contact_and_address()
self.enable_shopping_cart() self.enable_shopping_cart()
if not frappe.db.exists("Website Item", {"item_code": "_Test Item"}): if not frappe.db.exists("Website Item", {"item_code": "_Test Item"}):
make_website_item(frappe.get_cached_doc("Item", "_Test Item")) make_website_item(frappe.get_cached_doc("Item", "_Test Item"))
@@ -46,48 +44,57 @@ class TestShoppingCart(unittest.TestCase):
frappe.db.sql("delete from `tabTax Rule`") frappe.db.sql("delete from `tabTax Rule`")
def test_get_cart_new_user(self): def test_get_cart_new_user(self):
self.login_as_new_user() self.login_as_customer(
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
)
create_address_and_contact(
address_title="_Test Address for Customer 2",
first_name="_Test Contact for Customer 2",
email="test_contact_two_customer@example.com",
customer="_Test Customer 2",
)
# test if lead is created and quotation with new lead is fetched # test if lead is created and quotation with new lead is fetched
quotation = _get_cart_quotation() customer = frappe.get_doc("Customer", "_Test Customer 2")
quotation = _get_cart_quotation(party=customer)
self.assertEqual(quotation.quotation_to, "Customer") self.assertEqual(quotation.quotation_to, "Customer")
self.assertEqual( self.assertEqual(
quotation.contact_person, quotation.contact_person,
frappe.db.get_value("Contact", dict(email_id="test_cart_user@example.com")), frappe.db.get_value("Contact", dict(email_id="test_contact_two_customer@example.com")),
) )
self.assertEqual(quotation.contact_email, frappe.session.user) self.assertEqual(quotation.contact_email, frappe.session.user)
return quotation return quotation
def test_get_cart_customer(self): def test_get_cart_customer(self, customer="_Test Customer 2"):
def validate_quotation(): def validate_quotation(customer_name):
# test if quotation with customer is fetched # test if quotation with customer is fetched
quotation = _get_cart_quotation() party = frappe.get_doc("Customer", customer_name)
quotation = _get_cart_quotation(party=party)
self.assertEqual(quotation.quotation_to, "Customer") self.assertEqual(quotation.quotation_to, "Customer")
self.assertEqual(quotation.party_name, "_Test Customer") self.assertEqual(quotation.party_name, customer_name)
self.assertEqual(quotation.contact_email, frappe.session.user) self.assertEqual(quotation.contact_email, frappe.session.user)
return quotation return quotation
self.login_as_customer( quotation = validate_quotation(customer)
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
)
validate_quotation()
self.login_as_customer()
quotation = validate_quotation()
return quotation return quotation
def test_add_to_cart(self): def test_add_to_cart(self):
self.login_as_customer() self.login_as_customer(
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
)
create_address_and_contact(
address_title="_Test Address for Customer 2",
first_name="_Test Contact for Customer 2",
email="test_contact_two_customer@example.com",
customer="_Test Customer 2",
)
# clear existing quotations # clear existing quotations
self.clear_existing_quotations() self.clear_existing_quotations()
# add first item # add first item
update_cart("_Test Item", 1) update_cart("_Test Item", 1)
quotation = self.test_get_cart_customer() quotation = self.test_get_cart_customer("_Test Customer 2")
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item") self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
self.assertEqual(quotation.get("items")[0].qty, 1) self.assertEqual(quotation.get("items")[0].qty, 1)
@@ -95,7 +102,7 @@ class TestShoppingCart(unittest.TestCase):
# add second item # add second item
update_cart("_Test Item 2", 1) update_cart("_Test Item 2", 1)
quotation = self.test_get_cart_customer() quotation = self.test_get_cart_customer("_Test Customer 2")
self.assertEqual(quotation.get("items")[1].item_code, "_Test Item 2") self.assertEqual(quotation.get("items")[1].item_code, "_Test Item 2")
self.assertEqual(quotation.get("items")[1].qty, 1) self.assertEqual(quotation.get("items")[1].qty, 1)
self.assertEqual(quotation.get("items")[1].amount, 20) self.assertEqual(quotation.get("items")[1].amount, 20)
@@ -108,7 +115,7 @@ class TestShoppingCart(unittest.TestCase):
# update first item # update first item
update_cart("_Test Item", 5) update_cart("_Test Item", 5)
quotation = self.test_get_cart_customer() quotation = self.test_get_cart_customer("_Test Customer 2")
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item") self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
self.assertEqual(quotation.get("items")[0].qty, 5) self.assertEqual(quotation.get("items")[0].qty, 5)
self.assertEqual(quotation.get("items")[0].amount, 50) self.assertEqual(quotation.get("items")[0].amount, 50)
@@ -121,7 +128,7 @@ class TestShoppingCart(unittest.TestCase):
# remove first item # remove first item
update_cart("_Test Item", 0) update_cart("_Test Item", 0)
quotation = self.test_get_cart_customer() quotation = self.test_get_cart_customer("_Test Customer 2")
self.assertEqual(quotation.get("items")[0].item_code, "_Test Item 2") self.assertEqual(quotation.get("items")[0].item_code, "_Test Item 2")
self.assertEqual(quotation.get("items")[0].qty, 1) self.assertEqual(quotation.get("items")[0].qty, 1)
@@ -129,9 +136,20 @@ class TestShoppingCart(unittest.TestCase):
self.assertEqual(quotation.net_total, 20) self.assertEqual(quotation.net_total, 20)
self.assertEqual(len(quotation.get("items")), 1) self.assertEqual(len(quotation.get("items")), 1)
@unittest.skip("Flaky in CI")
def test_tax_rule(self): def test_tax_rule(self):
self.create_tax_rule() self.create_tax_rule()
self.login_as_customer()
self.login_as_customer(
"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
)
create_address_and_contact(
address_title="_Test Address for Customer 2",
first_name="_Test Contact for Customer 2",
email="test_contact_two_customer@example.com",
customer="_Test Customer 2",
)
quotation = self.create_quotation() quotation = self.create_quotation()
from erpnext.accounts.party import set_taxes from erpnext.accounts.party import set_taxes
@@ -319,7 +337,7 @@ class TestShoppingCart(unittest.TestCase):
if frappe.db.exists("User", email): if frappe.db.exists("User", email):
return return
frappe.get_doc( user = frappe.get_doc(
{ {
"doctype": "User", "doctype": "User",
"user_type": "Website User", "user_type": "Website User",
@@ -329,6 +347,40 @@ class TestShoppingCart(unittest.TestCase):
} }
).insert(ignore_permissions=True) ).insert(ignore_permissions=True)
user.add_roles("Customer")
def create_address_and_contact(**kwargs):
if not frappe.db.get_value("Address", {"address_title": kwargs.get("address_title")}):
frappe.get_doc(
{
"doctype": "Address",
"address_title": kwargs.get("address_title"),
"address_type": kwargs.get("address_type") or "Office",
"address_line1": kwargs.get("address_line1") or "Station Road",
"city": kwargs.get("city") or "_Test City",
"state": kwargs.get("state") or "Test State",
"country": kwargs.get("country") or "India",
"links": [
{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
],
}
).insert()
if not frappe.db.get_value("Contact", {"first_name": kwargs.get("first_name")}):
contact = frappe.get_doc(
{
"doctype": "Contact",
"first_name": kwargs.get("first_name"),
"links": [
{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
],
}
)
contact.add_email(kwargs.get("email") or "test_contact_customer@example.com", is_primary=True)
contact.add_phone(kwargs.get("phone") or "+91 0000000000", is_primary_phone=True)
contact.insert()
test_dependencies = [ test_dependencies = [
"Sales Taxes and Charges Template", "Sales Taxes and Charges Template",

View File

@@ -378,3 +378,4 @@ erpnext.patches.v13_0.update_schedule_type_in_loans
erpnext.patches.v13_0.update_asset_value_for_manual_depr_entries erpnext.patches.v13_0.update_asset_value_for_manual_depr_entries
erpnext.patches.v13_0.update_docs_link erpnext.patches.v13_0.update_docs_link
erpnext.patches.v13_0.correct_asset_value_if_je_with_workflow erpnext.patches.v13_0.correct_asset_value_if_je_with_workflow
execute:frappe.db.set_value("Accounts Settings", "Accounts Settings", "service_provider", "frankfurter.app")

View File

@@ -33,6 +33,7 @@ def after_install():
add_standard_navbar_items() add_standard_navbar_items()
add_app_name() add_app_name()
add_non_standard_user_types() add_non_standard_user_types()
update_roles()
frappe.db.commit() frappe.db.commit()
@@ -237,6 +238,12 @@ def create_custom_role(data):
).insert(ignore_permissions=True) ).insert(ignore_permissions=True)
def update_roles():
website_user_roles = ("Customer", "Supplier")
for role in website_user_roles:
frappe.db.set_value("Role", role, "desk_access", 0)
def create_user_type(user_type, data): def create_user_type(user_type, data):
if frappe.db.exists("User Type", user_type): if frappe.db.exists("User Type", user_type):
doc = frappe.get_cached_doc("User Type", user_type) doc = frappe.get_cached_doc("User Type", user_type)

View File

@@ -659,7 +659,7 @@ class update_entries_after(object):
) )
if self.valuation_method == "Moving Average": if self.valuation_method == "Moving Average":
rate = self.data[self.args.warehouse].previous_sle.valuation_rate rate = flt(self.data[self.args.warehouse].previous_sle.valuation_rate)
else: else:
rate = get_rate_for_return( rate = get_rate_for_return(
sle.voucher_type, sle.voucher_type,

View File

@@ -7,7 +7,7 @@
{% if d.thumbnail or d.image %} {% if d.thumbnail or d.image %}
{{ product_image(d.thumbnail or d.image, no_border=True) }} {{ product_image(d.thumbnail or d.image, no_border=True) }}
{% else %} {% else %}
<div class="no-image-cart-item" style="min-height: 100px;"> <div class="no-image-cart-item" style="min-height: 50px;">
{{ frappe.utils.get_abbr(d.item_name) or "NA" }} {{ frappe.utils.get_abbr(d.item_name) or "NA" }}
</div> </div>
{% endif %} {% endif %}

View File

@@ -81,7 +81,7 @@ rfq = Class.extend({
doc: doc doc: doc
}, },
btn: this, btn: this,
callback: function(r){ callback: function(r) {
frappe.unfreeze(); frappe.unfreeze();
if(r.message){ if(r.message){
$('.btn-sm').hide() $('.btn-sm').hide()

View File

@@ -1,19 +1,25 @@
{% from "erpnext/templates/includes/macros.html" import product_image_square, product_image %} {% from "erpnext/templates/includes/macros.html" import product_image_square, product_image %}
{% macro item_name_and_description(d, doc) %} {% macro item_name_and_description(d, doc) %}
<div class="row"> <div class="row">
<div class="col-3"> <div class="col-3">
{{ product_image(d.image) }} {% if d.image %}
</div> {{ product_image(d.image) }}
<div class="col-9"> {% else %}
{{ d.item_code }} <div class="website-image h-100 w-100" style="background-color:var(--gray-100);text-align: center;line-height: 3.6;">
<p class="text-muted small">{{ d.description }}</p> {{ frappe.utils.get_abbr(d.item_name)}}
</div>
{% endif %}
</div>
<div class="col-9">
{{ d.item_code }}
<p class="text-muted small">{{ d.description }}</p>
{% set supplier_part_no = frappe.db.get_value("Item Supplier", {'parent': d.item_code, 'supplier': doc.supplier}, "supplier_part_no") %} {% set supplier_part_no = frappe.db.get_value("Item Supplier", {'parent': d.item_code, 'supplier': doc.supplier}, "supplier_part_no") %}
<p class="text-muted small supplier-part-no"> <p class="text-muted small supplier-part-no">
{% if supplier_part_no %} {% if supplier_part_no %}
{{_("Supplier Part No") + ": "+ supplier_part_no}} {{_("Supplier Part No") + ": "+ supplier_part_no}}
{% endif %} {% endif %}
</p> </p>
</div> </div>
</div> </div>
{% endmacro %} {% endmacro %}

View File

@@ -153,7 +153,6 @@
</div> </div>
{% endif %} {% endif %}
{% if attachments %} {% if attachments %}
<div class="order-item-table"> <div class="order-item-table">
<div class="row order-items order-item-header text-muted"> <div class="row order-items order-item-header text-muted">
@@ -181,6 +180,7 @@
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block script %} {% block script %}
<script> {% include "templates/pages/order.js" %} </script> <script> {% include "templates/pages/order.js" %} </script>
<script> <script>

View File

@@ -1,7 +1,7 @@
{% extends "templates/web.html" %} {% extends "templates/web.html" %}
{% block header %} {% block header %}
<h1>{{ doc.name }}</h1> <h1 style="margin-top: 10px;">{{ doc.name }}</h1>
{% endblock %} {% endblock %}
{% block script %} {% block script %}
@@ -16,7 +16,7 @@
{% if doc.items %} {% if doc.items %}
<button class="btn btn-primary btn-sm" <button class="btn btn-primary btn-sm"
type="button"> type="button">
{{ _("Submit") }}</button> {{ _("Make Quotation") }}</button>
{% endif %} {% endif %}
{% endblock %} {% endblock %}