From c43d1fac591462e48d2c6aa823e9b7e89d9b5fdb Mon Sep 17 00:00:00 2001 From: Loocor Date: Tue, 23 Jan 2018 03:20:46 -0600 Subject: [PATCH] frappe chinese document translation plan (#4689) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [add] Chinese documents init. * Videos chinese document translated * Bench chinese documents translated half * Bench chinese documents translated * Fixed several mistakes for bench guides * Frappé Tutorial chinese version in translation --- frappe/docs/user/index.md | 5 +- frappe/docs/user/index.txt | 3 +- frappe/docs/user/zh/__init__.py | 0 frappe/docs/user/zh/bench/__init__.py | 0 frappe/docs/user/zh/bench/guides/__init__.py | 0 .../zh/bench/guides/adding-custom-domains.md | 28 ++ .../user/zh/bench/guides/configuring-https.md | 46 +++ .../bench/guides/diagnosing-the-scheduler.md | 33 ++ frappe/docs/user/zh/bench/guides/index.md | 3 + frappe/docs/user/zh/bench/guides/index.txt | 11 + .../zh/bench/guides/lets-encrypt-ssl-setup.md | 100 ++++++ .../docs/user/zh/bench/guides/manual-setup.md | 75 +++++ .../user/zh/bench/guides/settings-limits.md | 38 +++ .../zh/bench/guides/setup-multitenancy.md | 54 ++++ .../user/zh/bench/guides/setup-production.md | 44 +++ frappe/docs/user/zh/bench/index.md | 3 + frappe/docs/user/zh/bench/index.txt | 1 + .../docs/user/zh/bench/resources/__init__.py | 0 .../zh/bench/resources/background-services.md | 24 ++ .../resources/bench-commands-cheatsheet.md | 94 ++++++ .../user/zh/bench/resources/bench-procfile.md | 31 ++ frappe/docs/user/zh/bench/resources/index.md | 3 + frappe/docs/user/zh/bench/resources/index.txt | 3 + frappe/docs/user/zh/guides/.txt | 0 frappe/docs/user/zh/guides/__init__.py | 0 .../zh/guides/app-development/__init__.py | 0 .../adding-custom-button-to-form.md | 30 ++ .../adding-module-icons-on-desktop.md | 36 +++ .../app-development/custom-module-icon.md | 23 ++ .../guides/app-development/dialogs-types.md | 121 ++++++++ .../executing-code-on-doctype-events.md | 31 ++ .../exporting-customizations.md | 15 + ...from-master-to-all-related-transactions.md | 15 + .../guides/app-development/generating-docs.md | 70 +++++ .../how-enable-developer-mode-in-frappe.md | 19 ++ ...e-custom-fields-during-app-installation.md | 22 ++ .../how-to-improve-a-standard-control.md | 104 +++++++ .../user/zh/guides/app-development/index.md | 3 + .../user/zh/guides/app-development/index.txt | 15 + .../insert-a-document-via-api.md | 55 ++++ .../overriding-link-query-by-custom-script.md | 89 ++++++ .../running-background-jobs.md | 47 +++ .../app-development/single-type-doctype.md | 11 + .../trigger-event-on-deletion-of-grid-row.md | 49 +++ .../using-html-templates-in-javascript.md | 43 +++ .../zh/guides/automated-testing/__init__.py | 0 .../user/zh/guides/automated-testing/index.md | 7 + .../zh/guides/automated-testing/index.txt | 3 + .../automated-testing/integration-testing.md | 49 +++ .../guides/automated-testing/qunit-testing.md | 75 +++++ .../guides/automated-testing/unit-testing.md | 199 ++++++++++++ frappe/docs/user/zh/guides/basics/__init__.py | 0 frappe/docs/user/zh/guides/basics/apps.md | 109 +++++++ .../user/zh/guides/basics/frappe_ajax_call.md | 78 +++++ frappe/docs/user/zh/guides/basics/hooks.md | 277 +++++++++++++++++ frappe/docs/user/zh/guides/basics/index.md | 3 + frappe/docs/user/zh/guides/basics/index.txt | 8 + frappe/docs/user/zh/guides/basics/install.md | 10 + .../docs/user/zh/guides/basics/site_config.md | 52 ++++ frappe/docs/user/zh/guides/basics/sites.md | 82 +++++ .../user/zh/guides/basics/translations.md | 88 ++++++ frappe/docs/user/zh/guides/data/__init__.py | 0 .../zh/guides/data/import-large-csv-file.md | 25 ++ frappe/docs/user/zh/guides/data/index.md | 3 + frappe/docs/user/zh/guides/data/index.txt | 2 + .../guides/data/using-data-migration-tool.md | 99 ++++++ .../user/zh/guides/deployment/__init__.py | 0 ...otifications-for-failed-background-jobs.md | 25 ++ .../deployment/how-to-enable-social-logins.md | 66 ++++ ...o-migrate-doctype-changes-to-production.md | 15 + .../docs/user/zh/guides/deployment/index.md | 5 + .../docs/user/zh/guides/deployment/index.txt | 4 + .../user/zh/guides/deployment/migrations.md | 69 +++++ frappe/docs/user/zh/guides/desk/__init__.py | 0 .../guides/desk/formatter_for_link_fields.md | 19 ++ frappe/docs/user/zh/guides/desk/index.md | 5 + .../docs/user/zh/guides/desk/making_charts.md | 3 + frappe/docs/user/zh/guides/index.md | 7 + frappe/docs/user/zh/guides/index.txt | 7 + .../user/zh/guides/integration/__init__.py | 0 .../zh/guides/integration/google_gsuite.md | 74 +++++ .../guides/integration/how_to_setup_oauth.md | 53 ++++ .../docs/user/zh/guides/integration/index.md | 3 + .../docs/user/zh/guides/integration/index.txt | 5 + .../openid_connect_and_frappe_social_login.md | 72 +++++ .../user/zh/guides/integration/rest_api.md | 285 ++++++++++++++++++ .../user/zh/guides/integration/using_oauth.md | 109 +++++++ .../user/zh/guides/integration/webhooks.md | 103 +++++++ .../user/zh/guides/portal-development/.md | 5 + .../zh/guides/portal-development/__init__.py | 0 .../guides/portal-development/adding-pages.md | 39 +++ .../zh/guides/portal-development/contents.md | 29 ++ .../zh/guides/portal-development/context.md | 26 ++ .../guides/portal-development/generators.md | 89 ++++++ .../zh/guides/portal-development/index.md | 17 ++ .../zh/guides/portal-development/index.txt | 7 + .../zh/guides/portal-development/ordering.md | 13 + .../guides/portal-development/portal-roles.md | 25 ++ .../zh/guides/portal-development/web-forms.md | 11 + .../guides/reports-and-printing/__init__.py | 0 ...n-from-another-document-in-print-format.md | 9 + .../how-to-make-query-report.md | 58 ++++ .../how-to-make-script-reports.md | 51 ++++ .../zh/guides/reports-and-printing/index.md | 3 + .../zh/guides/reports-and-printing/index.txt | 5 + .../print-format-for-reports.md | 70 +++++ .../where-do-i-find-standard-print-formats.md | 42 +++ frappe/docs/user/zh/index.md | 5 + frappe/docs/user/zh/index.txt | 4 + frappe/docs/user/zh/tutorial/__init__.py | 0 frappe/docs/user/zh/tutorial/app.md | 9 + frappe/docs/user/zh/tutorial/before.md | 78 +++++ frappe/docs/user/zh/tutorial/bench.md | 11 + frappe/docs/user/zh/tutorial/conclusion.md | 7 + frappe/docs/user/zh/tutorial/controllers.md | 59 ++++ .../tutorial/doctype-directory-structure.md | 31 ++ frappe/docs/user/zh/tutorial/doctypes.md | 96 ++++++ .../user/zh/tutorial/form-client-scripting.md | 39 +++ frappe/docs/user/zh/tutorial/index.md | 33 ++ frappe/docs/user/zh/tutorial/index.txt | 19 ++ frappe/docs/user/zh/tutorial/models.md | 19 ++ .../user/zh/tutorial/naming-and-linking.md | 71 +++++ frappe/docs/user/zh/tutorial/new-app.md | 55 ++++ frappe/docs/user/zh/tutorial/reports.md | 7 + frappe/docs/user/zh/tutorial/roles.md | 14 + .../user/zh/tutorial/setting-up-the-site.md | 67 ++++ .../docs/user/zh/tutorial/single-doctypes.md | 9 + frappe/docs/user/zh/tutorial/start.md | 31 ++ frappe/docs/user/zh/tutorial/task-runner.md | 94 ++++++ .../user/zh/tutorial/users-and-records.md | 55 ++++ frappe/docs/user/zh/tutorial/web-views.md | 65 ++++ frappe/docs/user/zh/videos/__init__.py | 0 frappe/docs/user/zh/videos/index.md | 9 + 133 files changed, 4879 insertions(+), 2 deletions(-) create mode 100644 frappe/docs/user/zh/__init__.py create mode 100644 frappe/docs/user/zh/bench/__init__.py create mode 100644 frappe/docs/user/zh/bench/guides/__init__.py create mode 100755 frappe/docs/user/zh/bench/guides/adding-custom-domains.md create mode 100755 frappe/docs/user/zh/bench/guides/configuring-https.md create mode 100755 frappe/docs/user/zh/bench/guides/diagnosing-the-scheduler.md create mode 100644 frappe/docs/user/zh/bench/guides/index.md create mode 100755 frappe/docs/user/zh/bench/guides/index.txt create mode 100755 frappe/docs/user/zh/bench/guides/lets-encrypt-ssl-setup.md create mode 100755 frappe/docs/user/zh/bench/guides/manual-setup.md create mode 100644 frappe/docs/user/zh/bench/guides/settings-limits.md create mode 100755 frappe/docs/user/zh/bench/guides/setup-multitenancy.md create mode 100644 frappe/docs/user/zh/bench/guides/setup-production.md create mode 100644 frappe/docs/user/zh/bench/index.md create mode 100644 frappe/docs/user/zh/bench/index.txt create mode 100644 frappe/docs/user/zh/bench/resources/__init__.py create mode 100755 frappe/docs/user/zh/bench/resources/background-services.md create mode 100755 frappe/docs/user/zh/bench/resources/bench-commands-cheatsheet.md create mode 100755 frappe/docs/user/zh/bench/resources/bench-procfile.md create mode 100644 frappe/docs/user/zh/bench/resources/index.md create mode 100644 frappe/docs/user/zh/bench/resources/index.txt create mode 100755 frappe/docs/user/zh/guides/.txt create mode 100644 frappe/docs/user/zh/guides/__init__.py create mode 100644 frappe/docs/user/zh/guides/app-development/__init__.py create mode 100644 frappe/docs/user/zh/guides/app-development/adding-custom-button-to-form.md create mode 100755 frappe/docs/user/zh/guides/app-development/adding-module-icons-on-desktop.md create mode 100755 frappe/docs/user/zh/guides/app-development/custom-module-icon.md create mode 100755 frappe/docs/user/zh/guides/app-development/dialogs-types.md create mode 100755 frappe/docs/user/zh/guides/app-development/executing-code-on-doctype-events.md create mode 100644 frappe/docs/user/zh/guides/app-development/exporting-customizations.md create mode 100755 frappe/docs/user/zh/guides/app-development/fetch-custom-field-value-from-master-to-all-related-transactions.md create mode 100755 frappe/docs/user/zh/guides/app-development/generating-docs.md create mode 100755 frappe/docs/user/zh/guides/app-development/how-enable-developer-mode-in-frappe.md create mode 100755 frappe/docs/user/zh/guides/app-development/how-to-create-custom-fields-during-app-installation.md create mode 100755 frappe/docs/user/zh/guides/app-development/how-to-improve-a-standard-control.md create mode 100755 frappe/docs/user/zh/guides/app-development/index.md create mode 100755 frappe/docs/user/zh/guides/app-development/index.txt create mode 100755 frappe/docs/user/zh/guides/app-development/insert-a-document-via-api.md create mode 100755 frappe/docs/user/zh/guides/app-development/overriding-link-query-by-custom-script.md create mode 100644 frappe/docs/user/zh/guides/app-development/running-background-jobs.md create mode 100755 frappe/docs/user/zh/guides/app-development/single-type-doctype.md create mode 100755 frappe/docs/user/zh/guides/app-development/trigger-event-on-deletion-of-grid-row.md create mode 100755 frappe/docs/user/zh/guides/app-development/using-html-templates-in-javascript.md create mode 100644 frappe/docs/user/zh/guides/automated-testing/__init__.py create mode 100644 frappe/docs/user/zh/guides/automated-testing/index.md create mode 100644 frappe/docs/user/zh/guides/automated-testing/index.txt create mode 100644 frappe/docs/user/zh/guides/automated-testing/integration-testing.md create mode 100644 frappe/docs/user/zh/guides/automated-testing/qunit-testing.md create mode 100755 frappe/docs/user/zh/guides/automated-testing/unit-testing.md create mode 100644 frappe/docs/user/zh/guides/basics/__init__.py create mode 100755 frappe/docs/user/zh/guides/basics/apps.md create mode 100644 frappe/docs/user/zh/guides/basics/frappe_ajax_call.md create mode 100755 frappe/docs/user/zh/guides/basics/hooks.md create mode 100755 frappe/docs/user/zh/guides/basics/index.md create mode 100755 frappe/docs/user/zh/guides/basics/index.txt create mode 100755 frappe/docs/user/zh/guides/basics/install.md create mode 100755 frappe/docs/user/zh/guides/basics/site_config.md create mode 100755 frappe/docs/user/zh/guides/basics/sites.md create mode 100755 frappe/docs/user/zh/guides/basics/translations.md create mode 100644 frappe/docs/user/zh/guides/data/__init__.py create mode 100755 frappe/docs/user/zh/guides/data/import-large-csv-file.md create mode 100755 frappe/docs/user/zh/guides/data/index.md create mode 100755 frappe/docs/user/zh/guides/data/index.txt create mode 100644 frappe/docs/user/zh/guides/data/using-data-migration-tool.md create mode 100644 frappe/docs/user/zh/guides/deployment/__init__.py create mode 100755 frappe/docs/user/zh/guides/deployment/email-notifications-for-failed-background-jobs.md create mode 100755 frappe/docs/user/zh/guides/deployment/how-to-enable-social-logins.md create mode 100755 frappe/docs/user/zh/guides/deployment/how-to-migrate-doctype-changes-to-production.md create mode 100755 frappe/docs/user/zh/guides/deployment/index.md create mode 100755 frappe/docs/user/zh/guides/deployment/index.txt create mode 100755 frappe/docs/user/zh/guides/deployment/migrations.md create mode 100644 frappe/docs/user/zh/guides/desk/__init__.py create mode 100644 frappe/docs/user/zh/guides/desk/formatter_for_link_fields.md create mode 100755 frappe/docs/user/zh/guides/desk/index.md create mode 100644 frappe/docs/user/zh/guides/desk/making_charts.md create mode 100755 frappe/docs/user/zh/guides/index.md create mode 100755 frappe/docs/user/zh/guides/index.txt create mode 100644 frappe/docs/user/zh/guides/integration/__init__.py create mode 100644 frappe/docs/user/zh/guides/integration/google_gsuite.md create mode 100644 frappe/docs/user/zh/guides/integration/how_to_setup_oauth.md create mode 100755 frappe/docs/user/zh/guides/integration/index.md create mode 100755 frappe/docs/user/zh/guides/integration/index.txt create mode 100644 frappe/docs/user/zh/guides/integration/openid_connect_and_frappe_social_login.md create mode 100755 frappe/docs/user/zh/guides/integration/rest_api.md create mode 100644 frappe/docs/user/zh/guides/integration/using_oauth.md create mode 100644 frappe/docs/user/zh/guides/integration/webhooks.md create mode 100755 frappe/docs/user/zh/guides/portal-development/.md create mode 100644 frappe/docs/user/zh/guides/portal-development/__init__.py create mode 100755 frappe/docs/user/zh/guides/portal-development/adding-pages.md create mode 100755 frappe/docs/user/zh/guides/portal-development/contents.md create mode 100755 frappe/docs/user/zh/guides/portal-development/context.md create mode 100644 frappe/docs/user/zh/guides/portal-development/generators.md create mode 100755 frappe/docs/user/zh/guides/portal-development/index.md create mode 100755 frappe/docs/user/zh/guides/portal-development/index.txt create mode 100755 frappe/docs/user/zh/guides/portal-development/ordering.md create mode 100644 frappe/docs/user/zh/guides/portal-development/portal-roles.md create mode 100644 frappe/docs/user/zh/guides/portal-development/web-forms.md create mode 100644 frappe/docs/user/zh/guides/reports-and-printing/__init__.py create mode 100755 frappe/docs/user/zh/guides/reports-and-printing/getting-information-from-another-document-in-print-format.md create mode 100755 frappe/docs/user/zh/guides/reports-and-printing/how-to-make-query-report.md create mode 100755 frappe/docs/user/zh/guides/reports-and-printing/how-to-make-script-reports.md create mode 100755 frappe/docs/user/zh/guides/reports-and-printing/index.md create mode 100755 frappe/docs/user/zh/guides/reports-and-printing/index.txt create mode 100755 frappe/docs/user/zh/guides/reports-and-printing/print-format-for-reports.md create mode 100755 frappe/docs/user/zh/guides/reports-and-printing/where-do-i-find-standard-print-formats.md create mode 100755 frappe/docs/user/zh/index.md create mode 100755 frappe/docs/user/zh/index.txt create mode 100644 frappe/docs/user/zh/tutorial/__init__.py create mode 100755 frappe/docs/user/zh/tutorial/app.md create mode 100755 frappe/docs/user/zh/tutorial/before.md create mode 100755 frappe/docs/user/zh/tutorial/bench.md create mode 100755 frappe/docs/user/zh/tutorial/conclusion.md create mode 100755 frappe/docs/user/zh/tutorial/controllers.md create mode 100755 frappe/docs/user/zh/tutorial/doctype-directory-structure.md create mode 100755 frappe/docs/user/zh/tutorial/doctypes.md create mode 100755 frappe/docs/user/zh/tutorial/form-client-scripting.md create mode 100755 frappe/docs/user/zh/tutorial/index.md create mode 100755 frappe/docs/user/zh/tutorial/index.txt create mode 100755 frappe/docs/user/zh/tutorial/models.md create mode 100755 frappe/docs/user/zh/tutorial/naming-and-linking.md create mode 100755 frappe/docs/user/zh/tutorial/new-app.md create mode 100755 frappe/docs/user/zh/tutorial/reports.md create mode 100755 frappe/docs/user/zh/tutorial/roles.md create mode 100755 frappe/docs/user/zh/tutorial/setting-up-the-site.md create mode 100755 frappe/docs/user/zh/tutorial/single-doctypes.md create mode 100755 frappe/docs/user/zh/tutorial/start.md create mode 100755 frappe/docs/user/zh/tutorial/task-runner.md create mode 100755 frappe/docs/user/zh/tutorial/users-and-records.md create mode 100755 frappe/docs/user/zh/tutorial/web-views.md create mode 100644 frappe/docs/user/zh/videos/__init__.py create mode 100755 frappe/docs/user/zh/videos/index.md diff --git a/frappe/docs/user/index.md b/frappe/docs/user/index.md index 91ceae5836..26a0f204d2 100644 --- a/frappe/docs/user/index.md +++ b/frappe/docs/user/index.md @@ -5,4 +5,7 @@ Select your language 1. [English](/docs/user/en) 1. [Français](/docs/user/fr) 1. [Português](/docs/user/pt) -1. [Español](/docs/user/es) \ No newline at end of file +1. [Español](/docs/user/es) +1. [简体中文](/docs/user/zh) + + diff --git a/frappe/docs/user/index.txt b/frappe/docs/user/index.txt index d289cda85c..ae8ed6f5cd 100644 --- a/frappe/docs/user/index.txt +++ b/frappe/docs/user/index.txt @@ -1,4 +1,5 @@ en fr pt -es \ No newline at end of file +es +zh \ No newline at end of file diff --git a/frappe/docs/user/zh/__init__.py b/frappe/docs/user/zh/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/bench/__init__.py b/frappe/docs/user/zh/bench/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/bench/guides/__init__.py b/frappe/docs/user/zh/bench/guides/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/bench/guides/adding-custom-domains.md b/frappe/docs/user/zh/bench/guides/adding-custom-domains.md new file mode 100755 index 0000000000..ac232e6265 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/adding-custom-domains.md @@ -0,0 +1,28 @@ +# 为站点添加自定义域名 + +你可以为站点添加 **多个自定义域名**,只需运行: + + bench setup add-domain [desired-domain] + +在运行该命令时,将询问你要为哪个站点设置自定义域名。 + +你可以使用以下选项为自定义域名设置 SSL: + + --ssl-certificate [path-to-certificate] + --ssl-certificate-key [path-to-certificate-key] + +例如: + + bench setup add-domain custom.erpnext.com --ssl-certificate /etc/letsencrypt/live/erpnext.cert --ssl-certificate-key /etc/letsencrypt/live/erpnext.key + +域名配置保存在各站点自己的 site_config.json 配置文件里: + + "domains": [ + { + "ssl_certificate": "/etc/letsencrypt/live/erpnext.cert", + "domain": "erpnext.com", + "ssl_certificate_key": "/etc/letsencrypt/live/erpnext.key" + } + ], + +**你需要通过运行 `bench setup nginx` 重新生成 Nginx 配置,并重新加载 Nginx 服务使你的自定义域名生效** \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/guides/configuring-https.md b/frappe/docs/user/zh/bench/guides/configuring-https.md new file mode 100755 index 0000000000..82bb31d388 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/configuring-https.md @@ -0,0 +1,46 @@ +# 配置 HTTPS + +### 获取必要的文件 + +你可以从受信任的证书颁发机构获得 SSL 证书或生成自己的证书。对于自签名证书,浏览器将显示一个 “该证书不受信任” 的警告。[这里是通过 Let's Encrypt 获取免费 SSL 证书的教程](lets-encrypt-ssl-setup.html) + +这些必要的文件包括: + +* 证书 (通常后缀名为 .crt) +* 解密的私钥 + +如果你有多个证书(初级和中级),你将需要将它们连接起来。例如, + + cat your_certificate.crt CA.crt >> certificate_bundle.crt + +还要确保你的私钥不可读。一般来说,它只有 root 才能是所有者和可读 + + chown root private.key + chmod 600 private.key + +### 将两个文件移动到适当的位置 + + mkdir /etc/nginx/conf.d/ssl + mv private.key /etc/nginx/conf.d/ssl/private.key + mv certificate_bundle.crt /etc/nginx/conf.d/ssl/certificate_bundle.crt + +### 设置 Nginx 配置 + +为你的站点设置证书和私钥的路径 + + bench set-ssl-certificate site1.local /etc/nginx/conf.d/ssl/certificate_bundle.crt + bench set-ssl-key site1.local /etc/nginx/conf.d/ssl/private.key + +### 生成 Nginx 配置 + + bench setup nginx + +### 重启 Nginx + + sudo service nginx reload + +或 + + systemctl reload nginx # for CentOS 7 + +现在你已完成了 SSL 的配置,所有 HTTP 通信都将重定向到 HTTPS \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/guides/diagnosing-the-scheduler.md b/frappe/docs/user/zh/bench/guides/diagnosing-the-scheduler.md new file mode 100755 index 0000000000..68150dc702 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/diagnosing-the-scheduler.md @@ -0,0 +1,33 @@ +# 诊断计划任务 + + + +如果你在计划任务中遇到延迟,或者似乎无法运行,可以运行几个命令来诊断问题。 + +### `bench doctor` + +这将按顺序给出如下输出: +- 各站点计划任务状态 +- 执行单元 (Workers) 数量 +- 待处理任务 + + +预期输出: + + Workers online: 0 + -----None Jobs----- + +### `bench --site [site-name] show-pending-jobs` + +这将按顺序给出如下输出: +- 队列 +- 队列任务 + +预期输出: + + -----Pending Jobs----- + + +### `bench purge-jobs` + +这将从所有队列中删除全部待处理的任务 \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/guides/index.md b/frappe/docs/user/zh/bench/guides/index.md new file mode 100644 index 0000000000..94035fe8b1 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/index.md @@ -0,0 +1,3 @@ +# 指南 + +{index} \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/guides/index.txt b/frappe/docs/user/zh/bench/guides/index.txt new file mode 100755 index 0000000000..4f99377c88 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/index.txt @@ -0,0 +1,11 @@ +configuring-https +lets-encrypt-ssl-setup +diagnosing-the-scheduler +how-to-change-host-name-from-localhost +manual-setup +setup-multitenancy +setup-production +setup-ssl +stop-production-and-start-development +updating +setting-limits \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/guides/lets-encrypt-ssl-setup.md b/frappe/docs/user/zh/bench/guides/lets-encrypt-ssl-setup.md new file mode 100755 index 0000000000..998a38e5a1 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/lets-encrypt-ssl-setup.md @@ -0,0 +1,100 @@ +# 通过 Let's Encrypt 配置 HTTPS + +## 必备条件 + +1. 你需要有 DNS 多租户 (Multitenant) 设置 +2. 你的网站应可通过有效的域名访问 +3. 你需要服务器的 root 权限 + +**注意 : Let's Encrypt 证书将每三个月到期** + +## 使用 Bench 命令 + +运行: + + sudo -H bench setup lets-encrypt [site-name] + +您将碰到几个提示,请做出相应地回应。该命令还会向用户的 crontab 添加一个任务,每月尝试更新证书。 + +### 自定义域名 + +你还可以为[自定义域名](adding-custom-domains.html)设置 Let's Encrypt。使用 `--custom-domain` 选项即可 + + sudo -H bench setup lets-encrypt [site-name] --custom-domain [custom-domain] + +### 刷新证书 + +你可以使用以下命令手工刷新证书: + + sudo bench renew-lets-encrypt + +
+ +## 手工方式 + +### 下载适当的 Certbot-auto 脚本到 /opt 目录中 + + https://certbot.eff.org/ + +### 停止 nginx 服务 + + $ sudo service nginx stop + +### 运行 Certbot + + $ ./opt/certbot-auto certonly --standalone + +在 letsencrypt 初始化后,将提示你输入一些信息。取决于你之前是否使用了 Let's Encrypt,这个提示可能会有所不同,但我们会第一时间指导你完成。 + +在提示中,输入用于通知、以及恢复丢失密钥的电子邮件地址: + +![](https://assets.digitalocean.com/articles/letsencrypt/le-email.png) + +你必须同意 Let's Encrypt 的订阅协议,选择同意: +![](https://assets.digitalocean.com/articles/letsencrypt/le-agreement.png) + +然后输入你的域名。注意,如果你希望把一个证书用到多个域名上 (例如 example.com、www.example.com) ,确保像如下那样全部包含它们: + +![](https://assets.digitalocean.com/articles/letsencrypt/le-domain.png) + +### 证书文件 + +获得证书后,你将拥有以下 PEM 编码的文件: + +* **cert.pem**: 你的域名证书 +* **chain.pem**: Let's Encrypt 链证书 +* **fullchain.pem**: 合并的 cert.pem 和 chain.pem +* **privkey.pem**: 你的证书私钥 + + +这些证书文件保存在 `/etc/letsencrypt/live/example.com` 文件夹 + +### 为你的站点配置证书 + +转到你的 erpnext 站点 site_config.json + + $ cd frappe-bench/sites/{{site_name}} + +添加以下两行到你的 site_config.json 文件中 + + "ssl_certificate": "/etc/letsencrypt/live/example.com/fullchain.pem", + "ssl_certificate_key": "/etc/letsencrypt/live/example.com/privkey.pem" + + +重新生成 Nginx 配置 + + $ bench setup nginx + +重启 Nginx 服务 + + $ sudo service nginx restart + +--- + +### 自动更新 (实验功能) + +以 root 或拥有 superuser 权限的用户身份登录,运行 `crontab -e` 并输入: + + # 每月第一个周一刷新 letsencrypt 证书,如果执行完成后将收到邮件提示 + MAILTO="mail@example.com" + 0 0 1-7 * * [ "$(date '+\%a')" = "Mon" ] && sudo service nginx stop && /opt/certbot-auto renew && sudo service nginx start diff --git a/frappe/docs/user/zh/bench/guides/manual-setup.md b/frappe/docs/user/zh/bench/guides/manual-setup.md new file mode 100755 index 0000000000..dcec15ab06 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/manual-setup.md @@ -0,0 +1,75 @@ +# 手工设置 + +手工设置 +-------------- + +安装必备组件, + +* [Python 2.7](https://www.python.org/download/releases/2.7/) +* [MariaDB](https://mariadb.org/) +* [Redis](http://redis.io/topics/quickstart) +* [WKHTMLtoPDF with patched QT](http://wkhtmltopdf.org/downloads.html) (生成 pdf 需要) + +[在 OSX 下安装必备组件](https://github.com/frappe/bench/wiki/Installing-Bench-Pre-requisites-on-MacOSX) + +以*非 root 用户*安装 bench, + + git clone https://github.com/frappe/bench bench-repo + sudo pip install -e bench-repo + +提示:请不要删除上述命令将创建的 bench 目录 + + +从现有安装迁移 +------------------------------------ + +如果想从 ERPNext 版本 3 迁移,请参照[这里](https://github.com/frappe/bench/wiki/Migrating-from-ERPNext-version-3)的说明 + +如果想从老版本的 Bench 迁移,请参照[这里](https://github.com/frappe/bench/wiki/Migrating-from-old-bench)的说明 + + +基本用法 +=========== + +* 创建新的 Bench + + 命令 init 将创建一个安装了 frappe 框架的 bench 目录。它将被设置为定期备份和每天一次的自动更新。 + + bench init frappe-bench && cd frappe-bench + +* 添加应用 + + 命令 get-app 获取并安装 frappe 应用。例如: + + - [erpnext](https://github.com/frappe/erpnext) + - [erpnext_shopify](https://github.com/frappe/erpnext_shopify) + - [paypal_integration](https://github.com/frappe/paypal_integration) + + bench get-app erpnext https://github.com/frappe/erpnext + +* 添加站点 + + Frappé 应用由 frappe 站点运行,您需要至少创建一个站点。命令 new-site 可以达到该目的: + + bench new-site site1.local + +* 启动 Bench + + 要启动 Bench,使用 `bench start` 命令 + + bench start + + 要登录 Frappé / ERPNext,打开你的浏览器输入 `localhost:8000` + + 默认用户名为 "Administrator",密码则是当你设置新站点时指定的密码。 + + +配置 ERPNext +================== + +要安装 ERPNext,只需运行: +``` +bench install-app erpnext +``` + +现在你可以使用 `bench start` 启动或[设置生产用 Bench](setup-production.html) diff --git a/frappe/docs/user/zh/bench/guides/settings-limits.md b/frappe/docs/user/zh/bench/guides/settings-limits.md new file mode 100644 index 0000000000..9251878401 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/settings-limits.md @@ -0,0 +1,38 @@ +# 为站点配置限额 + +Frappé v7 加入了对站点进行限额设置的支持。这些限额在站点文件夹内的 `site_config.json` 文件中设置, + + { + "db_name": "xxxxxxxxxx", + "db_password": "xxxxxxxxxxxx", + "limits": { + "emails": 1500, + "space": 0.157, + "expiry": "2016-07-25", + "users": 1 + } + } + +你可以运行以下命令设置限制: + + bench --site [sitename] set-limit [limit] [value] + +你也可以同时设置多个限制,运行以下命令: + + bench --site [sitename] set-limits --limit [limit] [value] --limit [limit-2] [value-2] + + 你可以设置的有效限制有: + +- **users** - 限制站点的最大用户数 +- **emails** - 限制站每月邮件的发送数量上限 +- **space** - 限制站点可以使用的最大存储空间(GB) +- **email_group** - 限制邮件群组中允许的最大成员数量 +- **expiry** - 站点的到期日期(带括号的 YYYY-MM-DD 格式) + +例如: + + bench --site site1.local set-limit users 5 + +你可以通过从工具栏/ AwesomeBar 打开 “使用信息” 页面查看使用情况。设置的限制会显示在该页面上。 + +Doctype Saved diff --git a/frappe/docs/user/zh/bench/guides/setup-multitenancy.md b/frappe/docs/user/zh/bench/guides/setup-multitenancy.md new file mode 100755 index 0000000000..e41d8200f6 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/setup-multitenancy.md @@ -0,0 +1,54 @@ +# 多租户模式配置 + +假设你已经运行了你的第一个站点,并完成了[生产环境部署](setup-production.html),这篇文章将展示如何托管你的第二个站点(或更多)。你的第一个站点自动设置为默认站点。你可以通过如下命令更改默认站点, + + bench use sitename + +基于端口的多租户模式 +----------------------- + +你可以创建新的站点并运行在不同的端口上(第一个站点运行在 80 端口)。 + + +* 关闭基于 DNS 的多租户模式 (一次即可) + + `bench config dns_multitenant off` + +* 新增站点 + + `bench new-site site2name` + +* 设置端口 + + `bench set-nginx-port site2name 82` + +* 重新生成 Nginx 配置 + + `bench setup nginx` + +* 重新加载 Nginx 服务 + + `sudo service nginx reload` + + +基于 DNS 的多租户模式 +---------------------- +将你的站点命名为主机名(hostname)即可。你的所有站点都将运行在相同的端口上并根据其主机名(hostname)自动选择。 + +基于DNS的多租户在做一个新的网站,请执行以下步骤。 + +* 开启基于 DNS 的多租户模式 (一次即可) + + `bench config dns_multitenant on` + +* 新增站点 + + `bench new-site site2name` + +* 重新生成 Nginx 配置 + + `bench setup nginx` + +* 重新加载 Nginx 服务 + + `sudo service nginx reload` diff --git a/frappe/docs/user/zh/bench/guides/setup-production.md b/frappe/docs/user/zh/bench/guides/setup-production.md new file mode 100644 index 0000000000..322fc50023 --- /dev/null +++ b/frappe/docs/user/zh/bench/guides/setup-production.md @@ -0,0 +1,44 @@ +# 生产环境部署 + +你可以通过配置 Supervisor、Nginx 来部署生产环境。如果你想把生产环境恢复为开发环境,请参考[这些命令](https://github.com/frappe/bench/wiki/Stopping-Production-and-starting-Development)。 + +#### 自动部署生产环境 +运行命令 `sudo bench setup production` 将自动完成生产环境部署。 + + +#### 手工部署生产环境 + +Supervisor +---------- + +Supervisor 确保 Frappé 系统进程保持运行并在它发生崩溃后自动重新启动。你可以使用命令 `bench setup supervisor` 生成 Supervisor 所需的配置。该配置可参考`config/supervisor.conf` 文件。你可以将该文件复制或链接到 supervisor 配置目录并重新加载它以使其生效。 + +例如, + +``` +bench setup supervisor +sudo ln -s `pwd`/config/supervisor.conf /etc/supervisor/conf.d/frappe-bench.conf +``` + +注意:对于 CentOS 7, 其扩展名应是 `ini`, 因此命令变成了: + +``` +bench setup supervisor +sudo ln -s `pwd`/config/supervisor.conf /etc/supervisor/conf.d/frappe-bench.ini #for CentOS 7 only +``` + +更新 supervisor 配置后需要重启 supervisor 管理的相关进程。要自动完成它,你需要使用命令 `sudo bench setup sudoers $(whoami)` 对 sudoers 进行配置。 + +Nginx +----- + +Nginx 是一个 Web 服务器,我们用它来提供静态文件以及其他对 Frappe 请求的代理。你可以使用命令 `bench setup nginx` 生成 Supervisor 所需的配置。该配置可参考`config/nginx.conf` 文件。你可以将该文件复制或链接到 nginx 配置目录并重新加载它以使其生效。 + +例如, + +``` +bench setup nginx +sudo ln -s `pwd`/config/nginx.conf /etc/nginx/conf.d/frappe-bench.conf +``` + +注意:如果有另一个端口配置为 80 的服务存在,在你更改配置后重新启动 Nginx 可能失败(多数情况下导致 Nginx 的欢迎页出现)。你需要禁用此配置。通常它们位于 `/etc/nginx/conf.d/default.conf` 和 `/etc/nginx/conf.d/default` 中。 diff --git a/frappe/docs/user/zh/bench/index.md b/frappe/docs/user/zh/bench/index.md new file mode 100644 index 0000000000..39e8cc0ffe --- /dev/null +++ b/frappe/docs/user/zh/bench/index.md @@ -0,0 +1,3 @@ +# Bench + +{index} \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/index.txt b/frappe/docs/user/zh/bench/index.txt new file mode 100644 index 0000000000..97c835454b --- /dev/null +++ b/frappe/docs/user/zh/bench/index.txt @@ -0,0 +1 @@ +guides \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/resources/__init__.py b/frappe/docs/user/zh/bench/resources/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/bench/resources/background-services.md b/frappe/docs/user/zh/bench/resources/background-services.md new file mode 100755 index 0000000000..bbea008dc3 --- /dev/null +++ b/frappe/docs/user/zh/bench/resources/background-services.md @@ -0,0 +1,24 @@ +# 后台服务 + +外部服务 +----------------- + + * MariaDB (Frappe 数据库) + * Redis (Frappe 后台执行单元(Workers)和缓存查询) + * nginx (用于生产环境部署) + * supervisor (用于生产环境部署) + +Frappé 进程 +---------------- + +* WSGI 服务 + + * 该 WSGI 服务负责相应对 frappe 的 HTTP 请求。在开发场景下 (`bench serve` 或 `bench start`) 为 Werkzeug WSGI 服务,生产场景下则使用 gunicorn (由 supervisor 自动配置)。 + +* Redis 执行单元(Workers)进程 + + * 该 Celery 执行单元进程在 Frappé 系统里运行后台任务。当 supervisor 配置为生产环境时,这些进程在运行 `bench start` 时会自动启动。 + +* 计划任务进程 + + * 计划任务进程在 Frappé 系统里运行规划的任务。当 supervisor 配置为生产环境时,该进程在运行 `bench start` 时会自动启动。 \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/resources/bench-commands-cheatsheet.md b/frappe/docs/user/zh/bench/resources/bench-commands-cheatsheet.md new file mode 100755 index 0000000000..c0708bdf29 --- /dev/null +++ b/frappe/docs/user/zh/bench/resources/bench-commands-cheatsheet.md @@ -0,0 +1,94 @@ +# Bench 命令列表 + +### 常用 + +* `bench --version` - 显示 bench 版本 +* `bench src` - 显示 bench 仓库目录 +* `bench --help` - 显示所有命令和帮助 +* `bench [command] --help` - 显示指定命令的帮助 +* `bench init [bench-name]` - 创建新的工作台(bench) (在 home 目录下运行) +* `bench --site [site-name] COMMAND` - 指定命令应用的站点 +* `bench update` - 从 bench 仓库和其他所有应用获取更新,应用补丁,重建 JS、CSS,然后执行迁移操作 + * `--pull` 获取所有应用的更新 + * `--patch` 执行所有站点的迁移 + * `--build` 重建 JS、CSS + * `--bench` 更新 bench + * `--requirements` 更新依赖 + * `--restart-supervisor` 更新后重启 supervisor 进程 + * `--upgrade` 进行主版本升级 ( 如 ERPNext 6 -> 7) + * `--no-backup` 更新前不进行备份 +* `bench restart` 重启所有 bench 服务 +* `bench backup` 备份 +* `bench backup-all-sites` 备份所有站点 + * `--with-files` 备份站点及其文件 +* `bench restore` 恢复 + * `--with-private-files` 恢复站点及私有文件 (tar 文件路径) + * `--with-public-files` 恢复站点及公共文件 (tar 文件路径) +* `bench migrate` 读取 JSON 文件并对数据库进行相应的更改 + +### 配置 +* `bench config` - 更改 bench 配置 + * `auto_update [on/off]` 启用/禁用 bench 自动更新 + * `dns_multitenant [on/off]` 启用/禁用 DNS 多租户模式 + * `http_timeout` 设置 http 超时时间 + * `restart_supervisor_on_update` 启用/禁用 更新时自动重启 supervisor + * `serve_default_site` 配置 Nginx 默认站点 + * `update_bench_on_update` 启用/禁用 bench 同步更新 +* `bench setup` - 设置组件 + * `auto-update` 为 bench 自动更新增加 cronjob 任务 + * `backups ` 为 bench 备份增加 cronjob 任务 + * `config ` 重写或生成 config.json + * `env ` 生成 bench virtualenv 环境 + * `nginx ` 生成 nginx 配置文件 + * `procfile ` 设置 bench 启动过程文件(Procfile) + * `production ` 设置 bench 为生产环境 + * `redis ` 生成 redis 缓存配置文件 + * `socketio ` 设置 socketio 服务所需的 Node 依赖环境 + * `sudoers ` 增加命令到 sudoers 列表... + * `supervisor ` 生成 supervisor 配置文件 + * `add-domain ` 增加站点自定义域名 + * `firewall ` 设置防火墙并屏蔽除 22、80、443 之外的所有端口 + * `ssh-port ` 更改 SSH 默认连接端口 + +### 开发 + +* `bench new-app [app-name]` 创建一个新的应用 +* `bench get-app [repo-link]` - 从 git 仓库下载并安装一个应用 +* `bench install-app [app-name]` 安装已有的应用 +* `bench remove-from-installed-apps [app-name]` 从应用列表中移除应用 +* `bench uninstall-app [app-name]` 删除应用及与该应用相关的一切 (须确保 Bench 在运行) +* `bench remove-app [app-name]` 从 bench 中彻底删除应用 +* `bench --site [sitename] --force reinstall ` 全新数据库重新安装 (小心:将清除老的数据库) +* `bench new-site [sitename]` - 创建一个新的站点 + * `--db-name` 数据库名称 + * `--mariadb-root-username` MariaDB 数据库 root 用户名 + * `--mariadb-root-password` MariaDB 数据库 root 密码 + * `--admin-password` 新站点的管理员密码 + * `--verbose` 显示详细信息 + * `--force` 强制恢复 (如果站点已经存在) + * `--source_sql` 使用 SQL 文件初始化数据库 + * `--install-app` 站点安装后安装应用 +* `bench use [site]` 设置默认站点 +* `bench drop-site` 从磁盘及数据库中完全移除站点 + * `--root-login` + * `--root-password` +* `bench set-config [key] [value]` 为站点配置文件增加键值对 +* `bench console` 打开 bench venv 下的 IPython 终端 +* `bench execute` 执行任何应用内的方法 + * 例如 : `bench execute frappe.utils.scheduler.enqueue_scheduler_events` +* `bench mysql` 打开 SQL 终端 +* `bench run-tests` 运行测试 + * `--app` 应用名称 + * `--doctype` 用于测试的 DocType + * `--test` 具体测试 + * `--module` 运行具有测试的特定模块 + * `--profile` 运行具有测试的 Python 过程文件 +* `bench disable-production` 禁用生产环境 + +### 计划任务 + +* `bench enable-scheduler` - 启用运行计划任务 +* `bench doctor` - 显示有关后台执行单元的诊断信息 +* `bench show-pending-jobs`- 显示未完成任务 +* `bench purge-jobs` - 销毁所有未完成任务 + diff --git a/frappe/docs/user/zh/bench/resources/bench-procfile.md b/frappe/docs/user/zh/bench/resources/bench-procfile.md new file mode 100755 index 0000000000..b33727201e --- /dev/null +++ b/frappe/docs/user/zh/bench/resources/bench-procfile.md @@ -0,0 +1,31 @@ +# Bench Procfile + +在**开发模式**下 `bench start` 使用 [honcho](http://honcho.readthedocs.org) 管理多个流程。 + +### 过程 + +运行 Frappe 所需的相关过程是: + +1. `bench start` - Web 服务 +4. `redis_cache` 用于缓存 (通常) +5. `redis_queue` 用于管理后台执行单元队列 +6. `redis_socketio` 作为来自后台执行单元的实时消息代理 +7. `web` 用于 frappe Web 服务 +7. `socketio` 用于实时消息 +3. `schedule` 用于触发定期任务 +3. `worker_*` 用于 redis 执行单元处理异步任务 + +或者,如果你在开发 Frappe,你可以添加 `bench watch` 自动创建桌面 JavaScript 应用。 + +### 例子 + + redis_cache: redis-server config/redis_cache.conf + redis_socketio: redis-server config/redis_socketio.conf + redis_queue: redis-server config/redis_queue.conf + web: bench serve --port 8000 + socketio: /usr/bin/node apps/frappe/socketio.js + watch: bench watch + schedule: bench schedule + worker_short: bench worker --queue short + worker_long: bench worker --queue long + worker_default: bench worker --queue default diff --git a/frappe/docs/user/zh/bench/resources/index.md b/frappe/docs/user/zh/bench/resources/index.md new file mode 100644 index 0000000000..23b3af3625 --- /dev/null +++ b/frappe/docs/user/zh/bench/resources/index.md @@ -0,0 +1,3 @@ +# 资源 + +{index} \ No newline at end of file diff --git a/frappe/docs/user/zh/bench/resources/index.txt b/frappe/docs/user/zh/bench/resources/index.txt new file mode 100644 index 0000000000..4674526df6 --- /dev/null +++ b/frappe/docs/user/zh/bench/resources/index.txt @@ -0,0 +1,3 @@ +background-services +bench-commands-cheatsheet +bench-procfile \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/.txt b/frappe/docs/user/zh/guides/.txt new file mode 100755 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/__init__.py b/frappe/docs/user/zh/guides/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/app-development/__init__.py b/frappe/docs/user/zh/guides/app-development/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/app-development/adding-custom-button-to-form.md b/frappe/docs/user/zh/guides/app-development/adding-custom-button-to-form.md new file mode 100644 index 0000000000..fba6397b94 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/adding-custom-button-to-form.md @@ -0,0 +1,30 @@ +# Adding Custom Button To Form + +To create a custom button on your form, you need to edit the javascript file associated to your doctype. For example, If you want to add a custom button to User form then you must edit `user.js`. + +In this file, you need to write a new method `add_custom_button` which should add a button to your form. + +#### Function Signature for `add_custom_button(...)` + frm.add_custom_button(__(buttonName), function(){ + //perform desired action such as routing to new form or fetching etc. + }, __(groupName)); + +#### Example-1: Adding a button to User form +We should edit `frappe\core\doctype\user\user.js` + + frappe.ui.form.on('User', { + refresh: function(frm) { + ... + frm.add_custom_button(__('Get User Email Address'), function(){ + frappe.msgprint(frm.doc.email); + }, __("Utilities")); + ... + } + }); + +You should be seeing a button on user form as shown below, + +Custom Button + + + diff --git a/frappe/docs/user/zh/guides/app-development/adding-module-icons-on-desktop.md b/frappe/docs/user/zh/guides/app-development/adding-module-icons-on-desktop.md new file mode 100755 index 0000000000..4fbf6fda1e --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/adding-module-icons-on-desktop.md @@ -0,0 +1,36 @@ +# Adding Module Icons On Desktop + +To create a module icon for a Page, List or Module, you will have to edit the `config/desktop.py` file in your app. + +In this file you will have to write the `get_data` method that will return a dict object with the module icon parameters + +### Example 1: Module Icon + + def get_data(): + return { + "Accounts": { + "color": "#3498db", + "icon": "octicon octicon-repo", + "type": "module" + }, + } + +### Example 2: List Icon + + def get_data(): + return { + "To Do": { + "color": "#f1c40f", + "icon": "fa fa-check", + "icon": "octicon octicon-check", + "label": _("To Do"), + "link": "List/ToDo", + "doctype": "ToDo", + "type": "list" + }, + } + + +Note: Module views are visible based on permissions. + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/app-development/custom-module-icon.md b/frappe/docs/user/zh/guides/app-development/custom-module-icon.md new file mode 100755 index 0000000000..4a001b9c2c --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/custom-module-icon.md @@ -0,0 +1,23 @@ +# Custom Module Icon + +If you want to create a custom icon for your module, you will have to create an SVG file for your module and set the path to this file in the `desktop/config.py` of your app.
+ +This icon is loaded via AJAX first time, then it will be rendered. + +Example: + + from frappe import _ + + def get_data(): + return { + "Frappé Apps": { + "color": "orange", + "icon": "assets/frappe/images/frappe.svg", + "label": _("Frappé.io Portal"), + "type": "module" + } + } + +> PS: A great place to buy SVG icons for a low cost is the awesome [Noun Project](http://thenounproject.com/) + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/app-development/dialogs-types.md b/frappe/docs/user/zh/guides/app-development/dialogs-types.md new file mode 100755 index 0000000000..52f03ebbaa --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/dialogs-types.md @@ -0,0 +1,121 @@ +# Dialogs Types + +Frappé provides a group of standard dialogs that are very useful while coding. + +## Alert Dialog + + + +Alert Dialog is used for showing non-obstructive messages. + +It has 2 parameters: + +- **txt:** The message to be shown in the `Alert Dialog` +- **seconds:** The duration that the message will be displayed. The default is `3 seconds`. + +### Example + + show_alert('Hi, do you have a new message', 5); + +--- + +## Prompt Dialog + + + +Prompt Dialog is used for collecting data from users. + +It has 4 parameters: + +- **fields:** a list with the fields objects +- **callback:** a function to process the data in the dialog +- **title:** the title of the dialog +- **primary_label:** the label of the primary button + +### Example + + frappe.prompt([ + {'fieldname': 'birth', 'fieldtype': 'Date', 'label': 'Birth Date', 'reqd': 1} + ], + function(values){ + show_alert(values, 5); + }, + 'Age verification', + 'Subscribe me' + ) + +--- +## Confirm Dialog + + + +Confirm Dialog is used to get a confirmation from the user before executing an action. + +It has 3 arguments: + +- **mesage:** The message to display in the dialog +- **onyes:** The callback on positive confirmation +- **oncancel:** The callback on negative confirmation + +### Example + + frappe.confirm( + 'Are you sure to leave this page?', + function(){ + window.close(); + }, + function(){ + show_alert('Thanks for continue here!') + } + ) + +--- + +## Message Print + + + +Message Print is used for showing information to users. + +It has 2 arguments: + +- **message:** The message to display. It can be a HTML string +- **title:** The title of the dialog + +### Example + + msgprint("Server Status" + + "
" + + "", 'Server Info') + +--- + +### Custom Dialog + + + +You can extend and build your own custom dialogs using `frappe.ui.Dialog` + +### Example + + var d = new frappe.ui.Dialog({ + 'fields': [ + {'fieldname': 'ht', 'fieldtype': 'HTML'}, + {'fieldname': 'today', 'fieldtype': 'Date', 'default': frappe.datetime.nowdate()} + ], + primary_action: function(){ + d.hide(); + show_alert(d.get_values()); + } + }); + d.fields_dict.ht.$wrapper.html('Hello World'); + d.show(); + + + + + diff --git a/frappe/docs/user/zh/guides/app-development/executing-code-on-doctype-events.md b/frappe/docs/user/zh/guides/app-development/executing-code-on-doctype-events.md new file mode 100755 index 0000000000..ff03f85236 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/executing-code-on-doctype-events.md @@ -0,0 +1,31 @@ +# Executing Code On Doctype Events + +To execute code when a DocType is inserted, validated (before saving), updated, submitted, cancelled, deleted, you must write in the DocType's controller module. + +#### 1. Controller Module + +The controller module exists in the `doctype` folder in the Module of the `DocType`. For example, the controller for **ToDo** exists in `frappe/desk/doctype/todo/todo.py` (version 5). A controller template is created when the DocType is created. which looks like + + from __future__ import unicode_literals + + import frappe + from frappe.model.document import Document + + class CustomType(Document): + pass + +#### 2. Document Properties + +All the fields and child tables are available to the class as attributes. For example the **name** property is `self.name` + +#### 3. Adding Methods + +In this module, you can add standard methods to the class that are called when a document of that type is created. Standard Handlers are: + +1. `autoname`: Called while naming. You can set the `self.name` property in the method. +1. `before_insert`: Called before a document is inserted. +1. `validate`: Called before document is saved. You can throw an exception if you don't want the document to be saved +1. `on_update`: Called after the document is inserted or updated in the database. +1. `on_submit`: Called after submission. +1. `on_cancel`: Called after cancellation. +1. `on_trash`: Called after document is deleted. diff --git a/frappe/docs/user/zh/guides/app-development/exporting-customizations.md b/frappe/docs/user/zh/guides/app-development/exporting-customizations.md new file mode 100644 index 0000000000..710db1d3e5 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/exporting-customizations.md @@ -0,0 +1,15 @@ +# Exporting Customizations to your App + +A common use case is to extend a DocType via Custom Fields and Property Setters for a particular app. To save these settings to an app, go to **Customize Form** + +You will see a button for **Export Customizations** + + + +Here you can select the module and whether you want these particular customizations to be synced after every update. + +The customizations will be exported to a new folder `custom` in the module folder of your app. The customizations will be saved by the name of the DocType + + + +When you do `bench update` or `bench migrate` these customizations will be synced to the app. \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/app-development/fetch-custom-field-value-from-master-to-all-related-transactions.md b/frappe/docs/user/zh/guides/app-development/fetch-custom-field-value-from-master-to-all-related-transactions.md new file mode 100755 index 0000000000..7e47d63f5f --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/fetch-custom-field-value-from-master-to-all-related-transactions.md @@ -0,0 +1,15 @@ +# Fetch a Field Value from a Document into a Transaction + +Let's say, there is a custom field "VAT Number" in Supplier, which should be fetched in Purchase Order. + +#### Steps: + +1. Create a Custom Field **VAT Number** for *Supplier* document with *Field Type* as **Data**. + + +1. Create another Custom Field **VAT Number** for *Purchase Order* document, but in this case with *Field Type* as **Read Only** or check **Read Only** checkbox. Set the **Options** as `supplier.vat_number`. + + +1. Go to the user menu and click "Reload". +1. Now, on selection of Supplier in a new Purchase Order, **VAT Number** will be fetched automatically from the selected Supplier. + diff --git a/frappe/docs/user/zh/guides/app-development/generating-docs.md b/frappe/docs/user/zh/guides/app-development/generating-docs.md new file mode 100755 index 0000000000..3f08c68ec9 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/generating-docs.md @@ -0,0 +1,70 @@ +# Generating Documentation Website for your App + +Frappé version 6.7 onwards includes a full-blown documentation generator so that you can easily create a website for your app that has both user docs and developers docs (auto-generated). + +Version 8.7 onwards, these will be generated in a target app. + +## Writing Docs + +### 1. Setting up docs + +The first step is to setup the docs folder. For that you must create a new file in your app `config/docs.py` if it is not auto-generated. In your `docs.py` file, add the following module properties. + + + source_link = "https://github.com/[orgname]/[reponame]" + headline = "This is what my app does" + sub_heading = "Slightly more details with key features" + long_description = """(long description in markdown)""" + + def get_context(context): + # optional settings + + # context.brand_html = 'Brand info on the top left' + # context.favicon = 'path to favicon' + # + # context.top_bar_items = [ + # {"label": "About", "url": context.docs_base_url + "/about"}, + # ] + + pass + +### 2. Add User Documentation + +To add user documentation, add folders and pages in your `/docs/user` folder in the same way you would build a website pages in the `www` folder. + +Some quick tips: + +1. Add your pages as `.md` or `.html` pages +2. Optionally add `.css` files with the same name that will be automatically served +3. Add index by adding `{index}` + +### 3. Linking + +While linking make sure you add `/docs` to all your links. + + + {% raw %}Link Description{% endraw %} + + +### 4. Adding Images + +You can add images in the `/docs/assets` folder. You can add links to the images as follows: + + {% raw %}{% endraw %} + +--- + + +## Building Docs + +You must create a new app that will have the output of the docs, which is called the "target" app. For example, the docs for ERPNext are hosted at erpnext.org, which is based on the app "foundation". You can create a new app just to push docs of any other app. + +To output docs to another app, + + bench --site [site] build-docs [app] --target [target_app] + +This will create a new folder `/docs` inside the `www` folder of the target app and generate automatic docs (from code), model references and copy user docs and assets. + +To view the docs, just go the the `/docs` url on your target app. Example: + + https://erpnext.org/docs diff --git a/frappe/docs/user/zh/guides/app-development/how-enable-developer-mode-in-frappe.md b/frappe/docs/user/zh/guides/app-development/how-enable-developer-mode-in-frappe.md new file mode 100755 index 0000000000..24a3283a9f --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/how-enable-developer-mode-in-frappe.md @@ -0,0 +1,19 @@ +# How Enable Developer Mode In Frappé + +When you are in application design mode and you want the changes in your DocTypes, Reports etc to affect the app repository, you must be in **Developer Mode**. + +To enable developer mode, update the `site_config.json` file of your site in the sites folder for example: + + frappe-bench/sites/site1/site_config.json + +Add this to the JSON object + + "developer_mode": 1 + +After setting developer mode, clear the cache: + + $ bench clear-cache + +To view the full developer options, you must be logged in as the "Administrator" user. + + diff --git a/frappe/docs/user/zh/guides/app-development/how-to-create-custom-fields-during-app-installation.md b/frappe/docs/user/zh/guides/app-development/how-to-create-custom-fields-during-app-installation.md new file mode 100755 index 0000000000..0b71521116 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/how-to-create-custom-fields-during-app-installation.md @@ -0,0 +1,22 @@ +# How To Create Custom Fields During App Installation + +Your custom app can automatically add **Custom Fields** to DocTypes outside of your app when it is installed to a new site. + +To do this, add the new custom fields that your app requires, using the Frappé web application. + +In your `hooks.py` file, add `"Custom Fields"` + + fixtures = ["Custom Field"] + +Export fixtures before you commit your app with: + + $ bench --site mysite export-fixtures + +This will create a new folder called `fixtures` in your app folder and a `.csv` or `.json` file will be created with the definition of the custom fields you added. + +This file will be automatically imported when the app is installed in a new site or updated via `bench update`. + +Note: You can also add single DocTypes like "Website Settings" as fixtures + + + diff --git a/frappe/docs/user/zh/guides/app-development/how-to-improve-a-standard-control.md b/frappe/docs/user/zh/guides/app-development/how-to-improve-a-standard-control.md new file mode 100755 index 0000000000..df137db2a9 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/how-to-improve-a-standard-control.md @@ -0,0 +1,104 @@ +# How To Improve A Standard Control + +Frappé has a couple of elegant and useful widgets, but some times we need to edit them to add small improvements. This small article will describe how to add new resources to the standard widgets. + +Let me explain first our goal: + +> Add `many` alternative translations in `numerous records` and in a `lot of doctypes` + +Look the highlighted sections in the __goal__, we have _many translations_ to add in _many records_ and in _many doctypes_, so, we heave a **many of work**, so we have a lot to do right? + +The answer for this question is: _-Of course not! Because we know that if one element exists in many records and in many doctypes, this element is the `Control` or `Widget`_ + +So, what we need do, is improve your goal based on the `Control`, to reduce our quantity of work. + +But, where will we find this magic element, the control? _-For now, we can look it in the JavaScript sources - let's look now at [Github](https://github.com/frappe/frappe/blob/develop/frappe/public/js/frappe/form/control.js#L13)_ + +> Don't worry if you don't understand the code for now, our goal there is simplify our work. + +Let's go ahead with the thought! + +We know where we need to make the changes, but how will we dismember which are the controls that are affected by our feature and which aren't ? + +We need to keep in mind, that `Control` are instance of `DocFields` and the `DocFields` have a field that is very important for us in this case, the field that will help us to dismember which are affected by our feature and which aren't is the field `options` in the `DocField`. + +_-Wait!, we understood that the field `options` can help us, but, how will it help us?_ + +Good question, we will define a word to put in the `options` of the `DocFields` that we need to include the feature, this world will be **`Translatable`.** + +> If you forget how to customize the options of a field look [this article](https://kb.erpnext.com/kb/customize/creating-custom-link-field), it can refresh your knowledge. + +Well, with the defined word in `options` of our selected `DocFields`, now is time to code: + +_-At last, we think we would never stop talking!_ + + frappe.ui.form.ControlData = frappe.ui.form.ControlData.$extend({ + make_input: function(){ + var options = this.df.options; + if (!options || options!=="Translatable"){ + this._super(); + return; + } + var me = this; + $('').prependTo(this.input_area); + this.$input_area = $(this.input_area); + this.$input = this.$input_area.find('input'); + this.$btn = this.$input_area.find('.dialog-btn'); + this.set_input_attributes(); + this.$input.on("focus", function(){ + me.$btn.toggle(true); + }); + this.$input.on("blur", function(){ + setTimeout(function(){ me.$btn.toggle(false) }, 500); + }); + this.input = $this.input.get(0); + this.has_input = true; + var me = this; + this.setup_button(); + }, + setup_button: function(){ + var me = this; + if (this.only_input){ + this.$btn.remove(); + return; + } + this.$btn.on("click", function(){ + var value = me.get_value(); + var options = me.df.options; + if (value && options && options==="Translatable"){ + this.open_dialog(); + } + }); + }, + open_dialog: function(){ + var doc = this.doc; + if (!doc.__unsaved){ + new frappe.ui.form.TranslationSelector({ + doc: doc, + df: this.doc, + text: this.value + }); + } + } + }); + +_-Other letter soup, for my gosh!_ + +In fact, it IS a soup of letters, for a newbie, but we are not a beginner. + +Let me explain what this code does; + + - At line 1 the code overrides the `ControlData` by one extended `Class` of itself. + - The method `make_input` checks if the docfield is **`Translatable`** to make the new `Control` if not, it calls the *original* `make_input` using `_super()` + - The method `setup_button` checks if the docfield is **`Translatable`** to enable it show a `dialog` + - The method `open_dialog` invokes a new instance of the `TranslationSelector` that we will create in the code below. + + + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/app-development/index.md b/frappe/docs/user/zh/guides/app-development/index.md new file mode 100755 index 0000000000..b04ae83043 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/index.md @@ -0,0 +1,3 @@ +# App Development + +{index} diff --git a/frappe/docs/user/zh/guides/app-development/index.txt b/frappe/docs/user/zh/guides/app-development/index.txt new file mode 100755 index 0000000000..5a9b4cc4a9 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/index.txt @@ -0,0 +1,15 @@ +adding-module-icons-on-desktop +custom-module-icon +generating-docs +how-enable-developer-mode-in-frappe +fetch-custom-field-value-from-master-to-all-related-transactions +executing-code-on-doctype-events +exporting-customizations +how-to-create-custom-fields-during-app-installation +insert-a-document-via-api +how-to-improve-a-standard-control +overriding-link-query-by-custom-script +single-type-doctype +trigger-event-on-deletion-of-grid-row +dialogs-types +using-html-templates-in-javascript diff --git a/frappe/docs/user/zh/guides/app-development/insert-a-document-via-api.md b/frappe/docs/user/zh/guides/app-development/insert-a-document-via-api.md new file mode 100755 index 0000000000..c3a8358ddb --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/insert-a-document-via-api.md @@ -0,0 +1,55 @@ +# Insert A Document Via Api + +You can insert documents via a script using the `frappe.get_doc` method + +### Examples: + +#### 1. Insert a ToDo + + todo = frappe.get_doc({"doctype":"ToDo", "description": "test"}) + todo.insert() + +--- + +#### 2. Insert without the user's permissions being checked: + + todo = frappe.get_doc({"doctype":"ToDo", "description": "test"}) + todo.insert(ignore_permissions = True) + + +--- + +#### 3. Submit after inserting + + todo = frappe.get_doc({"doctype":"ToDo", "description": "test"}) + todo.insert(ignore_permissions=True) + todo.submit() + +--- + +#### 4. Insert a document on saving of another document + + class MyType(Document): + def on_update(self): + todo = frappe.get_doc({"doctype":"ToDo", "description": "test"}) + todo.insert() + +---- + +#### 5. Insert a document with child tables: + + sales_order = frappe.get_doc({ + "doctype": "Sales Order", + "company": "_Test Company", + "customer": "_Test Customer", + "delivery_date": "2013-02-23", + "sales_order_details": [ + { + "item_code": "_Test Item Home Desktop 100", + "qty": 10.0, + "rate": 100.0, + "warehouse": "_Test Warehouse - _TC" + } + ] + }) + sales_order.insert() diff --git a/frappe/docs/user/zh/guides/app-development/overriding-link-query-by-custom-script.md b/frappe/docs/user/zh/guides/app-development/overriding-link-query-by-custom-script.md new file mode 100755 index 0000000000..4e78d7fd58 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/overriding-link-query-by-custom-script.md @@ -0,0 +1,89 @@ +# Overriding Link Query By Custom Script + +You can override the standard link query by using `set_query` + +### 1. Adding Fitlers + +You can add filters to the query: + + frappe.ui.form.on("Bank Reconciliation", "onload", function(frm) { + cur_frm.set_query("bank_account", function() { + return { + "filters": { + "account_type": "Bank", + "group_or_ledger": "Ledger" + } + }; + }); + }); + +A more complex query: + + frappe.ui.form.on("Bank Reconciliation", "onload", function(frm){ + cur_frm.set_query("bank_account", function(){ + return { + "filters": [ + ["Bank Account": "account_type", "=", "Bank"], + ["Bank Account": "group_or_ledger", "!=", "Group"] + ] + } + }); + }); + +--- + +### 2. Calling a Different Method to Generate Results + +You can also set a server side method to be called on the query: + + frm.set_query("item_code", "items", function() { + return { + query: "erpnext.controllers.queries.item_query", + filters: frm.doc.enquiry_type === "Maintenance" ? + {"is_service_item": "Yes"} : {"is_sales_item": "Yes"} + }; + }); + + + +#### Custom Method + +The custom method should return a list of items for auto select. If you want to send additional data, you can send multiple columns in the list. + +Parameters to the custom method are: + +`def custom_query(doctype, txt, searchfield, start, page_len, filters)` + +**Example:** + + # searches for leads which are not converted + def lead_query(doctype, txt, searchfield, start, page_len, filters): + return frappe.db.sql("""select name, lead_name, company_name from `tabLead` + where docstatus < 2 + and ifnull(status, '') != 'Converted' + and ({key} like %(txt)s + or lead_name like %(txt)s + or company_name like %(txt)s) + {mcond} + order by + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), + if(locate(%(_txt)s, lead_name), locate(%(_txt)s, lead_name), 99999), + if(locate(%(_txt)s, company_name), locate(%(_txt)s, company_name), 99999), + name, lead_name + limit %(start)s, %(page_len)s""".format(**{ + 'key': searchfield, + 'mcond':get_match_cond(doctype) + }), { + 'txt': "%%%s%%" % txt, + '_txt': txt.replace("%", ""), + 'start': start, + 'page_len': page_len + }) + + + +For more examples see: + +[https://github.com/frappe/erpnext/blob/develop/erpnext/controllers/queries.py](https://github.com/frappe/erpnext/blob/develop/erpnext/controllers/queries.py) + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/app-development/running-background-jobs.md b/frappe/docs/user/zh/guides/app-development/running-background-jobs.md new file mode 100644 index 0000000000..f46349d36f --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/running-background-jobs.md @@ -0,0 +1,47 @@ +# Running Background Jobs + +Sometimes you may not want a user request to be executed immediately but added to a queue that will be executed by a background worker. The advantage of doing this is that your web workers remain free to execute other requests and longer jobs do not eat up all of your resources. + +From version 7, Frappé uses Python RQ to run background jobs. + +To enqueue a job, + + from frappe.jobs.background_jobs import enqueue + + def long_job(arg1, arg2): + frappe.publish_realtime('msgprint', 'Starting long job...') + # this job takes a long time to process + frappe.publish_realtime('msgprint', 'Ending long job...') + + def enqueue_long_job(arg1, args2): + enqueue('myapp.mymodule.long_job', arg1=arg1, arg2=arg2) + +This will enqueue to the queue `default` + +other queues are `worker_long` and `worker_short` + +#### Called delayed actions on Document objects + +You can also called delayed actions on document objects, for example in Stock Reconciliation if there are more than 100 items, it is executed as a background job. + +Example: you can call `doc.queue_action('submit')` + +Note: This only works for `save`, `submit`, `cancel` + +You can also push certain actions to the background if you anticipate the execution is very large. + +For example: + + def submit(self): + if len(self.items) > 100: + self.queue_action('submit') + else: + self._submit() + +#### Debugging + +If you are on `bench start` + +You will see logs in your terminal. + +Note: default worker does not auto restart, so you will have to kill bench and start again after you make changes. \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/app-development/single-type-doctype.md b/frappe/docs/user/zh/guides/app-development/single-type-doctype.md new file mode 100755 index 0000000000..3565246506 --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/single-type-doctype.md @@ -0,0 +1,11 @@ +# Single Type Doctype + +DocTypes have a table associated with them. For example DocType **Customer** will have a table `tabCustomer` associated with it. + +**Single** type DocTypes have no table associated and there is only one Document for it. This is similar to the Singleton pattern in Java. Single DocTypes are ideal for saving Settings (that are globally applicable) and for wizard / helper type forms that have no documents, but when the DocType is used for the Form UI. + +The data in Single DocType is stored in `tabSingles` (`doctype`, `field`, `value`) + +#### Examples + +In Frappé, Single types are **System Settings** and **Customize Form** \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/app-development/trigger-event-on-deletion-of-grid-row.md b/frappe/docs/user/zh/guides/app-development/trigger-event-on-deletion-of-grid-row.md new file mode 100755 index 0000000000..776742d4ea --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/trigger-event-on-deletion-of-grid-row.md @@ -0,0 +1,49 @@ +# Trigger Event On Deletion Of Grid Row + +To trigger an event when a row from a Child Table has been deleted (when user clicks on `delete` button), you need to add a handler the `fieldname_remove` event to Child Table, where fieldname is the fieldname of the Child Table in Parent Table declaration. + + For example: + + Assuming that your parent DocType is named `Item` has a Table Field linked to `Item Color` DocType with decloration name `color`. + + In order to "catch" the delete event: + + frappe.ui.form.on('Item Color', { + color_remove: function(frm) { + // You code here + // If you console.log(frm.doc.color) you will get the remaining color list + } + ); + + The same process is used to trigger the add event (when user clicks on `add row` button): + + frappe.ui.form.on('Item Color', { + color_remove: function(frm) { + // You code here + // If you console.log(frm.doc.color) you will get the remaining color list + }, + color_add: function(frm) { + } + }); + + Notice that the handling is be made on Child DocType Table `form.ui.on` and not on Parent Doctype so a minimal full example is: + + + ```javascript + frappe.ui.form.on('Item',{ + // Your client side handling for Item + }); + + frappe.ui.form.on('Item Color', { + color_remove: function(frm) { + // Deleting is triggered here + } + ); + ``` +Handlers are: + +1. fieldname_add +1. fieldname_move +1. fieldname_before_remove +1. fieldname_remove + diff --git a/frappe/docs/user/zh/guides/app-development/using-html-templates-in-javascript.md b/frappe/docs/user/zh/guides/app-development/using-html-templates-in-javascript.md new file mode 100755 index 0000000000..2de921551f --- /dev/null +++ b/frappe/docs/user/zh/guides/app-development/using-html-templates-in-javascript.md @@ -0,0 +1,43 @@ +# Using Html Templates In Javascript + +Often while building javascript interfaces, there is a need to render DOM as an HTML template. Frappé Framework uses John Resig's Microtemplate script to render HTML templates in the Desk application. + +> Note 1: In Frappé we use the Jinja-like `{% raw %}{%{% endraw %}` tags to embed code rather than the standard `<%` + +> Note 2: Never use single quotes `'` inside the HTML template. + +To render a template, + +1. Create a template `html` file in your app. e.g. `address_list.html` +1. Add it to `build.json` for your app (you can include it in `frappe.min.js` or your own javascript file). +1. To render it in your app, use `frappe.render(frappe.templates.address_list, {[context]})` + +#### Example Template: + +From `erpnext/public/js/templates/address_list.js` + + {% raw %}

+ {% for(var i=0, l=addr_list.length; i + + {%= __("Edit") %} +

{%= addr_list[i].address_type %}

+
+
+ {% if(addr_list[i].is_primary_address) { %} + {%= __("Primary") %}{% } %} + {% if(addr_list[i].is_shipping_address) { %} + {%= __("Shipping") %}{% } %} +
+

{%= addr_list[i].display %}

+
+ {% } %} + {% if(!addr_list.length) { %} +

{%= __("No address added yet.") %}

+ {% } %}{% endraw %} + + + + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/automated-testing/__init__.py b/frappe/docs/user/zh/guides/automated-testing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/automated-testing/index.md b/frappe/docs/user/zh/guides/automated-testing/index.md new file mode 100644 index 0000000000..ad1bae8629 --- /dev/null +++ b/frappe/docs/user/zh/guides/automated-testing/index.md @@ -0,0 +1,7 @@ +# Automated Testing + +Frappé Provides you a test framework to write and execute tests that can be run directly on a Continuous Integration Tool like Travis + +You can write server-side unit tests or UI tests + +{index} \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/automated-testing/index.txt b/frappe/docs/user/zh/guides/automated-testing/index.txt new file mode 100644 index 0000000000..7d40d39f8a --- /dev/null +++ b/frappe/docs/user/zh/guides/automated-testing/index.txt @@ -0,0 +1,3 @@ +unit-testing +integration-testing +qunit-testing \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/automated-testing/integration-testing.md b/frappe/docs/user/zh/guides/automated-testing/integration-testing.md new file mode 100644 index 0000000000..fb99949a61 --- /dev/null +++ b/frappe/docs/user/zh/guides/automated-testing/integration-testing.md @@ -0,0 +1,49 @@ +# UI Integration Testing + +You can write integration tests using the Selenium Driver. `frappe.utils.selenium_driver` gives you a friendly API to write selenium based tests + +To write integration tests, create a standard test case by creating a python file starting with `test_` + +All integration tests will be run at the end of the unittests. + +### Example + +Here is an example of an integration test to check insertion of a To Do + + from __future__ import print_function + from frappe.utils.selenium_testdriver import TestDriver + import unittest + import time + + class TestToDo(unittest.TestCase): + def setUp(self): + self.driver = TestDriver() + + def test_todo(self): + self.driver.login() + + # list view + self.driver.set_route('List', 'ToDo') + + time.sleep(2) + + # new + self.driver.click_primary_action() + + time.sleep(2) + + # set input + self.driver.set_text_editor('description', 'hello') + + # save + self.driver.click_modal_primary_action() + + time.sleep(2) + + self.assertTrue(self.driver.get_visible_element('.result-list') + .find_element_by_css_selector('.list-item') + .find_element_by_css_selector('.list-id').text=='hello') + + def tearDown(self): + self.driver.close() + diff --git a/frappe/docs/user/zh/guides/automated-testing/qunit-testing.md b/frappe/docs/user/zh/guides/automated-testing/qunit-testing.md new file mode 100644 index 0000000000..55a1242e51 --- /dev/null +++ b/frappe/docs/user/zh/guides/automated-testing/qunit-testing.md @@ -0,0 +1,75 @@ +# UI Testing with Frappé API + +You can either write integration tests, or directly write tests in Javascript using [QUnit](http://api.qunitjs.com/) + +QUnit helps you write UI tests using the UQuit framework and native frappe API. As you might have guessed, this is a much faster way of writing tests. + +### Test Runner + +To write QUnit based tests, add your tests in the `tests/ui` folder of your application. Your test files must begin with `test_` and end with `.js` extension. + +To run your files, you can use the **Test Runner**. The **Test Runner** gives a user interface to load all your QUnit tests and run them in the browser. + +In the CI, all QUnit tests are run by the **Test Runner** using `frappe/tests/test_test_runner.py` + + + +### Running Tests + +To run a Test Runner based test, use the `run-ui-tests` bench command by passing the name of the file you want to run. + + bench run-ui-tests --test frappe/tests/ui/test_list.js + +This will pass the filename to `test_test_runner.py` that will load the required JS in the browser and execute the tests + +### Debugging Tests + +To debug a test, you can open it in the **Test Runner** from your UI and run it manually to see where it is exactly failing. + +### Test Sequence + +In Frappé UI tests are run in a fixed sequence to ensure dependencies. + +The sequence in which the tests will be run will be in `tests/ui/tests.txt` +file. + +### Running All UI Tests + +To run all UI tests together for your app run + + bench run-ui-tests --app [app_name] + +This will run all the files in your `tests/ui` folder one by one. + +### Example QUnit Test + +Here is the example of the To Do test in QUnit + + QUnit.test("Test quick entry", function(assert) { + assert.expect(2); + let done = assert.async(); + let random_text = frappe.utils.get_random(10); + + frappe.run_serially([ + () => frappe.set_route('List', 'ToDo'), + () => frappe.new_doc('ToDo'), + () => frappe.quick_entry.dialog.set_value('description', random_text), + () => frappe.quick_entry.insert(), + (doc) => { + assert.ok(doc && !doc.__islocal); + return frappe.set_route('Form', 'ToDo', doc.name); + }, + () => assert.ok(cur_frm.doc.description.includes(random_text)), + + // Delete the created ToDo + () => frappe.tests.click_page_head_item('Menu'), + () => frappe.tests.click_dropdown_item('Delete'), + () => frappe.tests.click_page_head_item('Yes'), + + () => done() + ]); + }); + +### Writing Test Friendly Code with Promises + +Promises are a great way to write test-friendly code. If your method calls an aysnchronous call (ajax), then you should return an `Promise` object. While writing tests, if you encounter a function that does not return a `Promise` object, you should update the code to return a `Promise` object. diff --git a/frappe/docs/user/zh/guides/automated-testing/unit-testing.md b/frappe/docs/user/zh/guides/automated-testing/unit-testing.md new file mode 100755 index 0000000000..b5c2a8b8b2 --- /dev/null +++ b/frappe/docs/user/zh/guides/automated-testing/unit-testing.md @@ -0,0 +1,199 @@ +# Unit Testing + +## 1.Introduction + +Frappé provides some basic tooling to quickly write automated tests. There are some basic rules: + +1. Test can be anywhere in your repository but must begin with `test_` and should be a `.py` file. +1. Tests must run on a site that starts with `test_`. This is to prevent accidental loss of data. +1. Test stubs are automatically generated for new DocTypes. +1. Frappé test runner will automatically build test records for dependant DocTypes identified by the `Link` type field (Foreign Key) +1. Tests can be executed using `bench run-tests` +1. For non-DocType tests, you can write simple unittests and prefix your file names with `test_`. + +## 2. Running Tests + +This function will build all the test dependencies and run your tests. +You should run tests from "frappe_bench" folder. Without options all tests will be run. + + bench run-tests + +If you need more information about test execution - you can use verbose log level for bench. + + bench --verbose run-tests + +### Options: + + --app + --doctype + --test + --module (Run a particular module that has tests) + --profile (Runs a Python profiler on the test) + --junit-xml-output (The command provides test results in the standard XUnit XML format) + +#### 2.1. Example for app: +All applications are located in folder: "~/frappe-bench/apps". +We can run tests for each application. + + - frappe-bench/apps/erpnext/ + - frappe-bench/apps/erpnext_demo/ + - frappe-bench/apps/frappe/ + + bench run-tests --app erpnext + bench run-tests --app erpnext_demo + bench run-tests --app frappe + + +#### 2.2. Example for doctype: + + frappe@erpnext:~/frappe-bench$ bench run-tests --doctype "Activity Cost" + . + ---------------------------------------------------------------------- + Ran 1 test in 0.008s + + OK + +#### 2.3. Example for test: +Run a specific case in User: + + frappe@erpnext:~/frappe-bench$ bench run-tests --doctype User --test test_get_value + . + ---------------------------------------------------------------------- + Ran 1 test in 0.005s + + OK + +#### 2.4. Example for module: +If we want to run tests in the module: + + /home/frappe/frappe-bench/apps/erpnext/erpnext/support/doctype/issue/test_issue.py + +We should use module name like this (related to application folder) + + erpnext.support.doctype.issue.test_issue + +#####EXAMPLE: + + frappe@erpnext:~/frappe-bench$ bench run-tests --module "erpnext.stock.doctype.stock_entry.test_stock_entry" + ........................... + ---------------------------------------------------------------------- + Ran 27 tests in 30.549s + + +#### 2.5. Example for profile: + + frappe@erpnext:~/frappe-bench$ bench run-tests --doctype "Activity Cost" --profile + . + ---------------------------------------------------------------------- + Ran 1 test in 0.010s + + OK + 9133 function calls (8912 primitive calls) in 0.011 seconds + + Ordered by: cumulative time + + ncalls tottime percall cumtime percall filename:lineno(function) + 2 0.000 0.000 0.008 0.004 /home/frappe/frappe-bench/apps/frappe/frappe/model/document.py:187(insert) + 1 0.000 0.000 0.003 0.003 /home/frappe/frappe-bench/apps/frappe/frappe/model/document.py:386(_validate) + 13 0.000 0.000 0.002 0.000 /home/frappe/frappe-bench/apps/frappe/frappe/database.py:77(sql) + 255 0.000 0.000 0.002 0.000 /home/frappe/frappe-bench/apps/frappe/frappe/model/base_document.py:91(get) + 12 0.000 0.000 0.002 0.000 + +#### 2.6. Example for XUnit XML: + +##### How to run: + + bench run-tests --junit-xml-output=/reports/junit_test.xml + +##### Example of test report: + + + + + + details about failure + + + +It’s designed for the CI Jenkins, but will work for anything else that understands an XUnit-formatted XML representation of test results. + +#### Jenkins configuration support: +1. You should install xUnit plugin - https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin +2. After installation open Jenkins job configuration, click the box named “Publish JUnit test result report” under the "Post-build Actions" and enter path to XML report: +(Example: _reports/*.xml_) + +## 3. Tests for a DocType + +### 3.1. Writing DocType Tests: + +1. Records that are used for testing are stored in a file `test_records.json` in the doctype folder. [For example see the Event Tests](https://github.com/frappe/frappe/blob/develop/frappe/core/doctype/event/test_records.json). +1. Test cases are in a file named `test_[doctype].py` +1. To provide the test records (and dependencies) call `test_records = frappe.get_test_records('Event')` in your test case file. + +#### Example (for `test_records.json`): + + [ + { + "doctype": "Event", + "subject":"_Test Event 1", + "starts_on": "2014-01-01", + "event_type": "Public" + }, + { + "doctype": "Event", + "starts_on": "2014-01-01", + "subject":"_Test Event 2", + "event_type": "Private" + }, + { + "doctype": "Event", + "starts_on": "2014-01-01", + "subject":"_Test Event 3", + "event_type": "Private", + "event_individuals": [{ + "person": "test1@example.com" + }] + } + ] + + +#### Example (for `test_event.py`): + + # Copyright (c) 2015, Frappé Technologies Pvt. Ltd. and Contributors + # MIT License. See license.txt + + import frappe + import frappe.defaults + import unittest + + # load test records and dependencies + test_records = frappe.get_test_records('Event') + + class TestEvent(unittest.TestCase): + def tearDown(self): + frappe.set_user("Administrator") + + def test_allowed_public(self): + frappe.set_user("test1@example.com") + doc = frappe.get_doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 1"})) + self.assertTrue(frappe.has_permission("Event", doc=doc)) + + def test_not_allowed_private(self): + frappe.set_user("test1@example.com") + doc = frappe.get_doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 2"})) + self.assertFalse(frappe.has_permission("Event", doc=doc)) + + def test_allowed_private_if_in_event_user(self): + frappe.set_user("test1@example.com") + doc = frappe.get_doc("Event", frappe.db.get_value("Event", {"subject":"_Test Event 3"})) + self.assertTrue(frappe.has_permission("Event", doc=doc)) + + def test_event_list(self): + frappe.set_user("test1@example.com") + res = frappe.get_list("Event", filters=[["Event", "subject", "like", "_Test Event%"]], fields=["name", "subject"]) + self.assertEquals(len(res), 2) + subjects = [r.subject for r in res] + self.assertTrue("_Test Event 1" in subjects) + self.assertTrue("_Test Event 3" in subjects) + self.assertFalse("_Test Event 2" in subjects) + diff --git a/frappe/docs/user/zh/guides/basics/__init__.py b/frappe/docs/user/zh/guides/basics/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/basics/apps.md b/frappe/docs/user/zh/guides/basics/apps.md new file mode 100755 index 0000000000..2498992172 --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/apps.md @@ -0,0 +1,109 @@ +# Frappé Apps + +Frappé Apps are Python packages which use the Frappé platform. They can live +anywhere on the [Python +path](https://docs.python.org/2/tutorial/modules.html#the-module-search-path) +and must have an entry in the `apps.txt` file. + + +### Creating an app + +Frappé ships with a boiler plate for a new app. The command `bench make-app +app-name` helps you start a new app by starting an interactive shell. + + + % bench new-app sample_app + App Name: sample_app + App Title: Sample App + App Description: This is a sample app. + App Publisher: Acme Inc. + App Icon: icon-linux + App Color: #6DAFC9 + App Email: info@example.com + App URL: http://example.com + App License: MIT + +The above command would create an app with the following directory structure. + + sample_app + ├── license.txt + ├── MANIFEST.in + ├── README.md + ├── sample_app + │   ├── __init__.py + │   ├── sample_app + │   │   └── __init__.py + │   ├── config + │   │   ├── desktop.py + │   │   └── __init__.py + │   ├── hooks.py + │   ├── modules.txt + │   ├── patches.txt + │   └── templates + │   ├── generators + │   │   └── __init__.py + │   ├── __init__.py + │   ├── pages + │   │   └── __init__.py + │   └── statics + └── setup.py + +Here, "App Icon" is a font awesome class that you can select from +[http://fortawesome.github.io/Font-Awesome/icons/](http://fortawesome.github.io/Font-Awesome/icons/). + +The boiler plate contains just enough files to show your app icon on the [Desk]. + +### Files in the app + +#### `hooks.py` + +The `hooks.py` file defines the metadata of your app and integration points +with other parts of Frappé or Frappé apps. Examples of such parts include task +scheduling or listening to updates to different documents in the system. For +now, it just contains the details you entered during app creation. + + + app_name = "sample-app" + app_title = "Sample App" + app_publisher = "Acme Inc." + app_description = "This is a sample app." + app_icon = "fa-linux" + app_color = "black" + app_email = "info@example.com" + app_url = "http://example.com" + app_version = 0.0.1 + +#### `modules.txt` + +Modules in Frappé help you organize Documents in Frappé and they are defined in +the `modules.txt` file in your app. It is necessary for every [DocType] to be +attached to a module. By default a module by the name of your app is added. +Also, each module gets an icon on the [Desk]. For example, the [ERPNext] app is +organized in the following modules. + + accounts + buying + home + hr + manufacturing + projects + selling + setup + stock + support + utilities + contacts + +### Adding app to a site + +Once you have an app, whether it's the one you just created or any other you +downloaded, you are required to do the following things. + +Download the app via git + + bench get-app https://github.com/org/app_name + +Install the app to your site + + bench --site site_name install-app app_name + diff --git a/frappe/docs/user/zh/guides/basics/frappe_ajax_call.md b/frappe/docs/user/zh/guides/basics/frappe_ajax_call.md new file mode 100644 index 0000000000..7f5294015e --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/frappe_ajax_call.md @@ -0,0 +1,78 @@ +# Frappé Ajax Call + +In Frappé Framework, you can manage ajax calls via frappe.call. The frappe.call works in asynchronous manner ie. send requests and handle response via callback mechanism. + +## frappe.call Structure + + frappe.call({ + type: opts.type || "POST", + args: args, + success: callback, + error: opts.error, + always: opts.always, + btn: opts.btn, + freeze: opts.freeze, + freeze_message: opts.freeze_message, + async: opts.async, + url: opts.url || frappe.request.url, + }) + +#### Parameter description : +- type: String parameter, http request type "GET", "POST", "PUT", "DELETE". Default set to "POST". +- args: associative array, arguments that will pass with request. +- success: Function parameter, code snippet, will after successful execution of request +- error: Function parameter, code snippet, will execute after request failure +- always: Function parameter, code snipper, will execute in either case +- btn: Object parameter, triggering object +- freeze: Boolean parameter, if set freeze the instance util it receives response +- freeze_message: String parameter, message will populate to screen while screen is in freeze state. +- async: Boolean parameter, default set to true. So each frappe.call is asynchronous. To make call synchronous set parameter value as false +- url: String parameter, location from where hitting the request + + +## How to use frappe.call ? + +### Calling standard API + frappe.call({ + method: 'frappe.client.get_value', + args: { + 'doctype': 'Item', + 'filters': {'name': item_code}, + 'fieldname': [ + 'item_name', + 'web_long_description', + 'description', + 'image', + 'thumbnail' + ] + }, + callback: function(r) { + if (!r.exc) { + // code snippet + } + } + }); + +- Param description: + - doctype: name of doctype for which you want to pull information + - filters: condition specifier + - fieldname: you can specify fields in array that you want back in response + +### Calling whitelisted functions +- Code client side + + frappe.call({ + method: "frappe.core.doctype.user.user.get_all_roles", //dotted path to server method + callback: function(r) { + // code snippet + } + }) + +- Code at server side + + @frappe.whitelist() + def get_all_roles(): + // business logic + return value + +Note: While accessing any server side method via frappe.call(), you need to whitelist server side method using decorator `@frappe.whitelist` diff --git a/frappe/docs/user/zh/guides/basics/hooks.md b/frappe/docs/user/zh/guides/basics/hooks.md new file mode 100755 index 0000000000..9fb71f7800 --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/hooks.md @@ -0,0 +1,277 @@ +# Hooks + + +Hooks are the duct tape of the Frappé system. Hooks allow you to "hook" in to +functionality and events of other parts of the Frappé system. Following are the +official hooks from Frappé. + +### Application Name and Details + +1. `app_name` - slugified name with underscores e.g. "shopping\_cart" +2. `app_title` - full title name e.g. "Frappé" +3. `app_publisher` +4. `app_description` +5. `app_version` +6. `app_icon` - font-awesome icon or image url +7. `app_color` - hex colour background of the app icon + +### Install Events + +1. `before_install` +2. `after_install` + +The above hooks are called before and after installation of the app they are in. +For example, [ERPNext](/apps/erpnext)'s hooks contains a line, + + after_install = "erpnext.setup.install.after_install" + +So, the function after\_install is imported and called after ERPNext is installed. + +Note, the `before_install` and `after_install` hooks are called with no arguments. + +### Boot Session + +After a successful login, the Frappé JS Client requests for a resource called +`bootinfo`. The `bootinfo` is available as a global in Javascript via +`frappe.boot`. By default, the `bootinfo` contains + +* System defaults +* Notification status +* Permissions +* List of icons on desktop +* User settings +* Language and timezone info + +If your app wants to modify bootinfo, it can declare a hook `boot_session`. The +value is assumed to be a dotted path to a function and is called with one +argument, bootinfo which it can modify and return. + +Eg, + + boot_session = "erpnext.startup.boot.boot_session" + +### Notification configurations + +The notification configuration hook is expected to return a Python dictionary. + + { + "for_doctype": { + "Issue": {"status":"Open"}, + "Customer Issue": {"status":"Open"}, + }, + "for_module_doctypes": { + "ToDo": "To Do", + "Event": "Calendar", + "Comment": "Messages" + }, + "for_module": { + "To Do": "frappe.core.notifications.get_things_todo", + "Calendar": "frappe.core.notifications.get_todays_events", + "Messages": "frappe.core.notifications.get_unread_messages" + } + } + + +The above configuration has three parts, + +1. `for_doctype` part of the above configuration marks any "Issue" + or "Customer Issue" as unread if its status is Open +2. `for_module_doctypes` maps doctypes to module's unread count. +3. `for_module` maps modules to functions to obtain its unread count. The + functions are called without any argument. + +### Javascript / CSS Assets + +The following hooks allow you to bundle built assets to your app for serving. +There are two types of assets, app and web. The app assets are loaded in the +Desk and web assets are loaded in the website. + +1. `app_include_js` +2. `app_include_css` +3. `web_include_js` +4. `web_include_css` + +Eg, + + app_include_js = "assets/js/erpnext.min.js" + web_include_js = "assets/js/erpnext-web.min.js" + +Note: to create an asset bundle (eg, assets/js/erpnext.min.js) the target file +should be in build.json of your app. + +### Configuring Reports + +In the report view, you can force filters as per doctype using `dump_report_map` +hook. The hook should be a dotted path to a Python function which will be called +without any arguments. Example of output of this function is below. + + + "Warehouse": { + "columns": ["name"], + "conditions": ["docstatus < 2"], + "order_by": "name" + } + +Here, for a report with Warehouse doctype, would include only those records that +are not cancelled (docstatus < 2) and will be ordered by name. + +### Modifying Website Context + +Context used in website pages can be modified by adding +a `update_website_context` hook. This hook should be a dotted path to a function +which will be called with a context (dictionary) argument. + +### Customizing Email footer + +By default, for every email, a footer with content, "Sent via Frappé" is sent. +You can customize this globally by adding a `mail_footer` hook. The hook should +be a dotted path to a variable. + +### Session Creation Hook + +You can attach custom logic to the event of a successful login using +`on_session_creation` hook. The hook should be a dotted path to a Python +function that takes login\_manager as an argument. + +Eg, + + def on_session_creation(login_manager): + """make feed""" + if frappe.session['user'] != 'Guest': + # log to timesheet + pass + +### Website Clear Cache + +If you cache values in your views, the `website_clear_cache` allows you to hook +methods that invalidate your caches when Frappé tries to clear cache for all +website related pages. + +### Document hooks + +#### Permissions + +#### Query Permissions +You can customize how permissions are resolved for a DocType by hooking custom +permission match conditions using the `permission_query_conditions` hook. This +match condition is expected to be fragment for a where clause in an sql query. +Structure for this hook is as follows. + + + permission_query_conditions = { + "{doctype}": "{dotted.path.to.function}", + } + +The output of the function should be a string with a match condition. +Example of such a function, + + + def get_permission_query_conditions(): + return "(tabevent.event_type='public' or tabevent.owner='{user}'".format(user=frappe.session.user) + +The above function returns a fragment that permits an event to listed if it's +public or owned by the current user. + +#### Document permissions +You can hook to `doc.has_permission` for any DocType and add special permission +checking logic using the `has_permission` hook. Structure for this hook is, + + has_permission = { + "{doctype}": "{dotted.path.to.function}", + } + +The function will be passed the concerned document as an argument. It should +True or a falsy value after running the required logic. + +For Example, + + def has_permission(doc): + if doc.event_type=="Public" or doc.owner==frappe.session.user: + return True + +The above function permits an event if it's public or owned by the current user. + +#### CRUD Events + +You can hook to various CRUD events of any doctype, the syntax for such a hook +is as follows, + + doc_events = { + "{doctype}": { + "{event}": "{dotted.path.to.function}", + } + } + +To hook to events of all doctypes, you can use the follwing syntax also, + + doc_events = { + "*": { + "on_update": "{dotted.path.to.function}", + } + } + +The hook function will be passed the doc in concern as the only argument. + +##### List of events + +* `validate` +* `before_save` +* `autoname` +* `after_save` +* `before_insert` +* `after_insert` +* `before_submit` +* `before_cancel` +* `before_update_after_submit` +* `on_update` +* `on_submit` +* `on_cancel` +* `on_update_after_submit` +* `on_change` +* `on_trash` +* `after_delete` + + +Eg, + + doc_events = { + "Cab Request": { + "after_insert": topcab.schedule_cab", + } + } + +### Scheduler Hooks + +Scheduler hooks are methods that are run periodically in background. Structure for such a hook is, + + scheduler_events = { + "{event_name}": [ + "{dotted.path.to.function}" + ], + } + +#### Events + +* `daily` +* `daily_long` +* `weekly` +* `weekly_long` +* `monthly` +* `monthly_long` +* `hourly` +* `all` + +The scheduler events require celery, celerybeat and redis (or a supported and +configured broker) to be running. The events with suffix '\_long' are for long +jobs. The `all` event is triggered everytime (as per the celerybeat interval). + +Example, + + scheduler_events = { + "{daily}": [ + "erpnext.accounts.doctype.sales_invoice.sales_invoice.manage_recurring_invoices" + ], + "{daily_long}": [ + "erpnext.setup.doctype.backup_manager.backup_manager.take_backups_daily" + ], + } diff --git a/frappe/docs/user/zh/guides/basics/index.md b/frappe/docs/user/zh/guides/basics/index.md new file mode 100755 index 0000000000..645bed29a7 --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/index.md @@ -0,0 +1,3 @@ +# Basics + +{index} diff --git a/frappe/docs/user/zh/guides/basics/index.txt b/frappe/docs/user/zh/guides/basics/index.txt new file mode 100755 index 0000000000..47c5728f4b --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/index.txt @@ -0,0 +1,8 @@ +install +apps +sites +site_config +hooks +translations +writing-tests +frappe_ajax_call diff --git a/frappe/docs/user/zh/guides/basics/install.md b/frappe/docs/user/zh/guides/basics/install.md new file mode 100755 index 0000000000..cf65752cf1 --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/install.md @@ -0,0 +1,10 @@ +# Installing Frappé + +## Frappé bench + +The following steps help you setup an isolated environment (bench) to run and +develop Frappé apps. A virtualenv is installed in the env directory. You can +activate it by running `source ./env/bin/activate` or use execute using +absolute/relative path (eg, `./env/bin/frappe`). + +For more info, see [Frappé Bench](https://github.com/frappe/bench/) diff --git a/frappe/docs/user/zh/guides/basics/site_config.md b/frappe/docs/user/zh/guides/basics/site_config.md new file mode 100755 index 0000000000..4f4407271e --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/site_config.md @@ -0,0 +1,52 @@ +# Site Config + +Settings for `sites/[site]/site_config.json` + +`site_config.json` stores global settings for a particular site and is present in the site directory. Here is a list of properties you can set in `site_config.json`. + +Example: + + { + "db_name": "test_frappe", + "db_password": "test_frappe", + "admin_password": "admin", + } + +### Mandatory Settings + +- `db_name`: Database Name. +- `db_password`: Database password. +- `encryption_key`: encryption_key for stored non user passwords. + +### Optional Settings + +- `admin_password`: Default Password for "Administrator". +- `mute_emails`: Stops email sending if true. +- `deny_multiple_logins`: Stop users from having more than one active session. +- `root_password`: MariaDB root password. + +### Remote Database Host Settings +- `db_host`: Database host if not `localhost`. + +To connect to a remote database server using ssl, you must first configure the database host to accept SSL connections. An example of how to do this is available at https://www.digitalocean.com/community/tutorials/how-to-configure-ssl-tls-for-mysql-on-ubuntu-16-04. After you do the configuration, set the following three options. All options must be set for Frappé to attempt to connect using SSL. +- `db_ssl_ca`: Full path to the ca.pem file used for connecting to a database host using ssl. Example value is `"/etc/mysql/ssl/ca.pem"`. +- `db_ssl_cert`: Full path to the cert.pem file used for connecting to a database host using ssl. Example value is `"/etc/mysql/ssl/client-cert.pem"`. +- `db_ssl_key`: Full path to the key.pem file used for connecting to a database host using ssl. Example value is `"/etc/mysql/ssl/client-key.pem"`. + +### Default Outgoing Email Settings + +- `mail_server`: SMTP server hostname. +- `mail_port`: STMP port. +- `use_ssl`: Connect via SSL / TLS. +- `mail_login`: Login id for SMTP server. +- `mail_password`: Password for SMTP server. + +### Developer Settings + +- `developer_mode`: If developer mode is set, DocType changes are automatically updated in files. +- `disable_website_cache`: Don't cache website pages. +- `logging`: writes logs if **1**, writes queries also if set to **2**. + +### Others + +- `robots_txt`: Path to robots.txt file to be rendered when going to frappe-site.com/robots.txt diff --git a/frappe/docs/user/zh/guides/basics/sites.md b/frappe/docs/user/zh/guides/basics/sites.md new file mode 100755 index 0000000000..abf360cdf1 --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/sites.md @@ -0,0 +1,82 @@ +# Sites + +## Sites Directory + +Frappé is a multitenant platform and each tenant is called a site. Sites exist +in a directory called `sites_dir`, assumed as the current working directory when +running a frappe command or other services like Celery worker or a WSGI server. + +You can set `sites_dir` with an environment variable `SITES_DIR` or pass +`--sites_dir` option to the frappe command. + +Apart from the sites, the `sites_dir` should contain the following. + +#### apps.txt + +`apps.txt` contain a list of Python packages to treat as Frappé apps. Every +frappe app that you intend to use in you site should have an entry in this file. +Also, they should be in the `PYTHONPATH`. For more information, refer +[Frappé Apps](/help/apps). + +#### common\_site\_config.json + +`common_site_config.json` is an optional file. Configuration common to all sites +can be put in this file. + +#### assets + +Assets contain files that are required to be served for the browser client. +These generally include *.js, *.css, *.png files. This directory is auto +generated using the `bench build` command. + +#### languages.txt + +`languages.txt` is an autogenerated file which maps every language to it's code. + +## Site + +A site is a directory in `sites_dir` which represents a tenant in Frappé Platform. + + +### Directory Structure + + site + ├── locks + ├── private + │   └── backups + ├── public + │   └── files + │ └── testfile.txt + └── site_config.json + +* `locks` directory is used by the scheduler to synchronize various jobs using +the [file locking concept](http://en.wikipedia.org/wiki/File_locking). + +* `private` directory contains files that require authentication to access. +Presently, it is limited only to backups. + +* `public` directory contains files that can directly served. In the above + example, `testfile.txt` can be accessed by the URL, + http://site/files/testfile.txt + +* `site_config.json` contains site specific configuration + +### Site Config + +[See configuration options for `site_config.json`](/frappe/user/en/guides/basics/site_config) + +### Site Resolution + +While responding to an HTTP request, a site is automatically selected based on, + +* `Host` header in the HTTP request matches a site +* `X-Frappé-Site-Name` header in the HTTP request matches a site + +It is also possible to force the development server to serve a specific site by +starting it with the following command. + `bench --site SITENAME serve` + + +### Adding a new site + +`bench new-site SITENAME` diff --git a/frappe/docs/user/zh/guides/basics/translations.md b/frappe/docs/user/zh/guides/basics/translations.md new file mode 100755 index 0000000000..86d630898f --- /dev/null +++ b/frappe/docs/user/zh/guides/basics/translations.md @@ -0,0 +1,88 @@ +# Translations + + + + +This document shows how to translations are managed in ERPNext and how to add +a new language or update translations of an existing language. + +### 1. Source + +Translatable text exists in 3 main sources: + + 1. Javascript Code Files (both framework and application) + 2. Python Code Files + 3. DocTypes (names, labels and select options) + +#### Strings in Code Files + +Strings in code files are annotated using the `_` (underscore) method + + 1. In Python it is the `frappe._` method. Example: + +`frappe._("String {0} must be translated".format(txt))` + + 2. In Javascript it is the `__` method. Example: + +`__("String {0} must be translated", [txt])` + +**Note:** If you find translatable strings are not properly annotated using the `_` +method, you can add them in the code and rebuild the translations. + +### 2. How Translations Are Picked up During Execution + +Whenever a translation is called via the _ method, the entire translation +dictionaries from all apps are built and stored in memcache. + +Based on the user preferences or request preferences, the appropriate +translations are loaded at the time of request on the server side. Or if +metadata (DocType) is queried, then the appropriate translations are appended +when the DocType data is requested. + +The underscore `_` method will replace the strings based on the available +translations loaded at the time. + +### 3. Adding New Translations + +1. To find untranslated strings, run `bench get-untranslated [lang] [path]` +1. Add the translated strings in another file in the same order +1. run `bench update-translations [lang] [path of untranslated strings] [path of translated strings]` + +### 4. Improving Translations: + +For updating translations, please go to the to [the translation portal](https://frappe.io/translator). + +If you want to do it directly via code: + +To improve an existing translation, just edit the master translation files in +the `translations` of each app + +> Please contribute your translations back to ERPNext by sending us a Pull +Request. + +### 5. Bootstrapping a New Language + +If you want to add a new language it is similar to adding new translations. You need to first export all the translations strings in one file, get them translated via Google Translate Tool or Bing Translate Tool and then import the translations into individual apps. + +**Step 1: Export to a file** + + $ bench get-untranslated [lang] [path] + +**Step 2: Translate** + +Create another file with updated translations (in the same order as the source file). For this you can use the [Google Translator Toolkit](https://translate.google.com/toolkit) or [Bing Translator](http://www.bing.com/translator/). + +**Step 3: Import your translations** + + $ bench update-translations [lang] [source path] [translated path] + +**Step 4: Update `languages.txt`** + +Add your language in `apps/languages.txt` and also `frappe/data/languages.txt` (fore new bench installs) + +**Step 5: Commit each app and push** + +A new file will be added to the `translations` folder in each app. You need to add that file and push to your repo. Then send us a pull-request. + +--- + diff --git a/frappe/docs/user/zh/guides/data/__init__.py b/frappe/docs/user/zh/guides/data/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/data/import-large-csv-file.md b/frappe/docs/user/zh/guides/data/import-large-csv-file.md new file mode 100755 index 0000000000..013d02ac92 --- /dev/null +++ b/frappe/docs/user/zh/guides/data/import-large-csv-file.md @@ -0,0 +1,25 @@ +# Import Large Csv File + +To import very large CSV files, you can use the bench utility `import-csv`. + +The benefit is that this is not subject to timeouts if you use the web interface. + +Here is an example: + + bench --site test.erpnext.com import-csv ~/Downloads/Activity_Type.csv + +### Help + + $ bench import-csv --help + Usage: bench import-csv [OPTIONS] PATH + + Import CSV using data import tool + + Options: + --only-insert Do not overwrite existing records + --submit-after-import Submit document after importing it + --ignore-encoding-errors Ignore encoding errors while coverting to unicode + --help Show this message and exit. + + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/data/index.md b/frappe/docs/user/zh/guides/data/index.md new file mode 100755 index 0000000000..e6f341b236 --- /dev/null +++ b/frappe/docs/user/zh/guides/data/index.md @@ -0,0 +1,3 @@ +# Data Management + +{index} diff --git a/frappe/docs/user/zh/guides/data/index.txt b/frappe/docs/user/zh/guides/data/index.txt new file mode 100755 index 0000000000..620bafa4e3 --- /dev/null +++ b/frappe/docs/user/zh/guides/data/index.txt @@ -0,0 +1,2 @@ +import-large-csv-file +using-data-migration-tool diff --git a/frappe/docs/user/zh/guides/data/using-data-migration-tool.md b/frappe/docs/user/zh/guides/data/using-data-migration-tool.md new file mode 100644 index 0000000000..a7a57cb8c5 --- /dev/null +++ b/frappe/docs/user/zh/guides/data/using-data-migration-tool.md @@ -0,0 +1,99 @@ +# Using the Data Migration Tool + +> Data Migration Tool was introduced in Frappé Framework version 9. + +The Data Migration Tool was built to abstract all the syncing of data between a remote source and a DocType. This is a middleware layer between your Frappé based website and a remote data source. + +To understand this tool, let's make a connector to push ERPNext Items to an imaginary service called Atlas. + +### Data Migration Plan +A Data Migration Plan encapsulates a set of mappings. + +Let's make a new *Data Migration Plan*. Set the plan name as 'Atlas Sync'. We also need to add mappings in the mappings child table. + +New Data Migration Plan + + +### Data Migration Mapping +A Data Migration Mapping is a set of rules that specify field-to-field mapping. + +Make a new *Data Migration Mapping*. Call it 'Item to Atlas Item'. + +To define a mapping, we need to put in some values that define the structure of local and remote data. + +1. Remote Objectname: A name that identifies the remote object e.g Atlas Item +1. Remote primary key: This is the name of the primary key for Atlas Item e.g id +1. Local DocType: The DocType which will be used for syncing e.g Item +1. Mapping Type: A Mapping can be of type 'Push' or 'Pull', depending on whether the data is to be mapped remotely or locally. It can also be 'Sync', which will perform both push and pull operations in a single cycle. +1. Page Length: This defines the batch size of the sync. + +New Data Migration Mapping + +#### Specifying field mappings: + +The most basic form of a field mapping would be to specify fieldnames of the remote and local object. However, if the mapping is one-way (push or pull), the source field name can also take literal values in quotes (for e.g `"GadgetTech"`) and eval statements (for e.g `"eval:frappe.db.get_value('Company', 'Gadget Tech', 'default_currency')"`). For example, in the case of a push mapping, the local fieldname can be set to a string in quotes or an `eval` expression, instead of a field name from the local doctype. (This is not possible with a sync mapping, where both local and remote fieldnames serve as a target destination at a some point, and thus cannot be a literal value). + +Let's add the field mappings and save: + +Add fields in Data Migration Mapping + +We can now add the 'Item to Atlas Item' mapping to our Data Migration Plan and save it. + +Save Atlas Sync Plan + +#### Additional layer of control with pre and post process: + +Migrating data frequently involves more steps in addition to one-to-one mapping. For a Data Migration Mapping that is added to a Plan, a mapping module is generated in the module specified in that plan. + +In our case, an `item_to_atlas_item` module is created under the `data_migration_mapping` directory in `Integrations` (module for the 'Atlas Sync' plan). + +Mapping __init__.py + +You can implement the `pre_process` (receives the source doc) and `post_process` (receives both source and target docs, as well as any additional arguments) methods, to extend the mapping process. Here's what some operations could look like: + +Pre and Post Process + +### Data Migration Connector +Now, to connect to the remote source, we need to create a *Data Migration Connector*. + +New Data Migration Connector + +We only have two connector types right now, let's add another Connector Type in the Data Migration Connector DocType. + +Add Connector Type in Data Migration Connector + +Now, let's create a new Data Migration Connector. + +Atlas Connector + +As you can see we chose the Connector Type as Atlas. We also added the hostname, username and password for our Atlas instance so that we can authenticate. + +Now, we need to write the code for our connector so that we can actually push data. + +Create a new file called `atlas_connection.py` in `frappe/data_migration/doctype/data_migration_connector/connectors/` directory. Other connectors also live here. + +We just have to implement the `insert`, `update` and `delete` methods for our atlas connector. We also need to write the code to connect to our Atlas instance in the `__init__` method. Just see `frappe_connection.py` for reference. + +Atlas Connection file + +After creating the Atlas Connector, we also need to import it into `data_migration_connector.py` + +Edit Connector file + +### Data Migration Run +Now that we have our connector, the last thing to do is to create a new *Data Migration Run*. + +A Data Migration Run takes a Data Migration Plan and Data Migration Connector and execute the plan according to our configuration. It takes care of queueing, batching, delta updates and more. + +Data Migration Run + +Just click Run. It will now push our Items to the remote Atlas instance and you can see the progress which updates in realtime. + +After a run is executed successfully, you cannot run it again. You will have to create another run and execute it. + +Data Migration Run will try to be as efficient as possible, so the next time you execute it, it will only push those items which were changed or failed in the last run. + + +> Note: Data Migration Tool is still in beta. If you find any issues please report them [here](https://github.com/frappe/erpnext/issues) + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/deployment/__init__.py b/frappe/docs/user/zh/guides/deployment/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/deployment/email-notifications-for-failed-background-jobs.md b/frappe/docs/user/zh/guides/deployment/email-notifications-for-failed-background-jobs.md new file mode 100755 index 0000000000..b56c57891b --- /dev/null +++ b/frappe/docs/user/zh/guides/deployment/email-notifications-for-failed-background-jobs.md @@ -0,0 +1,25 @@ +# Email Notifications For Failed Background Jobs + + + +

Frappé handles failure of jobs in the following way,

1) If a job fails, (raises exception), it's logged in Scheduler Log and  logs/worker.error.log.
2) Keeps a lock file and would not run anymore if lock file is there.
3) Raises LockTimeoutError in case the lock file is more than 10 minutes old.

+ +

You can configure email notification for scheduler errors. By writing a file, sites/common_site_config.json with content

+ +
{
+  "celery_error_emails": {
+    "ADMINS": [
+      [
+        "Person 1",
+        "person1@example.com"
+      ],
+      [
+        "Person2 ",
+        "person2@example.com"
+      ]
+    ],
+    "SERVER_EMAIL": "exceptions@example.com"
+  }
+}
+ +

One limitation is that it'll use local mailserver on port 25 to send the emails.

\ No newline at end of file diff --git a/frappe/docs/user/zh/guides/deployment/how-to-enable-social-logins.md b/frappe/docs/user/zh/guides/deployment/how-to-enable-social-logins.md new file mode 100755 index 0000000000..bd16e88037 --- /dev/null +++ b/frappe/docs/user/zh/guides/deployment/how-to-enable-social-logins.md @@ -0,0 +1,66 @@ +# How To Enable Social Logins + +Use Facebook, Google or GitHub authentication to login to Frappé, and your users will be spared from remembering another password. + +The system uses the **Email Address** supplied by these services to **match with an existing user** in Frappé. If no such user is found, **a new user is created** of the default type **Website User**, if Signup is not disabled in Website Settings. Any System Manager can later change the user type from **Website User** to **System User**, so that the user can access the Desktop. + +#### Login screen with Social Logins enabled +Login screen with Social Logins enabled + +To enable these signups, you need to have **Client ID** and **Client Secret** from these authentication services for your Frappé site. The Client ID and Client Secret are to be set in Website > Setup > Social Login Keys. Here are the steps to obtain these credentials. + +> Use **https://{{ yoursite }}** if your site is HTTPS enabled. + +--- + +### Facebook + +1. Go to [https://developers.facebook.com](https://developers.facebook.com) +1. Click on Apps (topbar) > New App, fill in the form. +1. Go to Settings > Basic, set the **Contact Email** and save the changes. +1. Go to Settings > Advanced, find the field **Valid OAuth redirect URIs**, and enter: + **http://{{ yoursite }}/api/method/frappe.www.login.login\_via\_facebook** +1. Save the changes in Advance tab. +1. Go to Status & Review and switch on "Do you want to make this app and all its live features available to the general public?" +1. Go to Dashboard, click on the show button besides App Secret, and copy the App ID and App Secret into **Desktop > Website > Setup > Social Login Keys** + +
+ +
+ +--- + +### Google + +1. Go to [https://console.developers.google.com](https://console.developers.google.com) +1. Create a new Project and fill in the form. +1. Click on APIs & Auth > Credentials > Create new Client ID +1. Fill the form with: + - Web Application + - Authorized JavaScript origins as **http://{{ yoursite }}** + - Authorized redirect URI as + **http://{{ yoursite }}/api/method/frappe.www.login.login\_via\_google** +1. Go to the section **Client ID for web application** and copy the Client ID and Client Secret into **Desktop > Website > Setup > Social Login Keys** + +
+ +
+ +--- + +### GitHub + +1. Go to [https://github.com/settings/applications](https://github.com/settings/applications) +1. Click on **Register new application** +1. Fill the form with: + - Homepage URL as **http://{{ yoursite }}** + - Authorization callback URL as + **http://{{ yoursite }}/api/method/frappe.www.login.login\_via\_github** +1. Click on Register application. +1. Copy the generated Client ID and Client Secret into **Desktop > Website > Setup > Social Login Keys** + +
+ +
+ + diff --git a/frappe/docs/user/zh/guides/deployment/how-to-migrate-doctype-changes-to-production.md b/frappe/docs/user/zh/guides/deployment/how-to-migrate-doctype-changes-to-production.md new file mode 100755 index 0000000000..4bf2ab9351 --- /dev/null +++ b/frappe/docs/user/zh/guides/deployment/how-to-migrate-doctype-changes-to-production.md @@ -0,0 +1,15 @@ +# How To Migrate Doctype Changes To Production + +#### 1. DocType / Schema Changes + +If you are in `developer_mode`, the `.json` files for each **DocType** are automatically updated. + +When you update in your production using `--latest` or `bench update`, these changes are updated in the site's schema too! + +#### 2. Permissions + +Permissions do not get updated because the user may have changed them. To update permissions, you can add a new patch in the `patches.txt` of your app. + + execute:frappe.permissions.reset_perms("[docype]") + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/deployment/index.md b/frappe/docs/user/zh/guides/deployment/index.md new file mode 100755 index 0000000000..9487bbf803 --- /dev/null +++ b/frappe/docs/user/zh/guides/deployment/index.md @@ -0,0 +1,5 @@ +# Deployment + +Deploying your apps on remote servers + +{index} diff --git a/frappe/docs/user/zh/guides/deployment/index.txt b/frappe/docs/user/zh/guides/deployment/index.txt new file mode 100755 index 0000000000..7d11bf514a --- /dev/null +++ b/frappe/docs/user/zh/guides/deployment/index.txt @@ -0,0 +1,4 @@ +migrations +how-to-migrate-doctype-changes-to-production +email-notifications-for-failed-background-jobs +how-to-enable-social-logins diff --git a/frappe/docs/user/zh/guides/deployment/migrations.md b/frappe/docs/user/zh/guides/deployment/migrations.md new file mode 100755 index 0000000000..e40f48f0be --- /dev/null +++ b/frappe/docs/user/zh/guides/deployment/migrations.md @@ -0,0 +1,69 @@ +# Migrations + +A project often undergoes changes related to database schema during course of +its life. It may also be required patch existing data. Frappé bundles tools to +handle these schenarios. + +When you pull updates from any Frappé app (including Frappé), you should run +`bench migrate` to apply schema changes and data migrations if any. + +## Schema changes + +You can edit a DocType to add, remove or change fields. On saving a DocType, +a JSON file containing the DocType data is added to source tree of your app. +When you add an app to a site, the DocTypes are installed using this JSON file. +For making schema changes, it's required to set `developer_mode` in the +configuration. + +On running a sync (`bench migrate`), doctypes in the system are synced to +their latest version from the JSON files in the app. + +Note: Fields are soft deleted ie. the columns are not removed from the database +table and however, they will not be visible in the documents. This is done to +avoid any potential data loss situations and to allow you write related data +migrations which might need values from deleted fields. + +Note: Frappé doesn't support reverse schema migrations. + +## Data Migrations + +On introducing data related changes, you might want to run one off scripts to +change existing data to match expectations as per new code. + +To add a data migration to your code, you will have to write an `execute` +function to a python module and add it to `patches.txt` of your app. + +It is recommended to make a file with a patch number and name in its path and +add it to a patches package (directory) in your app. You can then add a line +with dotted path to the patch module to `patches.txt`. + +The directory structure followed in Frappé is as below + + + frappe + └── patches + └── 4_0 + └── my_awesome_patch.py + +The patch can be added to `patches.txt` by adding a line like + + frappe.patches.4_0.my_awesome_patch + +The metadata ie. DocType available in the execute function will be the latest as +per JSON files in the apps. However, you will not be able to access metadata of +any previous states of the system. + +#### One off Python statements + +You can also add one off python statements in `patches.txt` using the syntax, + execute:{python statement} + +For example, + execute:frappe.get_doc("User", "Guest").save() + +Note: All lines in patches.txt have to be unique. If you want to run a line +twice, you can make it unique by adding a distinct comment. + +For Example, + + execute:frappe.installer.make_site_dirs() #2014-02-19 diff --git a/frappe/docs/user/zh/guides/desk/__init__.py b/frappe/docs/user/zh/guides/desk/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/desk/formatter_for_link_fields.md b/frappe/docs/user/zh/guides/desk/formatter_for_link_fields.md new file mode 100644 index 0000000000..586477f32e --- /dev/null +++ b/frappe/docs/user/zh/guides/desk/formatter_for_link_fields.md @@ -0,0 +1,19 @@ +# Formatter For Link Fields + +In case where a code and a name is maintained for an entity, (for example for Employee there may be an Employee Code and Employee Name) and we want to show both the ID and name in a link field, we can make a formatter. + +#### Example: + + frappe.form.link_formatters['Employee'] = function(value, doc) { + if(doc.employee_name && doc.employee_name !== value) { + return value + ': ' + doc.employee_name; + } else { + return value; + } + } + +Notes: + +1. Both the primary key (`name) and the descriptive name (e.g. `employee_name`) must be present in the document. The descriptive name field could be hidden +1. This needs to be loaded before the document is loaded and can be re-used for all forms. You can also add it in `build.json` + diff --git a/frappe/docs/user/zh/guides/desk/index.md b/frappe/docs/user/zh/guides/desk/index.md new file mode 100755 index 0000000000..47a3b43578 --- /dev/null +++ b/frappe/docs/user/zh/guides/desk/index.md @@ -0,0 +1,5 @@ +# Desk Customization + +Articles related to customization of Frappé Desk + +{index} diff --git a/frappe/docs/user/zh/guides/desk/making_charts.md b/frappe/docs/user/zh/guides/desk/making_charts.md new file mode 100644 index 0000000000..3d8db9ac16 --- /dev/null +++ b/frappe/docs/user/zh/guides/desk/making_charts.md @@ -0,0 +1,3 @@ +# Making Charts + +[**Frappé Charts**](https://frappe.github.io/charts/) enables you to render simple line, bar or percentage graphs for single or multiple discreet sets of data points. You can also set special checkpoint values and summary stats. Check out the docs at https://frappe.github.io/charts/ to learn more. \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/index.md b/frappe/docs/user/zh/guides/index.md new file mode 100755 index 0000000000..765334be62 --- /dev/null +++ b/frappe/docs/user/zh/guides/index.md @@ -0,0 +1,7 @@ +# Guides + +The Frappé Framework is a server side and client side framework and is built with the philosophy make it a "battries included" framework. It has libraries and API for everything from authentication to reports. + +In this section we will try and cover the most commonly used API on client and server side that will be useful for app development. + +{index} diff --git a/frappe/docs/user/zh/guides/index.txt b/frappe/docs/user/zh/guides/index.txt new file mode 100755 index 0000000000..d47785065a --- /dev/null +++ b/frappe/docs/user/zh/guides/index.txt @@ -0,0 +1,7 @@ +basics +app-development +deployment +reports-and-printing +portal-development +data +integration diff --git a/frappe/docs/user/zh/guides/integration/__init__.py b/frappe/docs/user/zh/guides/integration/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/integration/google_gsuite.md b/frappe/docs/user/zh/guides/integration/google_gsuite.md new file mode 100644 index 0000000000..d44773c271 --- /dev/null +++ b/frappe/docs/user/zh/guides/integration/google_gsuite.md @@ -0,0 +1,74 @@ +# Google GSuite + +Frappe allows you to use Google's Gsuite documents as templates, generate from them a new Gsuite document that will be placed in a chosen folder. Variables can populated in both the body and the name of the Gsuite document using the standard Jinja2 format. Once generated, the Gsuite document will remain associate to the DocType as an attachment. + +The Gsuite document is generated by invoking the "attach file" function of any DocType. + +A common use cases of this features is populating contracts from customer/employee/supplier data. + +## 1. Enable integration with Google Gsuite + +### 1.1 Publish Google apps script + +*If you will use the default script you can go to 1.2* + +1. Go to [https://script.google.com](https://script.google.com) +1. Create a new Project. Click on **File > New > Project** +1. Copy the code of **Desk > Explore > Integrations > GSuite Settings > Google Apps Script** to clipboard and paste to an empty Code.gs in script.google.com +1. Save the Project. Click on **File > Save > Enter new project name** +1. Deploy the app. Click on **Publish > Deploy as web app** +1. Copy "Current web app URL" into **Desk > Explore > Integrations > GSuite Settings > Script URL** +1. Click on OK but don't close the script + +### 1.2 + +### 1.2 Get Google access + +1. Go to your Google project console and select your project or create a new one. [https://console.developers.google.com](https://console.developers.google.com) +1. In Library click on **Google Drive API** and **Enable** +1. Click on **Credentials > Create Credentials > OAuth Client ID** +1. Fill the form with: + - Web Application + - Authorized redirect URI as `http://{{ yoursite }}/?cmd=frappe.integrations.doctype.gsuite_settings.gsuite_settings.gsuite_callback` +1. Copy the Client ID and Client Secret into **Desk > Explore > Integrations > GSuite Settings > Client ID and Client Secret** +1. Save GSuite Settings + +### 1.3 Test Script + +1. Click on **Allow GSuite Access** and you will be redirected to select the user and give access. If you have any error please verify you are using the correct Authorized Redirect URI. + You can find the complete URI Gsuite redirected to in the final part of the URL of the error page. Check that the protocol `http://` or `https://` matches the one your using. +1. Click on **Run Script test**. You should be asked to give permission. + +## 2. GSuite Templates + +### 2.1 Google Document as Template + +1. Create a new Document or use one you already have. Set variables as you need. Variables are defined with `{{VARIABLE}}` with ***VARIABLE*** is the field of your Doctype + + For Example, + If this document will be used to employee and the Doctype has the field ***name*** then you can use it in Google Docs ad `{{name}}` + +1. Get the ID of that Document from url of your document. + For example: in this document url `https://docs.google.com/document/d/1Y2_btbwSqPIILLcJstHnSm1u5dgYE0QJspcZBImZQso/edit` the document ID is `1Y2_btbwSqPIILLcJstHnSm1u5dgYE0QJspcZBImZQso` + +1. Get the ID of the folder where you want to place the generated documents. (You can step this point if you want to place the generated documents in Google Drive root. ) + + For example: in this folder url `https://drive.google.com/drive/u/0/folders/0BxmFzZZUHbgyQzVJNzY5eG5jbmc` the folder ID is `0BxmFzZZUHbgyQzVJNzY5eG5jbmc` + +### 2.2 Associate the Template to a Doctype + +1. Go to **Desk > Explore > Integrations > GSuite Templates > New** +2. Fill the form with: + - Template Name (Example: `Employee Contract`) + - Related DocType (Example: `Employee`) + - Template ID is the Document ID you get from your Google Docs (Example: `1Y2_btbwSqPIILLcJstHnSm1u5dgYE0QJspcZBImZQso`) + - Document name is the name of the new files. You can use field from DocType (Example: `Employee Contract of {name}`) + - Destination ID is the folder ID of your files created from this Template. (Example: `0BxmFzZZUHbgyQzVJNzY5eG5jbmc`) + +## 3. Create Documents + +1. Go to a Document you already have a Template for (Example: Employee > John Doe) +2. Click on **Attach File** +3. On **GSuite Document** section select the Template and click **Attach** +4. You should see the generated document is already created and attached +5. Clicking on the attached document will open it inside Gsuite diff --git a/frappe/docs/user/zh/guides/integration/how_to_setup_oauth.md b/frappe/docs/user/zh/guides/integration/how_to_setup_oauth.md new file mode 100644 index 0000000000..6a4fc93650 --- /dev/null +++ b/frappe/docs/user/zh/guides/integration/how_to_setup_oauth.md @@ -0,0 +1,53 @@ +# How to setup oauth? + +OAuth 2.0 provider based on oauthlib is built into frappe. Third party apps can now access resources of users based on Frappé Role and User permission system. To setup an app to access + +## OAuth defines four roles + +#### resource owner +An entity capable of granting access to a protected resource. When the resource owner is a person, it is referred to as an end-user. + +#### resource server +The server hosting the protected resources, capable of accepting and responding to protected resource requests using access tokens. + +#### client +An application making protected resource requests on behalf of the resource owner and with its authorization. The term "client" does not imply any particular implementation characteristics (e.g., +whether the application executes on a server, a desktop, or other devices). + +#### authorization server +The server issuing access tokens to the client after successfully authenticating the resource owner and obtaining authorization. + +## Setup OAuth Provider + +System Managers can setup behavior of confirmation message as `Force` or `Auto` in OAuth Provider Settings. +If Force is selected the system will always ask for user's confirmation. If Auto is selected system asks for the confirmation only if there are no active tokens for the user. + +Go to + +> Setup > Integrations > OAuth Provider Settings + + + +### Add Primary Server + +This is the main server hosting all the users. e.g. `https://frappe.io`. To setup this as the main server, go to *Setup* > *Integrations* > *Social Login Keys* and enter `https://frappe.io` in the field `Frappé Server URL`. This URL repeats in all other Frappé servers who connect to this server to authenticate. Effectively, this is the main Identity Provider (IDP). + +Under this server add as many `OAuth Client`(s) as required. + +## Add a Client App + +As a System Manager go to + +> Setup > Integrations > OAuth Client + + + +To add a client fill in the following details + +1. **App Name** : Enter App Name e.g. CAVS +2. **Skip Authorization** : If this is checked, during authentication there won't be me any confirmation message +3. **Scopes** : List of scopes shown to user along with confirmation message. scopes are separated by semicolon ';' +4. **Redirect URIs** : List of Redirect URIs separated by semicolon ';' +5. **Default Redirect URIs** : Default Redirect URI from list of Redirect URIs +6. **Grant Type**: select `Authorization Code` +7. **Response Type**: select `Code` diff --git a/frappe/docs/user/zh/guides/integration/index.md b/frappe/docs/user/zh/guides/integration/index.md new file mode 100755 index 0000000000..b97815866a --- /dev/null +++ b/frappe/docs/user/zh/guides/integration/index.md @@ -0,0 +1,3 @@ +# Integrations + +{index} diff --git a/frappe/docs/user/zh/guides/integration/index.txt b/frappe/docs/user/zh/guides/integration/index.txt new file mode 100755 index 0000000000..a2c88ed7d3 --- /dev/null +++ b/frappe/docs/user/zh/guides/integration/index.txt @@ -0,0 +1,5 @@ +rest_api +how_to_setup_oauth +using_oauth +openid_connect_and_frappe_social_login +google_gsuite diff --git a/frappe/docs/user/zh/guides/integration/openid_connect_and_frappe_social_login.md b/frappe/docs/user/zh/guides/integration/openid_connect_and_frappe_social_login.md new file mode 100644 index 0000000000..f9a4269ec7 --- /dev/null +++ b/frappe/docs/user/zh/guides/integration/openid_connect_and_frappe_social_login.md @@ -0,0 +1,72 @@ +# OpenID Connect and Frappé social login + +## OpenID Connect + +Frappé also uses Open ID connect essential standard for authenticating users. To get `id_token` with `access_token`, pass `openid` as the value for the scope parameter during authorization request. + +If the scope is `openid` the JSON response with `access_token` will also include a JSON Web Token (`id_token`) signed with `HS256` and `Client Secret`. The decoded `id_token` includes the `at_hash`. + +Example Bearer Token with scope `openid` + +``` +{ + "token_type": "Bearer", + "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6Imp3dCJ9.eyJpc3MiOiJodHRwczovL21udGVjaG5pcXVlLmNvbSIsImF0X2hhc2giOiJOQlFXbExJUy1lQ1BXd1d4Y0EwaVpnIiwiYXVkIjoiYjg3NzJhZWQ1YyIsImV4cCI6MTQ3Nzk1NTYzMywic3ViIjoiNWFjNDE2NThkZjFiZTE1MjI4M2QxYTk0YjhmYzcwNDIifQ.1GRvhk5wNoR4GWoeQfleEDgtLS5nvj9nsO4xd8QE-Uk", + "access_token": "ZJD04ldyyvjuAngjgBrgHwxcOig4vW", + "scope": "openid", + "expires_in": 3600, + "refresh_token": "2pBTDTGhjzs2EWRkcNV1N67yw0nizS" +} +``` + +## Frappé social login setup + +In this example there are 2 servers, + +### Primary Server +This is the main server hosting all the users. e.g. `https://frappe.io`. To setup this as the main server, go to *Setup* > *Integrations* > *Social Login Keys* and enter `https://frappe.io` in the field `Frappé Server URL`. This URL repeats in all other Frappé servers who connect to this server to authenticate. Effectively, this is the main Identity Provider (IDP). + +Under this server add as many `OAuth Client`(s) as required. Because we are setting up one app server, add only one `OAuth Client` + +### Frappé App Server +This is the client connecting to the IDP. Go to *Setup* > *Integrations* > *Social Login Keys* on this server and add appropriate values to `Frappé Client ID` and `Frappé Client Secret` (refer to client added in primary server). As mentioned before keep the `Frappé Server URL` as `https://frappe.io` + +Now you will see Frappé icon on the login page. Click on this icon to login with account created in primary server (IDP) `https://frappe.io` + +**Note**: If `Skip Authorization` is checked while registering a client, page to allow or deny the granting access to resource is not shown. This can be used if the apps are internal to one organization and seamless user experience is needed. + +## Steps + +### Part 1 : on Frappé Identity Provider (IDP) + +Login to IDP + + +Add OAuth Client on IDP + + +Set Server URL on IDP + + +### Part 2 : on Frappé App Server + +Set `Frappé Client ID` and `Frappé Client Secret` on App server (refer the client set on IDP) + + +**Note**: Frappé Server URL is the main server where identities from your organization are stored. + +Login Screen on App Server (login with frappe) + + +### Part 3 : Redirected on IDP + +login with user on IDP + + +Confirm Access on IDP + + +### Part 4 : Back on App Server + +Logged in on app server with ID from IDP + diff --git a/frappe/docs/user/zh/guides/integration/rest_api.md b/frappe/docs/user/zh/guides/integration/rest_api.md new file mode 100755 index 0000000000..041545addd --- /dev/null +++ b/frappe/docs/user/zh/guides/integration/rest_api.md @@ -0,0 +1,285 @@ +# REST API + +Frappé ships with an HTTP API. There are two parts of this API. + +1. Remote Procedure Calls (RPC) +2. REST + +## 1. RPC + +A request to an endpoint `/api/method/{dotted.path.to.function}` will call +a whitelisted python function. A function can be whitelisted using the +`frappe.whitelist` decorator. + +For example, Add the following to sample\_app/\_\_init\_\_.py + + @frappe.whitelist(allow_guest=True) + def ping(): + return 'pong' + +GET http://frappe.local:8000**/api/method/sample_app.ping** + +_Response:_ + + { + "message": "pong" + } + + +## 2. REST + +All documents in Frappé are available via a RESTful API with prefix +`/api/resource/`. + +### Login + +To login, you will have to send a POST request to the login method. + +POST http://frappe.local:8000**/api/method/login** + + usr=Administrator&pwd=admin + +_Response:_ + + { + "full_name": "Administrator", + "message": "Logged In" + } + + +Try to make an authenticated request + +GET http://frappe.local:8000**/api/method/frappe.auth.get\_logged\_user** + +_Response:_ + + { + "message": "Administrator" + } + + +### Listing Documents + +To list documents, the URL endpoint is `/api/resource/{doctype}` and the +expected HTTP verb is GET. + +Response is returned as JSON Object and the listing is an array in with the key `data`. + +GET http://frappe.local:8000**/api/resource/Person** + +_Response:_ + + { + "data": [ + { + "name": "000000012" + }, + { + "name": "000000008" + } + ] + } + + +#### Fields + +By default, only name field is included in the listing, to add more fields, you +can pass the fields param to GET request. The param has to be a JSON array. + +GET http://frappe.local:8000**/api/resource/Person/?fields=["name", "first\_name"]** + +_Response:_ + + { + "data": [ + { + "first_name": "Jane", + "name": "000000012" + }, + { + "first_name": "John", + "name": "000000008" + } + ] + } + + +#### Filters + +You can filter the listing using sql conditions by passing them as the `filters` +GET param. Each condition is an array of the format, [{doctype}, {field}, +{operator}, {operand}]. + +Eg, to filter persons with name Jane, pass a param `filters=[["Person", "first_name", "=", "Jane"]]` + +GET http://frappe.local:8000**/api/resource/Person/** + +_Response:_ + { + "data": [ + { + "name": "000000012" + } + ] + } + + +#### Pagination + +All listings are returned paginated by 20 items. To change the page size, you +can pass `limit_page_length`. To request succesive pages, pass `limit_start` as +per your `limit_page_length`. + +For Example, to request second page, pass `limit_start` as 20. + +GET http://frappe.local:8000**/api/resource/DocType** + +_Response:_ + + { + "data": [ + { + "name": "testdoc" + }, + { + "name": "Person" + }, + + ...... + + { + "name": "Website Template" + } + ] + } + + +GET http://frappe.local:8000**/api/resource/DocType?limit_start=20** + +_Response:_ + + { + "data": [ + { + "name": "Website Route" + }, + { + "name": "Version" + }, + { + "name": "Blog Post" + }, + + ...... + + { + "name": "Custom Field" + } + ] + } + + +### CRUD + +#### Create + +You can create a document by sending a `POST` request to the url, `/api/resource/{doctype}`. + +POST http://frappe.local:8000**/api/resource/Person** + +_Body_: + + data={"first_name": "Robert"} + +_Response:_ + + { + "data": { + "first_name": "Robert", + "last_name": null, + "modified_by": "Administrator", + "name": "000000051", + "parent": null, + "creation": "2014-05-04 17:22:38.037685", + "modified": "2014-05-04 17:22:38.037685", + "doctype": "Person", + "idx": null, + "parenttype": null, + "owner": "Administrator", + "docstatus": 0, + "parentfield": null + } + } + +Note: `POST` requests are to be sent along with `X-Frappe-CSRF-Token:` header. + +#### Read + +You can get a document by its name using the url, `/api/resource/{doctype}/{name}` + +For Example, + +GET http://frappe.local:8000**/api/resource/Person/000000012** + +_Response:_ + + { + "data": { + "first_name": "Jane", + "last_name": "Doe", + "modified_by": "Administrator", + "name": "000000012", + "parent": null, + "creation": "2014-04-25 17:56:51.105372", + "modified": "2014-04-25 17:56:51.105372", + "doctype": "Person", + "idx": null, + "parenttype": null, + "owner": "Administrator", + "docstatus": 0, + "parentfield": null + } + } + +### Update + +You can create a document by sending a `PUT` request to the url, +`/api/resource/{doctype}`. This acts like a `PATCH` HTTP request in which you do +not have to send the whole document but only the parts you want to change. + +For Example, + +PUT http://frappe.local:8000**/api/resource/Person/000000008** + +_Body:_ + + data={"last_name": "Watson"} + +_Response:_ + + { + "data": { + "first_name": "John ", + "last_name": "Watson", + "modified_by": "Administrator", + "name": "000000008", + "creation": "2014-04-25 17:26:22.728327", + "modified": "2014-05-04 18:21:45.385995", + "doctype": "Person", + "owner": "Administrator", + "docstatus": 0 + } + } + +### Delete + +You can delete a document by its name by sending a `DELETE` request to the url, +`/api/resource/{doctype}/{name}`. + +For Example, + +DELETE http://frappe.local:8000**/api/resource/Person/000000008** + +_Response:_ + + {"message":"ok"} diff --git a/frappe/docs/user/zh/guides/integration/using_oauth.md b/frappe/docs/user/zh/guides/integration/using_oauth.md new file mode 100644 index 0000000000..d3db092905 --- /dev/null +++ b/frappe/docs/user/zh/guides/integration/using_oauth.md @@ -0,0 +1,109 @@ +# Using OAuth + +Once the client and provider settings are entered, following steps can be used to start using OAuth 2.0 + +### Authorization Code Endpoint + +#### Authorization Request + +URL: +``` +[GET] 0.0.0.0:8000/api/method/frappe.integrations.oauth2.authorize +``` +Params: +``` +client_id = +scope = +response_type = "code" +redirect_uri = +``` + +#### Confirmation Dialog + + + +Click 'Allow' to receive authorization code in redirect uri. + +``` +http://localhost:3000/oauth_code?code=plkj2mqDLwaLJAgDBAkyR1W8Co08Ud +``` +If user clicks 'Deny' receive error +``` +http://localhost:3000/oauth_code?error=access_denied +``` + +### Token Endpoints + +#### Get Access Token + +URL: +``` +[POST] 0.0.0.0:8000/api/method/frappe.integrations.oauth2.get_token +``` +Params: +``` +grant_type = "authorization_code" +code = +redirect_uri = +client_id = +``` +Response: +``` +{ + "access_token": "pNO2DpTMHTcFHYUXwzs74k6idQBmnI", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "cp74cxbbDgaxFuUZ8Usc7egYlhKbH1", + "scope": "project" +} +``` + +#### Refresh Access Token + +URL: +``` +[POST] 0.0.0.0:8000/api/method/frappe.integrations.oauth2.get_token +``` +Params: +``` +grant_type = "refresh_token" +refresh_token = +redirect_uri = +client_id = +``` +Response: +``` +{ + "access_token": "Ywz1iNk0b21iAmjWAYnFWT4CuudHD5", + "token_type": "Bearer", + "expires_in": 3600, + "refresh_token": "PNux3Q8Citr3s9rl2zEsKuU1l8bSN5", + "scope": "project" +} +``` +#### Revoke Token Endpoint + +URL: +``` +[POST] 0.0.0.0:8000/api/method/frappe.integrations.oauth2.revoke_token +``` +Params: +``` +token = +``` +Success Response +``` +status : 200 + +{"message": "success"} +``` +Error Response: +``` +status : 400 + +{"message": "bad request"} +``` + +### Accessing Resource + +Add header `Authorizaton: Bearer ` to Frappé's REST API endpoints to access user's resource diff --git a/frappe/docs/user/zh/guides/integration/webhooks.md b/frappe/docs/user/zh/guides/integration/webhooks.md new file mode 100644 index 0000000000..ff7df4015f --- /dev/null +++ b/frappe/docs/user/zh/guides/integration/webhooks.md @@ -0,0 +1,103 @@ +# Webhooks + +Webhooks are "user-defined HTTP callbacks". You can create webhook which triggers on Doc Event of the selected DocType. When the `doc_events` occurs, the source site makes an HTTP request to the URI configured for the webhook. Users can configure them to cause events on one site to invoke behaviour on another. + +#### Configure Webhook + +To add Webhook go to + +> Integrations > External Documents > Webhook + +Webhook + + + +1. Select the DocType for which hook needs to be triggered e.g. Note +2. Select the DocEvent for which hook needs to be triggered e.g. on_trash +3. Enter a valid request URL. On occurence of DocEvent, POST request with doc's json as data is made to the URL. +4. Optionally you can add headers to the request to be made. Useful for sending api key if required. +5. Optionally you can select fields and set its `key` to be sent as data json + +e.g. Webhook + +- **DocType** : `Quotation` +- **Doc Event** : `on_update` +- **Request URL** : `https://httpbin.org/post` +- **Webhook Data** : + 1. **Fieldname** : `name` and **Key** : `id` + 2. **Fieldname** : `items` and **Key** : `lineItems` + +Note: if no headers or data is present, request will be made without any header or body + +Example response of request sent by frappe server on `Quotation` - `on_update` to https://httpbin.org/post: + +``` +{ + "args": {}, + "data": "{\"lineItems\": [{\"stock_qty\": 1.0, \"base_price_list_rate\": 1.0, \"image\": \"\", \"creation\": \"2017-09-14 13:41:58.373023\", \"base_amount\": 1.0, \"qty\": 1.0, \"margin_rate_or_amount\": 0.0, \"rate\": 1.0, \"owner\": \"Administrator\", \"stock_uom\": \"Unit\", \"base_net_amount\": 1.0, \"page_break\": 0, \"modified_by\": \"Administrator\", \"base_net_rate\": 1.0, \"discount_percentage\": 0.0, \"item_name\": \"I1\", \"amount\": 1.0, \"actual_qty\": 0.0, \"net_rate\": 1.0, \"conversion_factor\": 1.0, \"warehouse\": \"Finished Goods - R\", \"docstatus\": 0, \"prevdoc_docname\": null, \"uom\": \"Unit\", \"description\": \"I1\", \"parent\": \"QTN-00001\", \"brand\": null, \"gst_hsn_code\": null, \"base_rate\": 1.0, \"item_code\": \"I1\", \"projected_qty\": 0.0, \"margin_type\": \"\", \"doctype\": \"Quotation Item\", \"rate_with_margin\": 0.0, \"pricing_rule\": null, \"price_list_rate\": 1.0, \"name\": \"QUOD/00001\", \"idx\": 1, \"item_tax_rate\": \"{}\", \"item_group\": \"Products\", \"modified\": \"2017-09-14 17:09:51.239271\", \"parenttype\": \"Quotation\", \"customer_item_code\": null, \"net_amount\": 1.0, \"prevdoc_doctype\": null, \"parentfield\": \"items\"}], \"id\": \"QTN-00001\"}", + "files": {}, + "form": {}, + "headers": { + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate", + "Connection": "close", + "Content-Length": "1075", + "Host": "httpbin.org", + "User-Agent": "python-requests/2.18.1" + }, + "json": { + "id": "QTN-00001", + "lineItems": [ + { + "actual_qty": 0.0, + "amount": 1.0, + "base_amount": 1.0, + "base_net_amount": 1.0, + "base_net_rate": 1.0, + "base_price_list_rate": 1.0, + "base_rate": 1.0, + "brand": null, + "conversion_factor": 1.0, + "creation": "2017-09-14 13:41:58.373023", + "customer_item_code": null, + "description": "I1", + "discount_percentage": 0.0, + "docstatus": 0, + "doctype": "Quotation Item", + "gst_hsn_code": null, + "idx": 1, + "image": "", + "item_code": "I1", + "item_group": "Products", + "item_name": "I1", + "item_tax_rate": "{}", + "margin_rate_or_amount": 0.0, + "margin_type": "", + "modified": "2017-09-14 17:09:51.239271", + "modified_by": "Administrator", + "name": "QUOD/00001", + "net_amount": 1.0, + "net_rate": 1.0, + "owner": "Administrator", + "page_break": 0, + "parent": "QTN-00001", + "parentfield": "items", + "parenttype": "Quotation", + "prevdoc_docname": null, + "prevdoc_doctype": null, + "price_list_rate": 1.0, + "pricing_rule": null, + "projected_qty": 0.0, + "qty": 1.0, + "rate": 1.0, + "rate_with_margin": 0.0, + "stock_qty": 1.0, + "stock_uom": "Unit", + "uom": "Unit", + "warehouse": "Finished Goods - R" + } + ] + }, + "url": "https://httpbin.org/post" +} +``` \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/portal-development/.md b/frappe/docs/user/zh/guides/portal-development/.md new file mode 100755 index 0000000000..9136f09c29 --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/.md @@ -0,0 +1,5 @@ +# Pages + +You can make your website by adding pages to the `/www` folder of your website. The urls of your site will match the path of your pages within the `/www` folder. + +Pages must be `.html` or `.md` (Markdown) files. Basic HTML template is provided in frappe in `frappe/templates/base_template.html` diff --git a/frappe/docs/user/zh/guides/portal-development/__init__.py b/frappe/docs/user/zh/guides/portal-development/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/portal-development/adding-pages.md b/frappe/docs/user/zh/guides/portal-development/adding-pages.md new file mode 100755 index 0000000000..c9cd0191c3 --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/adding-pages.md @@ -0,0 +1,39 @@ +# Adding Pages + +To add pages, just add `.html` or `.md` files in the `www` folder. The pages must only have the content, not the `` and `` tags. + +You can also write markdown pages + +### Index + +The first file in a folder must be called `index.md` or `index.html` + +Either file must be present for the system to make this a valid folder to build pages. + +### Markdown + + # This is a title + + This is some page content + a [link](/link/to/page) + +### Adding Links + +Links urls to pages can be given without the `.html` extension for example `/home/link` + +### Title + +The first `

` block if present will be the page title if not specified in a special tag. If no `

` or title is specified, the file name will be the title. + +### Adding CSS + +You can also add a `.css` file with the same filename (e.g. `index.css` for `index.md`) that will be rendered with the page. + +### Special Tags + +1. `` will make the page render in Jinja +2. `` will add a custom title +3. `` will not add breadcrumbs in the page +4. `` will enable caching (if you have used Jinja templating) + +{next} diff --git a/frappe/docs/user/zh/guides/portal-development/contents.md b/frappe/docs/user/zh/guides/portal-development/contents.md new file mode 100755 index 0000000000..fc2fa6936c --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/contents.md @@ -0,0 +1,29 @@ +# Table of Contents + +You can add a table of contents by adding `{index}` string on a new line. + +You can also make Previous and Next buttons by adding `previous` or `next` in `{}` + +### Showing contents + + # This is a title + + Hello paragraph + + ### Contents: + + {index} + +{next} + +### Ordering + +You can defining the ordering of pages in index by defining the index.txt file in your folder. The index.txt file must have the names (without extensions) of the pages in that folder indicating the order. + +For example for this folder the `index.txt` looks like: + + adding-pages + ordering + contents + context + building diff --git a/frappe/docs/user/zh/guides/portal-development/context.md b/frappe/docs/user/zh/guides/portal-development/context.md new file mode 100755 index 0000000000..d0fdc3cf63 --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/context.md @@ -0,0 +1,26 @@ +# Dynamic Pages + +You can render pages dynamically using Jinja templating language. To query data, you can update that `context` object that you pass to the template. + +This can be done by adding a `.py` file with the same filename (e.g. `index.py` for `index.md`) with a `get_context` method. + +### Example + +If you want to show a page to see users, make a `users.html` and `users.py` file in the `www/` folder. + +In `users.py`: + + import frappe + def get_context(context): + context.users = frappe.db.sql("select first_name, last_name from `tabUser`") + +In `users.html`: + +

List of Users

+
    + {% for user in users %} +
  1. {{ user.first_name }} {{ user.last_name or "" }}
  2. + {% endfor %} +
+ +{next} diff --git a/frappe/docs/user/zh/guides/portal-development/generators.md b/frappe/docs/user/zh/guides/portal-development/generators.md new file mode 100644 index 0000000000..8bf52d7299 --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/generators.md @@ -0,0 +1,89 @@ +# Generators + +If every document in a table (DocType) corresponds to a web-page, you can setup generators. + +To setup a generator you must: + +1. Add a field `route` that specifies the route of the page +2. Add a condition field to indicate whether a page is viewable or not. +3. Add the doctype name in `website_generators` in `hooks.py` of your app. +4. Subclass the controller from `frappe.website.website_generator.WebsiteGenerator` +5. Create a template for your page +6. Add custom properties (context) for the template +6. Customize route and list view + +Let us see this with the help of an example: + +## Example + +#### 1. Add fields + +We added `published`, `route` in the DocType + +**Note:** The field `route` is mandatory + +Generator fields + +#### 2. Added Website Generator to Hooks + +Since Job Opening is in `erpnext`, we have added to the list of existing generator hooks: + + website_generators = ["Item Group", "Item", "Sales Partner", "Job Opening"] + +If the `website_generators` property does not exist in your hooks.py, add it! + +#### 3. Controller + +We add the `website` property to the **JobOpening** class in `job_opening.py` + +In `get_context`, `parents` property will indicate the breadcrumbs + + from frappe.website.website_generator import WebsiteGenerator + from frappe import _ + + # subclass from WebsiteGenerator, not Document + class JobOpening(WebsiteGenerator): + website = frappe._dict( + template = "templates/generators/job_opening.html", + condition_field = "published", + page_title_field = "job_title", + ) + + def get_context(self, context): + # show breadcrumbs + context.parents = [{'name': 'jobs', 'title': _('All Jobs') }] + +**Note:** Once you do this, you should see the "See in Website" link on the document form. + +#### 4. Add the template + +Add the template in `erpnext/templates/generators/job_opening.html` + + {% raw %}{% extends "templates/web.html" %} + + {% block breadcrumbs %} + {% include "templates/includes/breadcrumbs.html" %} + {% endblock %} + + {% block header %} +

{{ job_title }}

+ {% endblock %} + + {% block page_content %} + +
{{ description }}
+ + + {{ _("Apply Now") }} + + {% endblock %}{% endraw %} + +#### 5. Customizing List View + +If you add a method `get_list_view` in the controller file (job_opening.py), you can set properties for the listview + + def get_list_context(context): + context.title = _("Jobs") + context.introduction = _('Current Job Openings') +{next} diff --git a/frappe/docs/user/zh/guides/portal-development/index.md b/frappe/docs/user/zh/guides/portal-development/index.md new file mode 100755 index 0000000000..bba3ff1fb1 --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/index.md @@ -0,0 +1,17 @@ +# Making Portals + +Frappé has powerful tools to build portals where pages can be dynamically generated using templates (Jinja) and users can be shown records after login + +#### Adding Pages + +You can make your website by adding pages to the `/www` folder of your website. The urls of your site will match the path of your pages within the `/www` folder. + +Pages must be `.html` or `.md` (Markdown) files. Basic HTML template is provided in frappe in `frappe/templates/base_template.html` + +#### Views after Login + +After logging in, the user sees a "My Account" page `/me` where user can access certain documents that are shown via a menu + +The user can view records based on permissions and also add / edit them with **Web Forms** + +{index} diff --git a/frappe/docs/user/zh/guides/portal-development/index.txt b/frappe/docs/user/zh/guides/portal-development/index.txt new file mode 100755 index 0000000000..c0822bc8b0 --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/index.txt @@ -0,0 +1,7 @@ +adding-pages +context +generators +contents +web-forms +ordering +portal-roles diff --git a/frappe/docs/user/zh/guides/portal-development/ordering.md b/frappe/docs/user/zh/guides/portal-development/ordering.md new file mode 100755 index 0000000000..179951739e --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/ordering.md @@ -0,0 +1,13 @@ +# Ordering + +You can defining the ordering of pages in index by defining the index.txt file in your folder. The index.txt file must have the names (without extensions) of the pages in that folder indicating the order. + +For example for this folder the `index.txt` looks like: + + adding-pages + ordering + contents + context + building + +{next} \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/portal-development/portal-roles.md b/frappe/docs/user/zh/guides/portal-development/portal-roles.md new file mode 100644 index 0000000000..2248bb0b55 --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/portal-roles.md @@ -0,0 +1,25 @@ +# Portal Roles + +Version: 7.1+ + +Roles can be assigned to Website Users and they will see menu based on their role + +1. A default role can be set in **Portal Settings** +1. Each Portal Menu Item can have a role associated with it. If that role is set, then only those users having that role can see that menu item +1. Rules can be set for default roles that will be set on default users on hooks + +Portal Settings + +#### Rules for Default Role + +For example if the Email Address matches with a contact id, then set role Customer or Supplier: + + default_roles = [ + {'role': 'Customer', 'doctype':'Contact', 'email_field': 'email_id', + 'filters': {'ifnull(customer, "")': ('!=', '')}}, + {'role': 'Supplier', 'doctype':'Contact', 'email_field': 'email_id', + 'filters': {'ifnull(supplier, "")': ('!=', '')}}, + {'role': 'Student', 'doctype':'Student', 'email_field': 'student_email_id'} + ] + + diff --git a/frappe/docs/user/zh/guides/portal-development/web-forms.md b/frappe/docs/user/zh/guides/portal-development/web-forms.md new file mode 100644 index 0000000000..7cca8de2cc --- /dev/null +++ b/frappe/docs/user/zh/guides/portal-development/web-forms.md @@ -0,0 +1,11 @@ +# Customizing Web Forms + +Web Forms are a powerful way to add forms to your website. Web forms are powerful and scriptable and from Version 7.1+ they also include tables, paging and other utilities + +Web Form + +### Standard Web Forms + +If you check on the "Is Standard" checkbox, a new folder will be created in the `module` of the Web Form for that web form. In this folder, you will see a `.py` and `.js` file that you can customize the web form with. + +{next} \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/reports-and-printing/__init__.py b/frappe/docs/user/zh/guides/reports-and-printing/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/guides/reports-and-printing/getting-information-from-another-document-in-print-format.md b/frappe/docs/user/zh/guides/reports-and-printing/getting-information-from-another-document-in-print-format.md new file mode 100755 index 0000000000..afe87c04f8 --- /dev/null +++ b/frappe/docs/user/zh/guides/reports-and-printing/getting-information-from-another-document-in-print-format.md @@ -0,0 +1,9 @@ +# Getting Information From Another Document In Print Format + +In a print format, you can get data from another document. For example in if you have a fields called `sales_order` in Sales Invoice, then you can get the sales order details using `frappe.get_doc`: + +{% raw %} + {% set sales_order_doc = frappe.get_doc("Sales Order", sales_order) %} + + {{ sales_order_doc.customer }} +{% endraw %} diff --git a/frappe/docs/user/zh/guides/reports-and-printing/how-to-make-query-report.md b/frappe/docs/user/zh/guides/reports-and-printing/how-to-make-query-report.md new file mode 100755 index 0000000000..c4c047a63f --- /dev/null +++ b/frappe/docs/user/zh/guides/reports-and-printing/how-to-make-query-report.md @@ -0,0 +1,58 @@ +# How To Make Query Report + +You can create tabulated reports using complex SQL queries by creating a new Report. These reports can be created by a System Manager and are stored in the Database + +> Note: You will need System Manager Permissions for this. + +To create a new Query Report: + +### 1. Create a new Report + +Query Report + +1. Set type as "Query Report" +1. Set the reference DocType - Users that have access to the reference DocType will have access to the report +1. Set the module - The report will appear in the "Custom Reports" section of the module. +1. Add your Query + +### 2. Set the Query + +You can define complex queries such as: + + + SELECT + `tabProduction Order`.name as "Production Order:Link/Production Order:200", + `tabProduction Order`.creation as "Date:Date:120", + `tabProduction Order`.production_item as "Item:Link/Item:150", + `tabProduction Order`.qty as "To Produce:Int:100", + `tabProduction Order`.produced_qty as "Produced:Int:100" + FROM + `tabProduction Order` + WHERE + `tabProduction Order`.docstatus=1 + AND ifnull(`tabProduction Order`.produced_qty,0) < `tabProduction Order`.qty + AND EXISTS (SELECT name from `tabStock Entry` where production_order =`tabProduction Order`.name) + +1. To format the columns, set labels for each column in the format: [Label]:[Field Type]/[Options]:[Width] + +### 3. Check the Report + +Query Report + +### 4. Advanced (adding filters) + +If you are making a standard report, you can add filters in your query report just like [script reports](https://frappe.io/docs/user/en/guides/reports-and-printing/how-to-make-script-reports) by adding a `.js` file in your query report folder. To include filters in your query, use `%(filter_key)s` where your filter value will be shown. + +For example + + SELECT ... FROM ... WHERE item_code = %(item_code)s ORDER BY ... + +--- + +### Note: Standard Script Report + +If you are developing a standard report for an app, make sure to set "Is Standard" as "Yes" + + + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/reports-and-printing/how-to-make-script-reports.md b/frappe/docs/user/zh/guides/reports-and-printing/how-to-make-script-reports.md new file mode 100755 index 0000000000..80522d435b --- /dev/null +++ b/frappe/docs/user/zh/guides/reports-and-printing/how-to-make-script-reports.md @@ -0,0 +1,51 @@ +# Script Report + +You can create tabulated reports using server side scripts by creating a new Report. + +> Note: You will need Administrator Permissions for this. + +Since these reports give you unrestricted access via Python scripts, they can only be created by Administrators. The script part of the report becomes a part of the repository of the application. If you have not created an app, [read this](https://frappe.io/docs/user/en/guides/app-development/). + +> Note: You must be in [Developer Mode](https://frappe.io/docs/user/en/guides/app-development/how-enable-developer-mode-in-frappe) to do this + +### 1. Create a new Report + +Script Report + +1. Set Report Type as "Script Report" +1. Set "Is Standard" as "Yes" +1. Select the Module in which you want to add this report +1. In the module folder (for example if it is Accounts in ERPnext the folder will be `erpnext/accounts/report/[report-name]`) you will see that templates for the report files will be created. +1. In the `.js` file, you can set filters for the reports +1. In the `.py` file, you can write the script that will generate the report + +### 2. Add Filters + +You can add filters in the `.js`. See an example below: + + frappe.query_reports["Accounts Receivable"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("company") + }, + ] + } + +1. These properties are the same as you would set in a DocField in a DocType + +### 3. Add the Script + +In the `.py` file you can add the script for generating the report. + +1. In the `execute` method, two lists `columns` and `data` are returned +2. Columns must be a list of labels in the same format as query report. **[Label]:[Field Type]/[Options]:[Width]**. For example `Item:Link/Item:150` +3. You can use all server side modules to build your report. +4. For example see existing reports. ([Balance Sheet](https://github.com/frappe/erpnext/blob/develop/erpnext/accounts/report/balance_sheet/balance_sheet.py)) + +### 4. Commit and Push the app + +Don't forget to commit and push your app. diff --git a/frappe/docs/user/zh/guides/reports-and-printing/index.md b/frappe/docs/user/zh/guides/reports-and-printing/index.md new file mode 100755 index 0000000000..42a169f9f0 --- /dev/null +++ b/frappe/docs/user/zh/guides/reports-and-printing/index.md @@ -0,0 +1,3 @@ +# Reports and Printing + +{index} diff --git a/frappe/docs/user/zh/guides/reports-and-printing/index.txt b/frappe/docs/user/zh/guides/reports-and-printing/index.txt new file mode 100755 index 0000000000..ec34e2b102 --- /dev/null +++ b/frappe/docs/user/zh/guides/reports-and-printing/index.txt @@ -0,0 +1,5 @@ +how-to-make-script-reports +how-to-make-query-report +getting-information-from-another-document-in-print-format +where-do-i-find-standard-print-formats +print-format-for-reports diff --git a/frappe/docs/user/zh/guides/reports-and-printing/print-format-for-reports.md b/frappe/docs/user/zh/guides/reports-and-printing/print-format-for-reports.md new file mode 100755 index 0000000000..07a7bc222b --- /dev/null +++ b/frappe/docs/user/zh/guides/reports-and-printing/print-format-for-reports.md @@ -0,0 +1,70 @@ +# Report Print Formats + +In version 4.1 we introduce Report Print Formats. These are HTML templates that you can use to format Query Report data for printing. + +### 1. Creating New Print Formats + +To create a new Print Format, just drop in a `.html` file in the folder of the query report. For example, for the [General Ledger](https://github.com/frappe/erpnext/tree/develop/erpnext/accounts/report/general_ledger) report in ERPNext, you can drop in a file called `general_ledger.html` along side the `.js` and `.py` files. + +##### Tree Of `erpnext/accounts/general_ledger` + + general_ledger/ + ├── __init__.py + ├── general_ledger.html + ├── general_ledger.js + ├── general_ledger.json + └── general_ledger.py + + +### 2. Templating + +For templating, we use an adapted version of [John Resig's microtemplating script](http://ejohn.org/blog/javascript-micro-templating/). If you know Javascript, it is very easy to follow this templating language. + +##### Here are some examples (from John Resig's Blog): + +Example: Properities: + +
"> +
+ +
+
+ +
+
+ +Example: Code structures, Loops + + <% for ( var i = 0; i < users.length; i++ ) { %> +
  • <%=users[i].name%>
  • + <% } %> + +> **Note**: It is important to note that you should not use single quotes (') in your template as the engine cannot handle them effectively. + +### 3. Data + +Data is available to the template as: + +- `data`: this is a list of records, with each record as an object with slugified properties from labels. For example "Posting Date" becomes "posting_date" +- `filters`: filters set in the report +- `report`: reportview object + +### 4. Example + +Here is how the General Ledger Report is built: + +[General Ledger Print Format Template](https://github.com/frappe/erpnext/blob/develop/erpnext/accounts/report/general_ledger/general_ledger.html) + +Here is what the report looks like: + +General Ledger + +##### Comments: + +1. [Bootstrap Stylesheet](http://getbootstrap.com) is pre-loaded. +1. You can use all global functions like `fmt_money` and dateutil. +1. Translatable strings should be written as `__("text")` +1. You can create modules and import using `{% raw %}{% include "templates/includes/formats/common_format" %}{% endraw %}` + + \ No newline at end of file diff --git a/frappe/docs/user/zh/guides/reports-and-printing/where-do-i-find-standard-print-formats.md b/frappe/docs/user/zh/guides/reports-and-printing/where-do-i-find-standard-print-formats.md new file mode 100755 index 0000000000..c1816751dd --- /dev/null +++ b/frappe/docs/user/zh/guides/reports-and-printing/where-do-i-find-standard-print-formats.md @@ -0,0 +1,42 @@ +# Where Do I Find Standard Print Formats + +Standard Print formats are auto generated from the layout of the DocType. You can customize the standard format by +
    +
    + +

    1. Customizing Standard Print

    +Go to Setup > Customize > Customize Form View and you can: +
    +
      +
    1. Re-arranging fields by dragging and dropping
    2. +
    3. Add static elements by adding HTML type fields and adding your HTML in Options + +
    4. +
    5. Hiding fields by setting the Print Hide property
    6. +
    +
    + +

    2. Creating new layouts based on Print Formats

    + +

    As there are not templates that are generated for standard Print Formats, you will have to create new templates from scratch using the Jinja Templating Language via

    +

    Setup > Printing and Branding > Print Format + +

    +
      +
    1. See Print Format help. +
      +
    2. +
    3. You can use the Bootstrap CSS framework to layout your print formats +
      +
    4. +
    +
    +

    Tip: You can import Standard Template macros for building your print formats. + +

    +

    Example, adding the standard header: +
    +

    +
    {% raw %}{%- from "templates/print_formats/standard_macros.html" import add_header -%}
    +{{ add_header() }}{% endraw %}
    +
    diff --git a/frappe/docs/user/zh/index.md b/frappe/docs/user/zh/index.md new file mode 100755 index 0000000000..8136e22770 --- /dev/null +++ b/frappe/docs/user/zh/index.md @@ -0,0 +1,5 @@ +# 使用 Frappé 开发应用 + +{index} + + diff --git a/frappe/docs/user/zh/index.txt b/frappe/docs/user/zh/index.txt new file mode 100755 index 0000000000..85c302b8cf --- /dev/null +++ b/frappe/docs/user/zh/index.txt @@ -0,0 +1,4 @@ +tutorial +bench +guides +videos diff --git a/frappe/docs/user/zh/tutorial/__init__.py b/frappe/docs/user/zh/tutorial/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/tutorial/app.md b/frappe/docs/user/zh/tutorial/app.md new file mode 100755 index 0000000000..e236bbe67f --- /dev/null +++ b/frappe/docs/user/zh/tutorial/app.md @@ -0,0 +1,9 @@ +# 什么是应用 + +Frappé 中的应用只是标准的 Python 应用。 您可以采用与标准 Python 应用相同的方式来构造 Frappé 应用。 对于部署,Frappé 使用标准的 Python Setuptools,因此您可以在任何计算机上轻松地进行应用移植和安装。 + +Frappé 框架提供了 WSGI 接口,您可以使用内置的 Werkzeug 服务进行开发。 对于生产环境的实施,我们建议使用 Nginx 和 Gunicorn。 + +Frappé 也拥有多租户架构。 这意味着您可以在安装中运行多个 "站点",每个都可以为不同的应用和用户提供服务。 每个站点的数据库也是独立的。 + +{next} \ No newline at end of file diff --git a/frappe/docs/user/zh/tutorial/before.md b/frappe/docs/user/zh/tutorial/before.md new file mode 100755 index 0000000000..4b887539d6 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/before.md @@ -0,0 +1,78 @@ +# 在您开始之前 + +

    帮助您开始使用 Frappé 创建应用的资源列表

    + +--- + +#### 1. Python + +Frappé 使用 Python (v2.7) 进行服务器端编程。 在您开始使用 Frappé 构建应用之前,强烈建议您学习 Python。 + +要编写高质量的服务器端代码,还必须包括自动测试。 + +相关资源: + + 1. [Codecademy Python 教程](https://www.codecademy.com/learn/python) + 1. [官方 Python 教程](https://docs.python.org/2.7/tutorial/index.html) + 1. [测试驱动开发基础](http://code.tutsplus.com/tutorials/beginning-test-driven-development-in-python--net-30137) + +--- + +#### 2. MariaDB / MySQL + +要使用 Frappé 创建数据库驱动的应用,您必须了解数据库管理的基本知识,比如如何安装、登录、创建新数据库以及基本的 SQL 查询。 + +相关资源: + + 1. [Codecademy SQL 教程](https://www.codecademy.com/learn/learn-sql) + 1. [DigitalOcean 基础 MySQL 教程](https://www.digitalocean.com/community/tutorials/a-basic-mysql-tutorial) + 1. [MariaDB 入门](https://mariadb.com/kb/en/mariadb/documentation/getting-started/) + +--- + +#### 3. HTML / CSS + +如果要使用 Frappé 构建用户界面,您需要学习基本的 HTML/CSS 和 Boostrap CSS 框架。 + +相关资源: + + 1. [Codecademy HTML/CSS 教程](https://www.codecademy.com/learn/learn-html-css) + 1. [Bootstrap 入门](https://getbootstrap.com/getting-started/) + +--- + +#### 4. JavaScript 和 jQuery + +要自定义表单并创建丰富的用户界面,您应该学习 JavaScript 和流行的 jQuery。 + + +相关资源: + + 1. [Codecademy JavaScript 教程](https://www.codecademy.com/learn/learn-javascript) + 1. [Codecademy jQuery 教程](https://www.codecademy.com/learn/jquery) +--- + +#### 5. Jinja 模板 + +如果您要自定义打印模板或 Web 页面,则需要了解 Jinja 模板语言。 这是创建动态 Web 页面 (HTML) 的一种简单方法。 + +相关资源: + + 1. [Jinja 模板入门](https://realpython.com/blog/python/primer-on-jinja-templating/) + 1. [官方文档](http://jinja.pocoo.org/) + +--- + +#### 6. Git 和 GitHub + +了解如何使用 Git 和 GitHub 为开源项目做出贡献,这两个工具可以帮助您管理代码并与他人共享。 + +相关资源: + + 1. [Git 基础教程](https://try.github.io) + 2. [如何贡献开源](https://opensource.guide/how-to-contribute/) + +--- + +当您准备好时,您可以尝试使用 Frappé [构建示例应用](/docs/user/en/tutorial/app)。 + diff --git a/frappe/docs/user/zh/tutorial/bench.md b/frappe/docs/user/zh/tutorial/bench.md new file mode 100755 index 0000000000..cfe65f2b8e --- /dev/null +++ b/frappe/docs/user/zh/tutorial/bench.md @@ -0,0 +1,11 @@ +# 安装 Frappé Bench + +在类 Unix 系统上设置 Frappé 最简单的方法是使用 frappe-bench。 阅读有关如何使用 Frappé Bench 安装的详细说明。 + +> [https://github.com/frappe/bench](https://github.com/frappe/bench) + +有了 Frappé Bench,您将能够设置并服务多个应用和站点,它还将设置一个 Python Virtualenv,以便您可以有一个独立的环境来运行您的应用 (并不会与其他开发环境的版本冲突)。 + +还将安装 `bench` 命令行工具,这将帮助您开发及管理安装。 + +{next} diff --git a/frappe/docs/user/zh/tutorial/conclusion.md b/frappe/docs/user/zh/tutorial/conclusion.md new file mode 100755 index 0000000000..bc254ba5d9 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/conclusion.md @@ -0,0 +1,7 @@ +# Conclusion + + +We hope this will give you an overview of how applications are developed in Frappé. The objective was to briefly touch on the various aspects of application development and give a broad overview. To get help on specific issues, look at the API. + +For help, join the community at the [chat channel on Gitter](https://gitter.im/frappe/erpnext) or the [developer forum](https://discuss.erpnext.com) + diff --git a/frappe/docs/user/zh/tutorial/controllers.md b/frappe/docs/user/zh/tutorial/controllers.md new file mode 100755 index 0000000000..09edce15d0 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/controllers.md @@ -0,0 +1,59 @@ +# Controllers + +Next step would be adding methods and event handlers to models. In the app, we should ensure that if a Library Transaction is made, the Article in question must be in stock and the member loaning the Article must have a valid membership. + +For this, we can write a validation just before the Library Transaction object is saved. To do this, open the `library_management/doctype/library_transaction/library_transaction.py` template. + +This file is the controller for the Library Transaction object. In this you can write methods for: + +1. `before_insert` +1. `validate` (before inserting or updating) +1. `on_update` (after saving) +1. `on_submit` (when document is set as submitted) +1. `on_cancel` +1. `on_trash` (before it is about to be deleted) + +You can write methods for these events and they will be called by the framework when the document is saved etc. + +Here is the finished controller: + + from __future__ import unicode_literals + import frappe + from frappe import _ + from frappe.model.document import Document + + class LibraryTransaction(Document): + def validate(self): + last_transaction = frappe.get_list("Library Transaction", + fields=["transaction_type", "transaction_date"], + filters = { + "article": self.article, + "transaction_date": ("<=", self.transaction_date), + "name": ("!=", self.name) + }) + if self.transaction_type=="Issue": + msg = _("Article {0} {1} has not been recorded as returned since {2}") + if last_transaction and last_transaction[0].transaction_type=="Issue": + frappe.throw(msg.format(self.article, self.article_name, + last_transaction[0].transaction_date)) + else: + if not last_transaction or last_transaction[0].transaction_type!="Issue": + frappe.throw(_("Cannot return article not issued")) + +In this script: + +1. We get the last transaction before the current transaction date using the query function `frappe.get_list` +1. If the last transaction is something we don't like we throw an exception using `frappe.throw` +1. We use `_("text")` method to identify translatable strings. + +Check if your validations work by creating new records + +Transaction + +#### Debugging + +To Debug, always keep your JS Console open. Lookout for both Javascript and server tracebacks. + +Also check your terminal window for exceptions. Any **500 Internal Server Errors** will get printed in your terminal where on which your server is running. + +{next} diff --git a/frappe/docs/user/zh/tutorial/doctype-directory-structure.md b/frappe/docs/user/zh/tutorial/doctype-directory-structure.md new file mode 100755 index 0000000000..b5e1b10006 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/doctype-directory-structure.md @@ -0,0 +1,31 @@ +# DocType Directory Structure + +After saving the DocTypes, check that the model `.json` and `.py` files are created in the `apps/library_management/library_management` module. The directory structure after creating the models should look like this: + + . + ├── MANIFEST.in + ├── README.md + ├── library_management + .. + │   ├── library_management + │   │   ├── __init__.py + │   │   └── doctype + │   │   ├── __init__.py + │   │   ├── article + │   │   │   ├── __init__.py + │   │   │   ├── article.json + │   │   │   └── article.py + │   │   ├── library_member + │   │   │   ├── __init__.py + │   │   │   ├── library_member.json + │   │   │   └── library_member.py + │   │   ├── library_membership + │   │   │   ├── __init__.py + │   │   │   ├── library_membership.json + │   │   │   └── library_membership.py + │   │   └── library_transaction + │   │   ├── __init__.py + │   │   ├── library_transaction.json + │   │   └── library_transaction.py + +{next} diff --git a/frappe/docs/user/zh/tutorial/doctypes.md b/frappe/docs/user/zh/tutorial/doctypes.md new file mode 100755 index 0000000000..889f923b3f --- /dev/null +++ b/frappe/docs/user/zh/tutorial/doctypes.md @@ -0,0 +1,96 @@ +# DocType + +After creating the Roles, let us create the **DocTypes** + +To create a new **DocType**, go to: + +> Developer > Documents > Doctype > New + +New Doctype + +In the DocType, first the Module, which in our case is **Library Management** + +#### Adding Fields + +In the Fields Table, you can add the fields (properties) of the DocType (Article). + +Fields are much more than database columns, they can be: + +1. Columns in the database +1. Layout helpers (section / column breaks) +1. Child tables (Table type field) +1. HTML +1. Actions (button) +1. Attachments or Images + +Let us add the fields of the Article. + +Adding Fields + +When you add fields, you need to enter the **Type**. **Label** is optional for Section Break and Column Break. **Name** (`fieldname`) is the name of the database table column and also the property of the controller. This has to be *code friendly*, i.e. it has to have small cases are _ instead of " ". If you leave the Fieldname blank, it will be automatically set when you save it. + +You can also set other properties of the field like whether it is mandatory, read only etc. + +We can add the following fields: + +1. Article Name (Data) +2. Author (Data) +3. Description +4. ISBN +5. Status (Select): For Select fields, you will enter the Options. Enter **Issued** and **Available** each on a new line in the Options box. See diagram below +6. Publisher (Data) +7. Language (Data) +8. Image (Attach Image) + + +#### Add Permissions + +After adding the fields, hit done and add a new row in the Permission Rules section. For now, let us give Read, Write, Create, Delete and Report access to **Librarian**. Frappé has a finely grained Role based permission model. You can also change permissions later using the **Role Permissions Manager** from **Setup**. + +Adding Permissions + +#### Saving + +Click on the **Save** button. When the button is clicked, a popup will ask you for the name. Give it the name **Article** and save the DocType. + +Now login into mysql and check the database table created: + + $ bench mysql + Welcome to the MariaDB monitor. Commands end with ; or \g. + Your MariaDB connection id is 3931 + Server version: 5.5.36-MariaDB-log Homebrew + + Copyright (c) 2000, 2014, Oracle, Monty Program Ab and others. + + Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. + + MariaDB [library]> DESC tabArticle; + +--------------+--------------+------+-----+---------+-------+ + | Field | Type | Null | Key | Default | Extra | + +--------------+--------------+------+-----+---------+-------+ + | name | varchar(255) | NO | PRI | NULL | | + | creation | datetime(6) | YES | | NULL | | + | modified | datetime(6) | YES | | NULL | | + | modified_by | varchar(40) | YES | | NULL | | + | owner | varchar(60) | YES | | NULL | | + | docstatus | int(1) | YES | | 0 | | + | parent | varchar(255) | YES | MUL | NULL | | + | parentfield | varchar(255) | YES | | NULL | | + | parenttype | varchar(255) | YES | | NULL | | + | idx | int(8) | YES | | NULL | | + | article_name | varchar(255) | YES | | NULL | | + | status | varchar(255) | YES | | NULL | | + | description | text | YES | | NULL | | + | image | varchar(255) | YES | | NULL | | + | publisher | varchar(255) | YES | | NULL | | + | isbn | varchar(255) | YES | | NULL | | + | language | varchar(255) | YES | | NULL | | + | author | varchar(255) | YES | | NULL | | + +--------------+--------------+------+-----+---------+-------+ + 18 rows in set (0.00 sec) + + +As you can see, along with the DocFields, several standard columns have also been added to the table. Important to note here are, the primary key, `name`, `owner`(the user who has created the record), `creation` and `modified` (timestamps for creation and last modification). + +{next} + diff --git a/frappe/docs/user/zh/tutorial/form-client-scripting.md b/frappe/docs/user/zh/tutorial/form-client-scripting.md new file mode 100755 index 0000000000..d4673c55d4 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/form-client-scripting.md @@ -0,0 +1,39 @@ +# Form Client Scripting + +## Scripting Forms + +Now we have created a basic system that works out of the box without us having to write any code. Let us now write some scripts to make the application richer and add validations so that the user does not enter wrong data. + +### Client Side Scripting + +In the **Library Transaction** DocType, we have only field for Member Name. We have not made two fields. Now this could well be two fields (and probably should), but for the sake of example, let us consider we have to implement this. To do this we would have to write a event handler for the event when the user selects the `library_member` field and then access the member resource from the server using REST API and set the values in the form. + +To start the script, in the `library_management/doctype/library_transaction` folder, create a new file `library_transaction.js`. This file will be automatically executed when the first Library Transaction is opened by the user. So in this file, we can bind events and write other functions. + +#### library_transaction.js + + frappe.ui.form.on("Library Transaction", "library_member", + function(frm) { + frappe.call({ + "method": "frappe.client.get", + args: { + doctype: "Library Member", + name: frm.doc.library_member + }, + callback: function (data) { + frappe.model.set_value(frm.doctype, + frm.docname, "member_name", + data.message.first_name + + (data.message.last_name ? + (" " + data.message.last_name) : "")) + } + }) + }); + +1. **frappe.ui.form.on(*doctype*, *fieldname*, *handler*)** is used to bind a handler to the event when the property library_member is set. +1. In the handler, we trigger an AJAX call to `frappe.client.get`. In response we get the requested object as JSON. [Learn more about the API](/frappe/user/en/guides/integration/rest_api). +1. Using **frappe.model.set_value(*doctype*, *name*, *fieldname*, *value*)** we set the value in the form. + +**Note:** To check if your script works, remember to 'reload' the page before testing your script. Client script changes are not automatically picked up when you are in developer mode. + +{next} diff --git a/frappe/docs/user/zh/tutorial/index.md b/frappe/docs/user/zh/tutorial/index.md new file mode 100755 index 0000000000..0867dab6c7 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/index.md @@ -0,0 +1,33 @@ +# Frappé 教程 + +在本指南中,我们将向您展示如何使用 **Frappé** 从头开始创建一个应用。 以图书馆管理系统为例,我们将涵盖: + +1. 安装 +2. 制作新的应用 +3. 制作模块 +4. 创建用户和记录 +5. 创建控制器 +6. 创建 Web 视图 +7. 配置钩子和任务 + +## 这是给谁的? + +本指南适用于熟悉 Web 应用生成和维护的软件开发人员。 Frappé 框架是建立在 Python 上,并使用 MariaDB 数据库以及 HTML/CSS/Javascript 创建 Web 视图。 如果您熟悉所有这些技术将会非常有帮助。 如果您以前从未使用过 Python,则在使用本指南之前,至少应先阅读快速教程。 + +Frappé 在 GitHub 上使用 git 版本控制系统。 您熟悉基本的 git 操作,并在 GitHub 上有一个帐户来管理您的应用也同样重要。 + +## 示例 + +在本指南中,我们将构建一个简单的 **图书馆管理** 应用。 在这个应用中,我们将有如下模型: + +1. 物品 (可以借出的图书或任何其他物品) +2. 图书馆用户 +3. 图书馆交易 (借出或归物品) +4. 图书馆会籍 (允许会员交易期间) +5. 图书馆管理设置 (如借出期限的全局设置) + +图书管理员的用户界面(UI)将会是 **Frappé 桌面**,一个内置的根据模型、角色和权限自动生成的基于浏览器的 UI 环境。 + +我们还将为图书馆创建 Web 视图,用户可以从网站浏览物品。 + +{index} \ No newline at end of file diff --git a/frappe/docs/user/zh/tutorial/index.txt b/frappe/docs/user/zh/tutorial/index.txt new file mode 100755 index 0000000000..1fed6aed93 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/index.txt @@ -0,0 +1,19 @@ +before +app +bench +new-app +setting-up-the-site +start +models +roles +doctypes +naming-and-linking +doctype-directory-structure +users-and-records +form-client-scripting +controllers +reports +web-views +single-doctypes +task-runner +conclusion diff --git a/frappe/docs/user/zh/tutorial/models.md b/frappe/docs/user/zh/tutorial/models.md new file mode 100755 index 0000000000..0dbb1b1b32 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/models.md @@ -0,0 +1,19 @@ +# Making Models + +The next step is to create the models as we discussed in the introduction. In Frappé, models are called **DocTypes**. You can create new DocTypes from the Desk UI. **DocTypes** are made of fields called **DocField** and role based permissions are integrated into the models, these are called **DocPerms**. + +When a DocType is saved, a new table is created in the database. This table is named as `tab[doctype]`. + +When you create a **DocType** a new folder is created in the **Module** and a model JSON file and a controller template in Python are automatically created. When you update the DocType, the JSON model file is updated and whenever `bench migrate` is executed, it is synced with the database. This makes it easy to propagate schema changes and migrate. + +### Developer Mode + +To create models, you must set `developer_mode` as 1 in the `site_config.json` file located in /sites/library and execute command `bench clear-cache` or use the user menu in UI and click on "Reload" for the changes to take effect. You should now see the "Developer" app on your desk + + { + "db_name": "bcad64afbf", + "db_password": "v3qHDeVKvWVi7s97", + "developer_mode": 1 + } + +{next} diff --git a/frappe/docs/user/zh/tutorial/naming-and-linking.md b/frappe/docs/user/zh/tutorial/naming-and-linking.md new file mode 100755 index 0000000000..546af3d50d --- /dev/null +++ b/frappe/docs/user/zh/tutorial/naming-and-linking.md @@ -0,0 +1,71 @@ +# DocType Naming and Linking + +Then let us create the other DocType and save it too: + +1. Library Member (First Name, Last Name, Email Address, Phone, Address) + +Doctype Saved + + +#### Naming of DocTypes + +DocTypes can be named in different ways: + +1. Based on a field +1. Based on a series +1. By controller (code) +1. Prompt + +This can be set by entering the **Autoname** field. For controller, leave blank. + +> **Search Fields**: A DocType may be named on a series but it still needs to be searched by name. In our case, the Article will be searched by the title or the author name. So this can be entered in search field. + +Autonaming and Search Field + +#### Link and Select Fields + +Foreign keys are specified in Frappé as **Link** type fields. The target DocType must be mentioned in the Options text area. + +In our example, in the Library Transaction DocType, we have to link both the Library Member and the Article. + +**Note:** Remeber that Link fields are not automatically set as Foreign Keys in the MariaDB database, because that will implicitly index the column. This may not be optimum hence the Foreign Key validation is done by the Framework. + +Link Field + +For select fields, as we mentioned earlier, add the various options in the **Options** input box, each option on a new row. + +Select Field + +Similary complete making the other models. + +#### Linked Values + +A standard pattern is when you select an ID, say **Library Member** in **Library Membership**, then the Member's first and last names should be copied into relevant fields in the Library Membership Transaction. + +To do this, we can use Read Only fields and in options, we can set the the name of the link and the fieldname of the property we want to fetch. For this example in **Member First Name** we can set `library_member.first_name` + +Fetch values + +### Complete the Models + +In the same way, you can complete all the models so that the final fields look like this: + +#### Article + +Article + +#### Library Member + +Library Member + +#### Library Membership + +Library Membership + +#### Library Transaction + +Library Transaction + +> Make sure to give permissions to **Librarian** on each DocType + +{next} diff --git a/frappe/docs/user/zh/tutorial/new-app.md b/frappe/docs/user/zh/tutorial/new-app.md new file mode 100755 index 0000000000..f72edcf21a --- /dev/null +++ b/frappe/docs/user/zh/tutorial/new-app.md @@ -0,0 +1,55 @@ +# 制作新的应用 + +一旦安装了 bench,你会看到两个主要的文件夹,`apps` 和 `sites`。 所有的应用将安装在 apps 中。 + +要制作新的应用,请转到您的 bench 文件夹,然后运行 `bench new-app {app_name}`,填写有关该应用的详细信息。 这将为您创建样板应用。 + + $ bench new-app library_management + App Title (defaut: Lib Mgt): Library Management + App Description: App for managing Articles, Members, Memberships and Transactions for Libraries + App Publisher: Frappé + App Email: info@frappe.io + App Icon (default 'octicon octicon-file-directory'): octicon octicon-book + App Color (default 'grey'): #589494 + App License (default 'MIT'): GNU General Public License + +### 应用结构 + +该应用将创建在一个名为 `library_management` 的文件夹中,并具有以下结构: + + . + ├── MANIFEST.in + ├── README.md + ├── library_management + │   ├── __init__.py + │   ├── config + │   │   ├── __init__.py + │   │   └── desktop.py + │   ├── hooks.py + │   ├── library_management + │   │   └── __init__.py + │   ├── modules.txt + │   ├── patches.txt + │   └── templates + │   ├── __init__.py + │   ├── generators + │   │   └── __init__.py + │   ├── pages + │   │   └── __init__.py + │   └── statics + ├── license.txt + ├── requirements.txt + └── setup.py + +1. `config` 文件夹包含应用配置信息。 +1. `desktop.py` 是可以添加到工作台 (Desk) 的桌面图标。 +1. `hooks.py` 是指与环境和其他应用进行集成的地方。 + +1. `library_management` (内部) 是引导**模块**。 在 Frappé 中,**模块**是模型和控制器文件所在的地方。 +1. `modules.txt` 包含应用中**模块**的列表。 当您创建新模块时,需要在该文件中更新它。 +1. `patches.txt` 是编写迁移补丁的地方。 它们是使用点表示法的 Python 模块引用。 +1. `templates` 是维护 Web 视图模板的文件夹。 **登录**以及其他标准页面的模板由 Frappé 处理。 +1. `generators` 是维护模型模板的地方,其中每个模型实例都有一个分离的 web 路由,例如**博客帖子**每个帖子都有其唯一的 Web URL。 在 Frappé 中,使用的模板引擎是 Jinja2。 +1. `pages` 是维护单个路由模板的位置。 例如 "/blog" 类型的页面。 + +{next} diff --git a/frappe/docs/user/zh/tutorial/reports.md b/frappe/docs/user/zh/tutorial/reports.md new file mode 100755 index 0000000000..6e2a2b6c42 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/reports.md @@ -0,0 +1,7 @@ +# Reports + +You can also click on the Reports text on the sidebar (left) to see tabulated records + +Report + +{next} diff --git a/frappe/docs/user/zh/tutorial/roles.md b/frappe/docs/user/zh/tutorial/roles.md new file mode 100755 index 0000000000..42b8246bec --- /dev/null +++ b/frappe/docs/user/zh/tutorial/roles.md @@ -0,0 +1,14 @@ +# Creating Roles + +Before creating Models, we must create Roles so that we can set permissions on the Model. There are two Roles we will create: + +1. Librarian +1. Library Member + +To create a new Role, go to: + +> Setup > Users > Role > New + +Adding Roles + +{next} diff --git a/frappe/docs/user/zh/tutorial/setting-up-the-site.md b/frappe/docs/user/zh/tutorial/setting-up-the-site.md new file mode 100755 index 0000000000..2a823dc5f3 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/setting-up-the-site.md @@ -0,0 +1,67 @@ +# Setting up the Site + +Let us create a new site and call it `library`. + +*Note: Before you create any new site, you need to activate the Barracuda storage engine on your MariaDB installation.* +*Copy the following default ERPNext database settings into your `my.cnf` file.* + + [mysqld] + innodb-file-format=barracuda + innodb-file-per-table=1 + innodb-large-prefix=1 + character-set-client-handshake = FALSE + character-set-server = utf8mb4 + collation-server = utf8mb4_unicode_ci + + [mysql] + default-character-set = utf8mb4 + +You can then install a new site, by the command `bench new-site library`. + +This will create a new database and site folder and install `frappe` (which is also an application!) in the new site. The `frappe` application has two built-in modules **Core** and **Website**. The Core module contains the basic models for the application. Frappé is a batteries included framework and comes with a lot of built-in models. These models are called **DocTypes**. More on that later. + + $ bench new-site library + MySQL root password: + Installing frappe... + Updating frappe : [========================================] + Updating country info : [========================================] + Set Administrator password: + Re-enter Administrator password: + Installing fixtures... + *** Scheduler is disabled *** + +### Site Structure + +A new folder called `library` will be created in the `sites` folder. Here is the standard folder structure for a site. + + . + ├── locks + ├── private + │   └── backups + ├── public + │   └── files + └── site_config.json + +1. `public/files` is where user uploaded files are stored. +1. `private/backups` is where backups are dumped +1. `site_config.json` is where site level configurations are maintained. + +### Setting Default Site + +In case you have multiple sites on you bench use `bench use [site_name]` to set the default site. + +Example: + + $ bench use library + +### Install App + +Now let us install our app `library_management` in our site `library` + +1. Install library_management in library with: `bench --site [site_name] install-app [app_name]` + +Example: + + $ bench --site library install-app library_management + +{next} diff --git a/frappe/docs/user/zh/tutorial/single-doctypes.md b/frappe/docs/user/zh/tutorial/single-doctypes.md new file mode 100755 index 0000000000..d01f13d700 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/single-doctypes.md @@ -0,0 +1,9 @@ +# Single DocTypes + +A application will usually have a Settings page. In our application, we can define a page where we can set the loan period. We also need to save this property. In Frappé, this can be done using a **Single** type DocType. A Single DocType is like the Singleton pattern in Java. It is an object with only one instance. Let us call this as **Library Managment Settings**. + +To create an new Single DocType, mark the **Is Single** property as checked. + +Single Doctypes + +{next} diff --git a/frappe/docs/user/zh/tutorial/start.md b/frappe/docs/user/zh/tutorial/start.md new file mode 100755 index 0000000000..10c921a2f2 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/start.md @@ -0,0 +1,31 @@ +# Starting the Bench + +Now we can login and check if everything works. + +To start the development server, run `bench start` + + $ bench start + 13:58:51 web.1 | started with pid 22135 + 13:58:51 worker.1 | started with pid 22136 + 13:58:51 workerbeat.1 | started with pid 22137 + 13:58:52 web.1 | * Running on http://0.0.0.0:8000/ + 13:58:52 web.1 | * Restarting with reloader + 13:58:52 workerbeat.1 | [2014-09-17 13:58:52,343: INFO/MainProcess] beat: Starting... + +You can now open your browser and go to `http://localhost:8000`. You should see this login page if all goes well: + +Login Screen + +Now login with : + +Login ID: **Administrator** + +Password : **Use the password that was created during installation** + +When you login, you should see the "Desk" home page + +Desk + +As you can see, the Frappé basic system comes with several pre-loaded applications like To Do, File Manager etc. These apps can integrated in your app workflow as we progress. + +{next} diff --git a/frappe/docs/user/zh/tutorial/task-runner.md b/frappe/docs/user/zh/tutorial/task-runner.md new file mode 100755 index 0000000000..a700f1cafd --- /dev/null +++ b/frappe/docs/user/zh/tutorial/task-runner.md @@ -0,0 +1,94 @@ +# Scheduled Tasks + +Finally, an application also has to send email notifications and do other kind of scheduled tasks. In Frappé, if you have setup the bench, the task / scheduler is setup via RQ using Redis Queue. + +To add a new task handler, go to `hooks.py` and add a new handler. Default handlers are `all`, `daily`, `weekly`, `monthly`, `cron`. The `all` handler is called every 4 minutes by default. + + # Scheduled Tasks + # --------------- + + scheduler_events = { + "daily": [ + "library_management.tasks.daily" + ], + "cron": { + "0/10 * * * *": [ + "library_management.task.run_every_ten_mins" + ], + "15 18 * * *": [ + "library_management.task.every_day_at_18_15" + ] + } + + } + +Here we can point to a Python function and that function will be executed every day. Let us look what this function looks like: + + # Copyright (c) 2013, Frappé + # For license information, please see license.txt + + from __future__ import unicode_literals + import frappe + from frappe.utils import datediff, nowdate, format_date, add_days + + def every_ten_minutes(): + # stuff to do every 10 minutes + pass + + def every_day_at_18_15(): + # stuff to do every day at 6:15pm + pass + + def daily(): + loan_period = frappe.db.get_value("Library Management Settings", + None, "loan_period") + + overdue = get_overdue(loan_period) + + for member, items in overdue.iteritems(): + content = """

    Following Items are Overdue

    +

    Please return them as soon as possible

      """ + + for i in items: + content += "
    1. {0} ({1}) due on {2}
    2. ".format(i.article_name, + i.article, + format_date(add_days(i.transaction_date, loan_period))) + + content += "
    " + + recipient = frappe.db.get_value("Library Member", member, "email_id") + frappe.sendmail(recipients=[recipient], + sender="test@example.com", + subject="Library Articles Overdue", content=content, bulk=True) + + def get_overdue(loan_period): + # check for overdue articles + today = nowdate() + + overdue_by_member = {} + articles_transacted = [] + + for d in frappe.db.sql("""select name, article, article_name, + library_member, member_name + from `tabLibrary Transaction` + order by transaction_date desc, modified desc""", as_dict=1): + + if d.article in articles_transacted: + continue + + if d.transaction_type=="Issue" and \ + datediff(today, d.transaction_date) > loan_period: + overdue_by_member.setdefault(d.library_member, []) + overdue_by_member[d.library_member].append(d) + + articles_transacted.append(d.article) + +We can place the above code in any accessible Python module. The route is defined in `hooks.py`, so for our purposes we would place this code in `library_management/tasks.py`. + +Note: + +1. We get the loan period from **Library Management Settings** by using `frappe.db.get_value`. +1. We run a query in the database with `frappe.db.sql` +1. Email is sent via `frappe.sendmail` + +{next} diff --git a/frappe/docs/user/zh/tutorial/users-and-records.md b/frappe/docs/user/zh/tutorial/users-and-records.md new file mode 100755 index 0000000000..4f0b0ad85d --- /dev/null +++ b/frappe/docs/user/zh/tutorial/users-and-records.md @@ -0,0 +1,55 @@ +# Making Users and Records + +Now that we have created the models, we can directly start making records using Frappé Desk UI. You do not have to create views! Views in Frappé are automatically made based on the DocType properties. + +### 4.1 Creating User + +To make records, we will first create a User. To create a user, go to: + +> Setup > Users > User > New + +Create a new User and set the name and first name and new password. + +Also give the Librarian and Library Member Roles to this user + +Add User Roles + +Now logout and login using the new user id and password. + +### 4.2 Creating Records + +You will now see an icon for the Library Management module. Click on that icon and you will see the Module page: + +Library Management Module + +Here you can see the DocTypes that we have created for the application. Let us start creating a few records. + +First let us create a new Article: + +New Article + +Here you will see that the DocType you had created has been rendered as a form. The validations and other rules will also apply as designed. Let us fill out one Article. + +New Article + +You can also add an image. + +Attach Image + +Now let us create a new member: + +New Library Member + +After this, let us create a new membership record for the member. + +Here if you remember we had set the values of Member First Name and Member Last Name to be directly fetched from the Member records and as soon as you will select the member id, the names will be updated. + +New Library Membership + +As you can see that the date is formatted as year-month-day which is a system format. To set / change date, time and number formats, go to + +> Setup > Settings > System Settings + +System Settings + +{next} diff --git a/frappe/docs/user/zh/tutorial/web-views.md b/frappe/docs/user/zh/tutorial/web-views.md new file mode 100755 index 0000000000..9eb36d48c7 --- /dev/null +++ b/frappe/docs/user/zh/tutorial/web-views.md @@ -0,0 +1,65 @@ +# Web Views + +Frappé has two main user environments, the Desk and Web. Desk is a controlled UI environment with a rich AJAX application and the web is more traditional HTML templates served for public consumption. Web views can also be generated to create more controlled views for users who may login but still do not have access to the Desk. + +In Frappé, Web Views are managed by templates and they are usually in the `templates` folder. There are 2 main types of templates. + +1. Pages: These are Jinja templates where a single view exists for a single web route e.g. `/blog`. +2. Generators: These are templates where each instance of a DocType has a separate web route `/blog/a-blog`, `blog/b-blog` etc. +3. Lists and Views: These are standard lists and views with the route `[doctype]/[name]` and are rendered based on permission. + +### Standard Web Views + +> This features is still under development. + +Let us look at the standard Web Views: + +If you are logged in as the test user, go to `/article` and you should see the list of articles: + +web list + +Click on one article and you will see the default web view + +web view + +Now if you want to make a better list view for the article, drop a file called `row_template.html` in the +`library_management/templates/includes/list/` folder. Here is an example file: + + {% raw %}
    +
    + + + +
    +
    +

    {{ doc.article_name }}

    +

    {{ doc.author }}

    +

    {{ (doc.description[:200] + "...") + if doc.description|len > 200 else doc.description }}

    +

    Publisher: {{ doc.publisher }}

    +
    +
    {% endraw %} + + +Here, you will get all the properties of the article in the `doc` object. + +The updated list view looks like this! + +new web list + +#### Home Page + +Frappé also has a built-in signup workflow which also includes 3rd party signups via Google, Facebook and GitHub. When a user signs up on the web, she does not have access to the desk interface by default. + +> To allow user access into the Desk, open set the user from Setup > User and set the User Type as "System User" + +Now for the non system users, we can set a home page when they login via `hooks.py` based on the role. + +To when library members sign in, they must be redirected to the `article` page, to set this open `library_management/hooks.py` and add this: + + role_home_page = { + "Library Member": "article" + } + +{next} diff --git a/frappe/docs/user/zh/videos/__init__.py b/frappe/docs/user/zh/videos/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/docs/user/zh/videos/index.md b/frappe/docs/user/zh/videos/index.md new file mode 100755 index 0000000000..a248152889 --- /dev/null +++ b/frappe/docs/user/zh/videos/index.md @@ -0,0 +1,9 @@ +# Frappé 框架视频教程 + +这里的10部视频教程,将教你如何在 Frappé 下构建复杂的应用 + +必备条件: 在你开始该教程前,你必须对 Python、Javascript 以及 MySQL 有一定的了解。 + +--- + + \ No newline at end of file