-
Notifications
You must be signed in to change notification settings - Fork 7.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: render doctype caster server-script-callable #42160
Changes from all commits
1b40ce4
0a8d711
887789e
43fdb97
b5c4b49
67994de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -525,7 +525,10 @@ def set_missing_ref_details( | |
continue | ||
|
||
if field == "exchange_rate" or not d.get(field) or force: | ||
d.db_set(field, value) | ||
if self.get("_action") in ("submit", "cancel"): | ||
d.db_set(field, value) | ||
else: | ||
d.set(field, value) | ||
|
||
def validate_payment_type(self): | ||
if self.payment_type not in ("Receive", "Pay", "Internal Transfer"): | ||
|
@@ -1170,6 +1173,61 @@ def set_remarks(self): | |
|
||
self.set("remarks", "\n".join(remarks)) | ||
|
||
@frappe.requires_permission("Account", "read") | ||
@frappe.requires_permission("Sales Order", "read") | ||
@frappe.requires_permission("Payment Entry", "create") | ||
def _from_sales_order(self, so): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. where are these methods used? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This particular method can be used through their entry point(s) like so: pe = frappe.new_doc_from("Payment Entry", "Sales Order", "SO-001")
# or
so = frappe.get_doc("Sales Order", "SO-001")
pe = so.into("Payment Entry")
# or
pe = frappe.new_doc("Payment Entry")
pe.flags.some_flag = True
# with either
pe.from_doc("Sales Order", "SO-001")
# or with
so = frappe.get_doc("Sales Order", "SO-001")
pe.from_doc(so) Note, how this While I did originally consider to mangle these methods It should be made clear from this, docs and initial code precedents that users are supposed to use these only through their entry point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The decorators ( This approach achieves two main objectives:
Ensuring the presence of such decorators throughout the code base (unfortunately without being able to check for semantic correctness), was the goal of this sem-grep rule: frappe/semgrep-rules#28 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additional note, which triggered this PR series: Unlike combinations of Coincidentally, this is also the reason why this PR is heavy on the Payment Entry doctype which is most often involved in these particular hooks. Code Example from our Production: if doc.method == "ecommerce_integrations.ecwid.order.sync_order_from_payment_request":
ps = doc.flags.payment_session
if ps.changed and ps.is_success:
si = frappe.new_doc_from("Sales Invoice", doc.reference_doctype, doc.reference_docname, whitelist_permissions=True)
si.naming_series = "EC-INV-.YYYY.-"
si.flags.ignore_permissions=True
si.insert().submit()
gateway = ps.psl.get_gateway()
pe = frappe.new_doc_from("Payment Entry", si, whitelist_permissions=True)
pe.naming_series = "EC-PAY-.YYYY.-"
pe.paid_amount = ps.state.tx_data.amount
pe.receive_amount = ps.state.tx_data.amount
pe.reference_no = doc.name
pe.bank_account = gateway.payment_account
pe.flags.ignore_permissions=True
pe.insert().submit()
payload = json.loads(doc.request_data)
returnUrl = payload["returnUrl"]
if ps.is_success:
doc.flags.payment_session.result = {
"message": "Pago exitoso",
"action": {
"href": returnUrl,
"label": "Revisar Confirmacion",
"redirect_after_milliseconds": 5000,
},
}
else:
doc.flags.payment_session.result = {
"message": "Pago fallido",
"action": {
"href": returnUrl,
"label": "Volver al Carrito",
"redirect_after_milliseconds": 5000,
},
} Note the use of |
||
return get_payment_entry( | ||
so.doctype, | ||
so.name, | ||
payment_type="Receive", | ||
party_type="Customer", | ||
) | ||
|
||
@frappe.requires_permission("Account", "read") | ||
@frappe.requires_permission("Sales Invoice", "read") | ||
@frappe.requires_permission("Payment Entry", "create") | ||
def _from_sales_invoice(self, si): | ||
frappe.flags.new_payment_entry = self | ||
return get_payment_entry( | ||
si.doctype, | ||
si.name, | ||
payment_type="Receive", | ||
party_type="Customer", | ||
) | ||
|
||
@frappe.requires_permission("Account", "read") | ||
@frappe.requires_permission("Purchase Order", "read") | ||
@frappe.requires_permission("Payment Entry", "create") | ||
def _from_purchase_order(self, po): | ||
frappe.flags.new_payment_entry = self | ||
return get_payment_entry( | ||
po.doctype, | ||
po.name, | ||
payment_type="Receive", | ||
party_type="Supplier", | ||
) | ||
|
||
@frappe.requires_permission("Account", "read") | ||
@frappe.requires_permission("Purchase Invoice", "read") | ||
@frappe.requires_permission("Payment Entry", "create") | ||
def _from_purchase_invoice(self, pi): | ||
frappe.flags.new_payment_entry = self | ||
return get_payment_entry( | ||
pi.doctype, | ||
pi.name, | ||
payment_type="Receive", | ||
party_type="Supplier", | ||
) | ||
|
||
@frappe.requires_permission("Account", "read") | ||
@frappe.requires_permission("Dunning", "read") | ||
@frappe.requires_permission("Payment Entry", "create") | ||
def _from_dunning(self, d): | ||
frappe.flags.ignore_account_permission = frappe.flags.ignore_permissions | ||
frappe.flags.new_payment_entry = self | ||
return get_payment_entry(d.doctype, d.name) | ||
|
||
def build_gl_map(self): | ||
if self.payment_type in ("Receive", "Pay") and not self.get("party_account_field"): | ||
self.setup_party_account_field() | ||
|
@@ -2350,7 +2408,7 @@ def get_payment_entry( | |
paid_amount, received_amount, doc, party_account_currency, reference_date | ||
) | ||
|
||
pe = frappe.new_doc("Payment Entry") | ||
pe = frappe.flags.new_payment_entry or frappe.new_doc("Payment Entry") | ||
pe.payment_type = payment_type | ||
pe.company = doc.company | ||
pe.cost_center = doc.get("cost_center") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bridge pattern, uncovering — thus far — hidden code smells and performance hits, and will disappear after a few years of using the document read only mode.