codymaster 4.6.0 → 5.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +74 -8
- package/README.md +192 -95
- package/dist/advisory-handoff.js +89 -0
- package/dist/advisory-report.js +105 -0
- package/dist/browse-server.js +251 -0
- package/dist/cli/command-registry.js +34 -0
- package/dist/cli/commands/agent.js +120 -0
- package/dist/cli/commands/bench.js +69 -0
- package/dist/cli/commands/brain.js +108 -0
- package/dist/cli/commands/dashboard.js +93 -0
- package/dist/cli/commands/design-studio.js +111 -0
- package/dist/cli/commands/distro.js +25 -0
- package/dist/cli/commands/engineering.js +596 -0
- package/dist/cli/commands/evolve.js +123 -0
- package/dist/cli/commands/mcp-serve.js +104 -0
- package/dist/cli/commands/project.js +324 -0
- package/dist/cli/commands/skill-chain.js +269 -0
- package/dist/cli/commands/system.js +89 -0
- package/dist/cli/commands/task.js +254 -0
- package/dist/cli/update-check.js +83 -0
- package/dist/cm-config.js +92 -0
- package/dist/cm-suggest.js +77 -0
- package/dist/codybench/judges/automated.js +31 -0
- package/dist/codybench/runners/claude-code.js +32 -0
- package/dist/codybench/suites/memory-retention.js +85 -0
- package/dist/codybench/suites/tdd-regression.js +35 -0
- package/dist/codybench/suites/token-efficiency.js +55 -0
- package/dist/codybench/types.js +2 -0
- package/dist/context-db.js +157 -0
- package/dist/continuity.js +2 -6
- package/dist/distro-validate.js +54 -0
- package/dist/execution-analyzer.js +138 -0
- package/dist/guardian-core.js +74 -0
- package/dist/index.js +36 -2759
- package/dist/indexer/skills-lib.js +533 -0
- package/dist/indexer/skills-map.js +1374 -0
- package/dist/indexer/skills.js +16 -0
- package/dist/learning-promoter.js +246 -0
- package/dist/mcp-context-server.js +289 -1
- package/dist/mcp-skills-tools.js +81 -0
- package/dist/retro-summary.js +70 -0
- package/dist/second-opinion-providers.js +79 -0
- package/dist/skill-chain.js +63 -1
- package/dist/skill-evolver.js +456 -0
- package/dist/skill-execution-cache.js +254 -0
- package/dist/smart-brain-router.js +184 -0
- package/dist/sprint-pipeline.js +228 -0
- package/dist/storage-backend.js +14 -67
- package/dist/token-budget.js +88 -0
- package/dist/utils/cli-utils.js +76 -0
- package/dist/utils/skill-utils.js +32 -0
- package/package.json +17 -7
- package/scripts/build-skills.mjs +51 -0
- package/scripts/gate-0-repo-hygiene.js +75 -0
- package/scripts/postinstall.js +34 -28
- package/scripts/security-scan.js +1 -1
- package/scripts/validate-skills.mjs +42 -0
- package/skills/CLAUDE.md +2 -7
- package/skills/_shared/helpers.md +2 -8
- package/skills/cm-ads-tracker/SKILL.md +3 -6
- package/skills/cm-browse/SKILL.md +34 -0
- package/skills/cm-conductor-worktrees/SKILL.md +28 -0
- package/skills/cm-content-factory/SKILL.md +1 -1
- package/skills/cm-content-factory/landing/docs/content/changelog.md +36 -0
- package/skills/cm-content-factory/landing/docs/content/deployment.md +46 -0
- package/skills/cm-content-factory/landing/docs/content/execution-flow.md +67 -0
- package/skills/cm-content-factory/landing/docs/content/memory-system.md +38 -0
- package/skills/cm-content-factory/landing/docs/content/openspace.md +27 -0
- package/skills/cm-content-factory/landing/docs/content/use-cases.md +26 -0
- package/skills/cm-content-factory/landing/docs/content/v5-intro.md +28 -0
- package/skills/cm-content-factory/landing/docs/index.html +240 -0
- package/skills/cm-content-factory/landing/index.html +100 -100
- package/skills/cm-content-factory/landing/script.js +42 -0
- package/skills/cm-content-factory/landing/translations.js +400 -400
- package/skills/cm-continuity/SKILL.md +32 -33
- package/skills/cm-design-studio/SKILL.md +34 -0
- package/skills/cm-ecosystem-roadmap/SKILL.md +15 -0
- package/skills/cm-engineering-meta/SKILL.md +73 -0
- package/skills/cm-growth-hacking/SKILL.md +1 -12
- package/skills/cm-guardian-runtime/SKILL.md +26 -0
- package/skills/cm-mcp-engineering/SKILL.md +22 -0
- package/skills/cm-notebooklm/SKILL.md +1 -17
- package/skills/cm-post-deploy-canary/SKILL.md +22 -0
- package/skills/cm-project-bootstrap/SKILL.md +11 -0
- package/skills/cm-qa-visual-cli/SKILL.md +22 -0
- package/skills/cm-retro-cli/SKILL.md +23 -0
- package/skills/cm-second-opinion-cli/SKILL.md +23 -0
- package/skills/cm-secret-shield/SKILL.md +2 -2
- package/skills/cm-security-gate/SKILL.md +1 -0
- package/skills/cm-skill-chain/SKILL.md +25 -4
- package/skills/cm-skill-evolution/SKILL.md +83 -0
- package/skills/cm-skill-health/SKILL.md +83 -0
- package/skills/cm-skill-index/SKILL.md +11 -3
- package/skills/cm-skill-search/SKILL.md +49 -0
- package/skills/cm-skill-share/SKILL.md +58 -0
- package/skills/cm-sprint-bus/SKILL.md +33 -0
- package/skills/cm-start/SKILL.md +0 -10
- package/skills/cm-tdd/SKILL.md +59 -72
- package/skills/profiles/README.md +21 -0
- package/skills/profiles/core.txt +23 -0
- package/skills/profiles/design.txt +6 -0
- package/skills/profiles/full.txt +62 -0
- package/skills/profiles/growth.txt +10 -0
- package/skills/profiles/knowledge.txt +7 -0
- package/install.sh +0 -901
- package/scripts/test-gemini.js +0 -13
- package/skills/cm-frappe-agent/SKILL.md +0 -134
- package/skills/cm-frappe-agent/agents/doctype-architect.md +0 -596
- package/skills/cm-frappe-agent/agents/erpnext-customizer.md +0 -643
- package/skills/cm-frappe-agent/agents/frappe-backend.md +0 -814
- package/skills/cm-frappe-agent/agents/frappe-custom-frontend.md +0 -557
- package/skills/cm-frappe-agent/agents/frappe-debugger.md +0 -625
- package/skills/cm-frappe-agent/agents/frappe-fixer.md +0 -275
- package/skills/cm-frappe-agent/agents/frappe-frontend.md +0 -660
- package/skills/cm-frappe-agent/agents/frappe-installer.md +0 -158
- package/skills/cm-frappe-agent/agents/frappe-performance.md +0 -307
- package/skills/cm-frappe-agent/agents/frappe-planner.md +0 -419
- package/skills/cm-frappe-agent/agents/frappe-remote-ops.md +0 -153
- package/skills/cm-frappe-agent/agents/github-workflow.md +0 -286
- package/skills/cm-frappe-agent/commands/frappe-app.md +0 -351
- package/skills/cm-frappe-agent/commands/frappe-backend.md +0 -162
- package/skills/cm-frappe-agent/commands/frappe-bench.md +0 -254
- package/skills/cm-frappe-agent/commands/frappe-debug.md +0 -263
- package/skills/cm-frappe-agent/commands/frappe-doctype-create.md +0 -272
- package/skills/cm-frappe-agent/commands/frappe-doctype-field.md +0 -310
- package/skills/cm-frappe-agent/commands/frappe-erpnext.md +0 -210
- package/skills/cm-frappe-agent/commands/frappe-fix.md +0 -59
- package/skills/cm-frappe-agent/commands/frappe-frontend.md +0 -210
- package/skills/cm-frappe-agent/commands/frappe-fullstack.md +0 -243
- package/skills/cm-frappe-agent/commands/frappe-github.md +0 -57
- package/skills/cm-frappe-agent/commands/frappe-install.md +0 -52
- package/skills/cm-frappe-agent/commands/frappe-plan.md +0 -442
- package/skills/cm-frappe-agent/commands/frappe-remote.md +0 -58
- package/skills/cm-frappe-agent/commands/frappe-test.md +0 -356
- package/skills/cm-frappe-agent/docs/README.md +0 -51
- package/skills/cm-frappe-agent/docs/agents-catalog.md +0 -113
- package/skills/cm-frappe-agent/docs/architecture.md +0 -149
- package/skills/cm-frappe-agent/docs/commands-catalog.md +0 -82
- package/skills/cm-frappe-agent/docs/resources-catalog.md +0 -66
- package/skills/cm-frappe-agent/docs/sitemap-urls.txt +0 -52
- package/skills/cm-frappe-agent/docs/sitemap.md +0 -81
- package/skills/cm-frappe-agent/docs/sop/user-guide.md +0 -178
- package/skills/cm-frappe-agent/docs/sop/vibe-coding-guide.md +0 -122
- package/skills/cm-frappe-agent/resources/7-layer-architecture.md +0 -985
- package/skills/cm-frappe-agent/resources/bench_commands.md +0 -73
- package/skills/cm-frappe-agent/resources/code-patterns-guide.md +0 -948
- package/skills/cm-frappe-agent/resources/common_pitfalls.md +0 -266
- package/skills/cm-frappe-agent/resources/doctype-registry.md +0 -158
- package/skills/cm-frappe-agent/resources/installation-guide.md +0 -289
- package/skills/cm-frappe-agent/resources/rest-api-patterns.md +0 -182
- package/skills/cm-frappe-agent/resources/scaffold_checklist.md +0 -82
- package/skills/cm-frappe-agent/resources/upgrade_patterns.md +0 -113
- package/skills/cm-frappe-agent/resources/web-form-patterns.md +0 -252
- package/skills/cm-frappe-agent/skills/bench-commands/SKILL.md +0 -621
- package/skills/cm-frappe-agent/skills/client-scripts/SKILL.md +0 -642
- package/skills/cm-frappe-agent/skills/doctype-patterns/SKILL.md +0 -576
- package/skills/cm-frappe-agent/skills/frappe-api/SKILL.md +0 -740
- package/skills/cm-frappe-agent/skills/remote-operations/SKILL.md +0 -47
- package/skills/cm-frappe-agent/skills/server-scripts/SKILL.md +0 -608
- package/skills/cm-frappe-agent/skills/web-forms/SKILL.md +0 -46
- package/skills/frappe-app-builder.zip +0 -0
|
@@ -1,740 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: frappe-api
|
|
3
|
-
description: Frappe Python and JavaScript API reference including document operations, database queries, utilities, and REST API patterns. Use when working with frappe.get_doc, frappe.db, frappe.call, or any Frappe API methods.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Frappe API Reference
|
|
7
|
-
|
|
8
|
-
Complete reference for Frappe's Python and JavaScript APIs for document operations, database queries, utilities, and server communication.
|
|
9
|
-
|
|
10
|
-
## When to Use This Skill
|
|
11
|
-
|
|
12
|
-
- Working with Document API (get_doc, new_doc, save)
|
|
13
|
-
- Database operations (frappe.db.*)
|
|
14
|
-
- Making API calls from client to server
|
|
15
|
-
- Using Frappe utilities (date, number formatting)
|
|
16
|
-
- Creating whitelisted API endpoints
|
|
17
|
-
- Working with REST API
|
|
18
|
-
|
|
19
|
-
## Python API
|
|
20
|
-
|
|
21
|
-
### Document Operations
|
|
22
|
-
|
|
23
|
-
#### Get Document
|
|
24
|
-
```python
|
|
25
|
-
# Get existing document
|
|
26
|
-
doc = frappe.get_doc("Customer", "CUST-001")
|
|
27
|
-
|
|
28
|
-
# Get document with filters
|
|
29
|
-
doc = frappe.get_doc("Customer", {"customer_name": "John"})
|
|
30
|
-
|
|
31
|
-
# Get last document
|
|
32
|
-
doc = frappe.get_last_doc("Customer", filters={"status": "Active"})
|
|
33
|
-
|
|
34
|
-
# Get cached document (read-only, faster)
|
|
35
|
-
doc = frappe.get_cached_doc("Customer", "CUST-001")
|
|
36
|
-
|
|
37
|
-
# Check if document exists
|
|
38
|
-
if frappe.db.exists("Customer", "CUST-001"):
|
|
39
|
-
doc = frappe.get_doc("Customer", "CUST-001")
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
#### Create Document
|
|
43
|
-
```python
|
|
44
|
-
# Create new document
|
|
45
|
-
doc = frappe.new_doc("Customer")
|
|
46
|
-
doc.customer_name = "New Customer"
|
|
47
|
-
doc.customer_type = "Company"
|
|
48
|
-
doc.insert()
|
|
49
|
-
|
|
50
|
-
# Create with dict
|
|
51
|
-
doc = frappe.get_doc({
|
|
52
|
-
"doctype": "Customer",
|
|
53
|
-
"customer_name": "New Customer",
|
|
54
|
-
"customer_type": "Company"
|
|
55
|
-
})
|
|
56
|
-
doc.insert()
|
|
57
|
-
|
|
58
|
-
# Create and insert in one step
|
|
59
|
-
doc = frappe.get_doc({
|
|
60
|
-
"doctype": "Customer",
|
|
61
|
-
"customer_name": "New Customer"
|
|
62
|
-
}).insert()
|
|
63
|
-
|
|
64
|
-
# Insert ignoring permissions
|
|
65
|
-
doc.insert(ignore_permissions=True)
|
|
66
|
-
|
|
67
|
-
# Insert ignoring mandatory fields
|
|
68
|
-
doc.insert(ignore_mandatory=True)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
#### Update Document
|
|
72
|
-
```python
|
|
73
|
-
# Update and save
|
|
74
|
-
doc = frappe.get_doc("Customer", "CUST-001")
|
|
75
|
-
doc.customer_name = "Updated Name"
|
|
76
|
-
doc.save()
|
|
77
|
-
|
|
78
|
-
# Save ignoring permissions
|
|
79
|
-
doc.save(ignore_permissions=True)
|
|
80
|
-
|
|
81
|
-
# Update single value
|
|
82
|
-
frappe.db.set_value("Customer", "CUST-001", "customer_name", "New Name")
|
|
83
|
-
|
|
84
|
-
# Update multiple values
|
|
85
|
-
frappe.db.set_value("Customer", "CUST-001", {
|
|
86
|
-
"customer_name": "New Name",
|
|
87
|
-
"status": "Active"
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
# Bulk update
|
|
91
|
-
frappe.db.set_value("Customer", {"status": "Inactive"}, "status", "Active")
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
#### Delete Document
|
|
95
|
-
```python
|
|
96
|
-
# Delete document
|
|
97
|
-
frappe.delete_doc("Customer", "CUST-001")
|
|
98
|
-
|
|
99
|
-
# Delete ignoring permissions
|
|
100
|
-
frappe.delete_doc("Customer", "CUST-001", ignore_permissions=True)
|
|
101
|
-
|
|
102
|
-
# Delete with linked documents
|
|
103
|
-
frappe.delete_doc("Customer", "CUST-001", force=True)
|
|
104
|
-
|
|
105
|
-
# Delete from controller
|
|
106
|
-
doc.delete()
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Database API (frappe.db)
|
|
110
|
-
|
|
111
|
-
#### Select Queries
|
|
112
|
-
```python
|
|
113
|
-
# Get single value
|
|
114
|
-
value = frappe.db.get_value("Customer", "CUST-001", "customer_name")
|
|
115
|
-
|
|
116
|
-
# Get multiple fields
|
|
117
|
-
values = frappe.db.get_value("Customer", "CUST-001",
|
|
118
|
-
["customer_name", "status"], as_dict=True)
|
|
119
|
-
|
|
120
|
-
# Get with filters
|
|
121
|
-
value = frappe.db.get_value("Customer",
|
|
122
|
-
{"customer_type": "Company"}, "customer_name")
|
|
123
|
-
|
|
124
|
-
# Get list of values
|
|
125
|
-
names = frappe.db.get_all("Customer",
|
|
126
|
-
filters={"status": "Active"},
|
|
127
|
-
fields=["name", "customer_name"],
|
|
128
|
-
order_by="creation desc",
|
|
129
|
-
limit=10
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
# Get list with pluck (single field as list)
|
|
133
|
-
names = frappe.db.get_all("Customer",
|
|
134
|
-
filters={"status": "Active"},
|
|
135
|
-
pluck="name"
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
# Complex filters
|
|
139
|
-
docs = frappe.db.get_all("Sales Invoice",
|
|
140
|
-
filters={
|
|
141
|
-
"status": ["in", ["Paid", "Unpaid"]],
|
|
142
|
-
"grand_total": [">", 1000],
|
|
143
|
-
"posting_date": ["between", ["2024-01-01", "2024-12-31"]],
|
|
144
|
-
"customer": ["like", "%Corp%"]
|
|
145
|
-
},
|
|
146
|
-
fields=["name", "customer", "grand_total"]
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
# Filter operators
|
|
150
|
-
# =, !=, <, >, <=, >=
|
|
151
|
-
# in, not in
|
|
152
|
-
# like, not like
|
|
153
|
-
# between
|
|
154
|
-
# is, is not (for None)
|
|
155
|
-
# descendants of, ancestors of (for tree doctypes)
|
|
156
|
-
|
|
157
|
-
# Get count
|
|
158
|
-
count = frappe.db.count("Customer", {"status": "Active"})
|
|
159
|
-
|
|
160
|
-
# Check existence
|
|
161
|
-
exists = frappe.db.exists("Customer", "CUST-001")
|
|
162
|
-
exists = frappe.db.exists("Customer", {"customer_name": "John"})
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
#### Raw SQL
|
|
166
|
-
```python
|
|
167
|
-
# Execute SQL query
|
|
168
|
-
result = frappe.db.sql("""
|
|
169
|
-
SELECT name, customer_name, grand_total
|
|
170
|
-
FROM `tabSales Invoice`
|
|
171
|
-
WHERE status = %s AND grand_total > %s
|
|
172
|
-
ORDER BY creation DESC
|
|
173
|
-
LIMIT 10
|
|
174
|
-
""", ("Paid", 1000), as_dict=True)
|
|
175
|
-
|
|
176
|
-
# Single value
|
|
177
|
-
total = frappe.db.sql("""
|
|
178
|
-
SELECT SUM(grand_total) FROM `tabSales Invoice`
|
|
179
|
-
WHERE status = 'Paid'
|
|
180
|
-
""")[0][0]
|
|
181
|
-
|
|
182
|
-
# With named parameters
|
|
183
|
-
result = frappe.db.sql("""
|
|
184
|
-
SELECT * FROM `tabCustomer`
|
|
185
|
-
WHERE name = %(name)s
|
|
186
|
-
""", {"name": "CUST-001"}, as_dict=True)
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
#### Insert/Update
|
|
190
|
-
```python
|
|
191
|
-
# Insert raw
|
|
192
|
-
frappe.db.sql("""
|
|
193
|
-
INSERT INTO `tabCustomer` (name, customer_name)
|
|
194
|
-
VALUES (%s, %s)
|
|
195
|
-
""", ("CUST-002", "New Customer"))
|
|
196
|
-
|
|
197
|
-
# Commit transaction
|
|
198
|
-
frappe.db.commit()
|
|
199
|
-
|
|
200
|
-
# Rollback
|
|
201
|
-
frappe.db.rollback()
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Whitelisted API
|
|
205
|
-
|
|
206
|
-
```python
|
|
207
|
-
# Create API endpoint
|
|
208
|
-
@frappe.whitelist()
|
|
209
|
-
def get_customer_details(customer):
|
|
210
|
-
"""Get customer details
|
|
211
|
-
|
|
212
|
-
Args:
|
|
213
|
-
customer: Customer ID
|
|
214
|
-
|
|
215
|
-
Returns:
|
|
216
|
-
dict: Customer details
|
|
217
|
-
"""
|
|
218
|
-
doc = frappe.get_doc("Customer", customer)
|
|
219
|
-
return {
|
|
220
|
-
"name": doc.name,
|
|
221
|
-
"customer_name": doc.customer_name,
|
|
222
|
-
"outstanding_amount": get_outstanding(customer)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
# Allow guest access (no login required)
|
|
226
|
-
@frappe.whitelist(allow_guest=True)
|
|
227
|
-
def public_api():
|
|
228
|
-
return {"status": "ok"}
|
|
229
|
-
|
|
230
|
-
# With specific methods
|
|
231
|
-
@frappe.whitelist(methods=["POST"])
|
|
232
|
-
def create_record(data):
|
|
233
|
-
doc = frappe.get_doc(data)
|
|
234
|
-
doc.insert()
|
|
235
|
-
return doc.name
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
### Utilities
|
|
239
|
-
|
|
240
|
-
#### Date/Time
|
|
241
|
-
```python
|
|
242
|
-
from frappe.utils import (
|
|
243
|
-
now, nowdate, nowtime, now_datetime,
|
|
244
|
-
today, getdate, get_datetime,
|
|
245
|
-
add_days, add_months, add_years,
|
|
246
|
-
date_diff, time_diff, time_diff_in_seconds,
|
|
247
|
-
get_first_day, get_last_day,
|
|
248
|
-
formatdate, format_datetime
|
|
249
|
-
)
|
|
250
|
-
|
|
251
|
-
# Current date/time
|
|
252
|
-
current = nowdate() # "2024-01-15"
|
|
253
|
-
current_dt = now_datetime() # datetime object
|
|
254
|
-
timestamp = now() # "2024-01-15 10:30:00"
|
|
255
|
-
|
|
256
|
-
# Date arithmetic
|
|
257
|
-
next_week = add_days(nowdate(), 7)
|
|
258
|
-
next_month = add_months(nowdate(), 1)
|
|
259
|
-
last_year = add_years(nowdate(), -1)
|
|
260
|
-
|
|
261
|
-
# Date difference
|
|
262
|
-
days = date_diff(end_date, start_date)
|
|
263
|
-
seconds = time_diff_in_seconds(end_time, start_time)
|
|
264
|
-
|
|
265
|
-
# First/last day of month
|
|
266
|
-
first = get_first_day(nowdate())
|
|
267
|
-
last = get_last_day(nowdate())
|
|
268
|
-
|
|
269
|
-
# Parse dates
|
|
270
|
-
date_obj = getdate("2024-01-15")
|
|
271
|
-
dt_obj = get_datetime("2024-01-15 10:30:00")
|
|
272
|
-
|
|
273
|
-
# Format dates
|
|
274
|
-
formatted = formatdate("2024-01-15", "dd-MM-yyyy")
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
#### Numbers
|
|
278
|
-
```python
|
|
279
|
-
from frappe.utils import (
|
|
280
|
-
flt, cint, cstr,
|
|
281
|
-
fmt_money, rounded,
|
|
282
|
-
money_in_words
|
|
283
|
-
)
|
|
284
|
-
|
|
285
|
-
# Type conversion with defaults
|
|
286
|
-
num = flt(value) # float, None -> 0.0
|
|
287
|
-
num = flt(value, 2) # with precision
|
|
288
|
-
integer = cint(value) # int, None -> 0
|
|
289
|
-
string = cstr(value) # string, None -> ""
|
|
290
|
-
|
|
291
|
-
# Formatting
|
|
292
|
-
formatted = fmt_money(1234.56, currency="USD") # "$1,234.56"
|
|
293
|
-
rounded_val = rounded(1234.567, 2) # 1234.57
|
|
294
|
-
words = money_in_words(1234.56, "USD") # "One Thousand..."
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
#### Strings
|
|
298
|
-
```python
|
|
299
|
-
from frappe.utils import (
|
|
300
|
-
strip_html, strip_html_tags,
|
|
301
|
-
escape_html, sanitize_html,
|
|
302
|
-
scrub, unscrub
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
# HTML handling
|
|
306
|
-
plain = strip_html("<p>Hello</p>") # "Hello"
|
|
307
|
-
safe = escape_html("<script>bad</script>")
|
|
308
|
-
|
|
309
|
-
# Field name conversion
|
|
310
|
-
field = scrub("My Field Name") # "my_field_name"
|
|
311
|
-
label = unscrub("my_field_name") # "My Field Name"
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
### Messaging & Notifications
|
|
315
|
-
|
|
316
|
-
```python
|
|
317
|
-
# Show message (appears as toast)
|
|
318
|
-
frappe.msgprint("Document saved successfully")
|
|
319
|
-
|
|
320
|
-
# With indicator
|
|
321
|
-
frappe.msgprint("Error occurred", indicator="red", title="Error")
|
|
322
|
-
|
|
323
|
-
# Throw error (stops execution)
|
|
324
|
-
frappe.throw("Invalid data provided")
|
|
325
|
-
|
|
326
|
-
# With exception type
|
|
327
|
-
from frappe.exceptions import ValidationError
|
|
328
|
-
frappe.throw("Validation failed", exc=ValidationError)
|
|
329
|
-
|
|
330
|
-
# Send email
|
|
331
|
-
frappe.sendmail(
|
|
332
|
-
recipients=["user@example.com"],
|
|
333
|
-
subject="Hello",
|
|
334
|
-
message="Email body",
|
|
335
|
-
template="email_template",
|
|
336
|
-
args={"name": "John"}
|
|
337
|
-
)
|
|
338
|
-
|
|
339
|
-
# Create system notification
|
|
340
|
-
frappe.publish_realtime(
|
|
341
|
-
"msgprint",
|
|
342
|
-
{"message": "Task completed"},
|
|
343
|
-
user="user@example.com"
|
|
344
|
-
)
|
|
345
|
-
```
|
|
346
|
-
|
|
347
|
-
### Background Jobs
|
|
348
|
-
|
|
349
|
-
```python
|
|
350
|
-
# Enqueue background job
|
|
351
|
-
frappe.enqueue(
|
|
352
|
-
"myapp.tasks.heavy_task",
|
|
353
|
-
queue="long",
|
|
354
|
-
timeout=600,
|
|
355
|
-
job_name="Heavy Task",
|
|
356
|
-
customer="CUST-001"
|
|
357
|
-
)
|
|
358
|
-
|
|
359
|
-
# In tasks.py
|
|
360
|
-
def heavy_task(customer):
|
|
361
|
-
# Long running task
|
|
362
|
-
process_customer(customer)
|
|
363
|
-
|
|
364
|
-
# Enqueue with callback
|
|
365
|
-
frappe.enqueue(
|
|
366
|
-
method=process_data,
|
|
367
|
-
queue="default",
|
|
368
|
-
on_success=on_complete,
|
|
369
|
-
on_failure=on_error
|
|
370
|
-
)
|
|
371
|
-
|
|
372
|
-
# Scheduled jobs (in hooks.py)
|
|
373
|
-
scheduler_events = {
|
|
374
|
-
"daily": [
|
|
375
|
-
"myapp.tasks.daily_task"
|
|
376
|
-
],
|
|
377
|
-
"hourly": [
|
|
378
|
-
"myapp.tasks.hourly_task"
|
|
379
|
-
],
|
|
380
|
-
"cron": {
|
|
381
|
-
"0 0 * * *": [ # Midnight
|
|
382
|
-
"myapp.tasks.midnight_task"
|
|
383
|
-
]
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
### Session & User
|
|
389
|
-
|
|
390
|
-
```python
|
|
391
|
-
# Current user
|
|
392
|
-
user = frappe.session.user
|
|
393
|
-
|
|
394
|
-
# Check if logged in
|
|
395
|
-
if frappe.session.user != "Guest":
|
|
396
|
-
pass
|
|
397
|
-
|
|
398
|
-
# Check permissions
|
|
399
|
-
if frappe.has_permission("Customer", "write"):
|
|
400
|
-
pass
|
|
401
|
-
|
|
402
|
-
# Get user info
|
|
403
|
-
user_doc = frappe.get_doc("User", frappe.session.user)
|
|
404
|
-
full_name = frappe.utils.get_fullname(frappe.session.user)
|
|
405
|
-
|
|
406
|
-
# Check roles
|
|
407
|
-
if "System Manager" in frappe.get_roles():
|
|
408
|
-
pass
|
|
409
|
-
|
|
410
|
-
# Run as different user
|
|
411
|
-
frappe.set_user("Administrator")
|
|
412
|
-
# ... do operations
|
|
413
|
-
frappe.set_user(original_user)
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
## JavaScript API
|
|
417
|
-
|
|
418
|
-
### Document Operations
|
|
419
|
-
|
|
420
|
-
```javascript
|
|
421
|
-
// Get document
|
|
422
|
-
frappe.call({
|
|
423
|
-
method: 'frappe.client.get',
|
|
424
|
-
args: {
|
|
425
|
-
doctype: 'Customer',
|
|
426
|
-
name: 'CUST-001'
|
|
427
|
-
},
|
|
428
|
-
callback: function(r) {
|
|
429
|
-
console.log(r.message);
|
|
430
|
-
}
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
// Create document
|
|
434
|
-
frappe.call({
|
|
435
|
-
method: 'frappe.client.insert',
|
|
436
|
-
args: {
|
|
437
|
-
doc: {
|
|
438
|
-
doctype: 'Customer',
|
|
439
|
-
customer_name: 'New Customer'
|
|
440
|
-
}
|
|
441
|
-
},
|
|
442
|
-
callback: function(r) {
|
|
443
|
-
console.log('Created:', r.message.name);
|
|
444
|
-
}
|
|
445
|
-
});
|
|
446
|
-
|
|
447
|
-
// Save document
|
|
448
|
-
frappe.call({
|
|
449
|
-
method: 'frappe.client.save',
|
|
450
|
-
args: {
|
|
451
|
-
doc: cur_frm.doc
|
|
452
|
-
}
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
// Delete document
|
|
456
|
-
frappe.call({
|
|
457
|
-
method: 'frappe.client.delete',
|
|
458
|
-
args: {
|
|
459
|
-
doctype: 'Customer',
|
|
460
|
-
name: 'CUST-001'
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### frappe.call (API Calls)
|
|
466
|
-
|
|
467
|
-
```javascript
|
|
468
|
-
// Call whitelisted method
|
|
469
|
-
frappe.call({
|
|
470
|
-
method: 'myapp.api.get_customer_details',
|
|
471
|
-
args: {
|
|
472
|
-
customer: 'CUST-001'
|
|
473
|
-
},
|
|
474
|
-
freeze: true,
|
|
475
|
-
freeze_message: 'Loading...',
|
|
476
|
-
callback: function(r) {
|
|
477
|
-
if (r.message) {
|
|
478
|
-
console.log(r.message);
|
|
479
|
-
}
|
|
480
|
-
},
|
|
481
|
-
error: function(r) {
|
|
482
|
-
frappe.msgprint('Error occurred');
|
|
483
|
-
}
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
// Async/await pattern
|
|
487
|
-
async function getCustomer(name) {
|
|
488
|
-
const response = await frappe.call({
|
|
489
|
-
method: 'myapp.api.get_customer',
|
|
490
|
-
args: { name }
|
|
491
|
-
});
|
|
492
|
-
return response.message;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// Call with promise
|
|
496
|
-
frappe.call({
|
|
497
|
-
method: 'myapp.api.process',
|
|
498
|
-
args: { data: 'test' }
|
|
499
|
-
}).then(r => {
|
|
500
|
-
console.log(r.message);
|
|
501
|
-
});
|
|
502
|
-
```
|
|
503
|
-
|
|
504
|
-
### Form API (cur_frm)
|
|
505
|
-
|
|
506
|
-
```javascript
|
|
507
|
-
frappe.ui.form.on('Sales Invoice', {
|
|
508
|
-
refresh: function(frm) {
|
|
509
|
-
// Add custom button
|
|
510
|
-
frm.add_custom_button('Process', function() {
|
|
511
|
-
// Button action
|
|
512
|
-
}, 'Actions');
|
|
513
|
-
|
|
514
|
-
// Set field properties
|
|
515
|
-
frm.set_df_property('field_name', 'read_only', 1);
|
|
516
|
-
frm.set_df_property('field_name', 'hidden', 1);
|
|
517
|
-
frm.set_df_property('field_name', 'reqd', 1);
|
|
518
|
-
|
|
519
|
-
// Set query for link field
|
|
520
|
-
frm.set_query('customer', function() {
|
|
521
|
-
return {
|
|
522
|
-
filters: {
|
|
523
|
-
status: 'Active'
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
// Toggle fields
|
|
529
|
-
frm.toggle_display('field_name', frm.doc.show_field);
|
|
530
|
-
frm.toggle_reqd('field_name', frm.doc.is_required);
|
|
531
|
-
},
|
|
532
|
-
|
|
533
|
-
customer: function(frm) {
|
|
534
|
-
// Field change handler
|
|
535
|
-
if (frm.doc.customer) {
|
|
536
|
-
frappe.call({
|
|
537
|
-
method: 'myapp.api.get_customer_details',
|
|
538
|
-
args: { customer: frm.doc.customer },
|
|
539
|
-
callback: function(r) {
|
|
540
|
-
frm.set_value('customer_name', r.message.name);
|
|
541
|
-
}
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
},
|
|
545
|
-
|
|
546
|
-
validate: function(frm) {
|
|
547
|
-
// Validate before save
|
|
548
|
-
if (!frm.doc.customer) {
|
|
549
|
-
frappe.throw('Customer is required');
|
|
550
|
-
return false;
|
|
551
|
-
}
|
|
552
|
-
},
|
|
553
|
-
|
|
554
|
-
before_save: function(frm) {
|
|
555
|
-
// Before save actions
|
|
556
|
-
frm.doc.modified_by_script = 1;
|
|
557
|
-
},
|
|
558
|
-
|
|
559
|
-
after_save: function(frm) {
|
|
560
|
-
// After save actions
|
|
561
|
-
frappe.show_alert('Document saved!');
|
|
562
|
-
}
|
|
563
|
-
});
|
|
564
|
-
|
|
565
|
-
// Child table events
|
|
566
|
-
frappe.ui.form.on('Sales Invoice Item', {
|
|
567
|
-
qty: function(frm, cdt, cdn) {
|
|
568
|
-
let row = locals[cdt][cdn];
|
|
569
|
-
row.amount = row.qty * row.rate;
|
|
570
|
-
frm.refresh_field('items');
|
|
571
|
-
},
|
|
572
|
-
|
|
573
|
-
items_add: function(frm, cdt, cdn) {
|
|
574
|
-
// New row added
|
|
575
|
-
let row = locals[cdt][cdn];
|
|
576
|
-
row.warehouse = frm.doc.default_warehouse;
|
|
577
|
-
},
|
|
578
|
-
|
|
579
|
-
items_remove: function(frm) {
|
|
580
|
-
// Row removed - recalculate totals
|
|
581
|
-
calculate_totals(frm);
|
|
582
|
-
}
|
|
583
|
-
});
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
### Dialogs
|
|
587
|
-
|
|
588
|
-
```javascript
|
|
589
|
-
// Simple prompt
|
|
590
|
-
frappe.prompt(
|
|
591
|
-
{fieldname: 'name', fieldtype: 'Data', label: 'Name', reqd: 1},
|
|
592
|
-
function(values) {
|
|
593
|
-
console.log(values.name);
|
|
594
|
-
},
|
|
595
|
-
'Enter Name'
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
// Multiple fields
|
|
599
|
-
frappe.prompt([
|
|
600
|
-
{fieldname: 'name', fieldtype: 'Data', label: 'Name', reqd: 1},
|
|
601
|
-
{fieldname: 'email', fieldtype: 'Data', label: 'Email', options: 'Email'},
|
|
602
|
-
{fieldname: 'date', fieldtype: 'Date', label: 'Date', default: frappe.datetime.nowdate()}
|
|
603
|
-
], function(values) {
|
|
604
|
-
console.log(values);
|
|
605
|
-
}, 'Enter Details', 'Submit');
|
|
606
|
-
|
|
607
|
-
// Custom dialog
|
|
608
|
-
let dialog = new frappe.ui.Dialog({
|
|
609
|
-
title: 'My Dialog',
|
|
610
|
-
fields: [
|
|
611
|
-
{fieldname: 'customer', fieldtype: 'Link', options: 'Customer', label: 'Customer'},
|
|
612
|
-
{fieldname: 'amount', fieldtype: 'Currency', label: 'Amount'}
|
|
613
|
-
],
|
|
614
|
-
primary_action_label: 'Submit',
|
|
615
|
-
primary_action: function(values) {
|
|
616
|
-
console.log(values);
|
|
617
|
-
dialog.hide();
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
dialog.show();
|
|
621
|
-
|
|
622
|
-
// Confirmation
|
|
623
|
-
frappe.confirm(
|
|
624
|
-
'Are you sure you want to proceed?',
|
|
625
|
-
function() {
|
|
626
|
-
// Yes
|
|
627
|
-
process_action();
|
|
628
|
-
},
|
|
629
|
-
function() {
|
|
630
|
-
// No
|
|
631
|
-
}
|
|
632
|
-
);
|
|
633
|
-
```
|
|
634
|
-
|
|
635
|
-
### Utilities
|
|
636
|
-
|
|
637
|
-
```javascript
|
|
638
|
-
// Messages
|
|
639
|
-
frappe.msgprint('Hello World');
|
|
640
|
-
frappe.msgprint({
|
|
641
|
-
title: 'Success',
|
|
642
|
-
message: 'Operation completed',
|
|
643
|
-
indicator: 'green'
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
frappe.throw('Error message'); // Stops execution
|
|
647
|
-
|
|
648
|
-
frappe.show_alert('Quick notification', 5); // 5 seconds
|
|
649
|
-
|
|
650
|
-
// Date/time
|
|
651
|
-
frappe.datetime.nowdate(); // "2024-01-15"
|
|
652
|
-
frappe.datetime.now_datetime(); // "2024-01-15 10:30:00"
|
|
653
|
-
frappe.datetime.add_days('2024-01-15', 7);
|
|
654
|
-
frappe.datetime.str_to_obj('2024-01-15');
|
|
655
|
-
|
|
656
|
-
// Format
|
|
657
|
-
frappe.format(1234.56, {fieldtype: 'Currency'});
|
|
658
|
-
frappe.format('2024-01-15', {fieldtype: 'Date'});
|
|
659
|
-
|
|
660
|
-
// Routing
|
|
661
|
-
frappe.set_route('Form', 'Customer', 'CUST-001');
|
|
662
|
-
frappe.set_route('List', 'Customer');
|
|
663
|
-
frappe.set_route('query-report', 'Sales Report');
|
|
664
|
-
|
|
665
|
-
// Current route
|
|
666
|
-
let route = frappe.get_route();
|
|
667
|
-
```
|
|
668
|
-
|
|
669
|
-
## REST API
|
|
670
|
-
|
|
671
|
-
### Authentication
|
|
672
|
-
|
|
673
|
-
```bash
|
|
674
|
-
# Token-based
|
|
675
|
-
curl -X GET "https://site.com/api/resource/Customer" \
|
|
676
|
-
-H "Authorization: token api_key:api_secret"
|
|
677
|
-
|
|
678
|
-
# Session-based (login first)
|
|
679
|
-
curl -X POST "https://site.com/api/method/login" \
|
|
680
|
-
-d "usr=user&pwd=password"
|
|
681
|
-
```
|
|
682
|
-
|
|
683
|
-
### CRUD Operations
|
|
684
|
-
|
|
685
|
-
```bash
|
|
686
|
-
# List
|
|
687
|
-
GET /api/resource/Customer?filters=[["status","=","Active"]]&fields=["name","customer_name"]&limit_page_length=10
|
|
688
|
-
|
|
689
|
-
# Get single
|
|
690
|
-
GET /api/resource/Customer/CUST-001
|
|
691
|
-
|
|
692
|
-
# Create
|
|
693
|
-
POST /api/resource/Customer
|
|
694
|
-
Content-Type: application/json
|
|
695
|
-
{"customer_name": "New Customer", "customer_type": "Company"}
|
|
696
|
-
|
|
697
|
-
# Update
|
|
698
|
-
PUT /api/resource/Customer/CUST-001
|
|
699
|
-
Content-Type: application/json
|
|
700
|
-
{"customer_name": "Updated Name"}
|
|
701
|
-
|
|
702
|
-
# Delete
|
|
703
|
-
DELETE /api/resource/Customer/CUST-001
|
|
704
|
-
```
|
|
705
|
-
|
|
706
|
-
### Call Methods
|
|
707
|
-
|
|
708
|
-
```bash
|
|
709
|
-
# Call whitelisted method
|
|
710
|
-
POST /api/method/myapp.api.get_customer_details
|
|
711
|
-
Content-Type: application/json
|
|
712
|
-
{"customer": "CUST-001"}
|
|
713
|
-
```
|
|
714
|
-
|
|
715
|
-
### JavaScript Fetch
|
|
716
|
-
|
|
717
|
-
```javascript
|
|
718
|
-
// Using fetch API
|
|
719
|
-
async function getCustomers() {
|
|
720
|
-
const response = await fetch('/api/resource/Customer?limit_page_length=10', {
|
|
721
|
-
headers: {
|
|
722
|
-
'Content-Type': 'application/json'
|
|
723
|
-
}
|
|
724
|
-
});
|
|
725
|
-
const data = await response.json();
|
|
726
|
-
return data.data;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
// POST request
|
|
730
|
-
async function createCustomer(customerData) {
|
|
731
|
-
const response = await fetch('/api/resource/Customer', {
|
|
732
|
-
method: 'POST',
|
|
733
|
-
headers: {
|
|
734
|
-
'Content-Type': 'application/json'
|
|
735
|
-
},
|
|
736
|
-
body: JSON.stringify(customerData)
|
|
737
|
-
});
|
|
738
|
-
return response.json();
|
|
739
|
-
}
|
|
740
|
-
```
|