├── .github ├── CODEOWNERS ├── helper │ ├── install.sh │ └── site_config.json └── workflows │ └── ci.yml ├── .gitignore ├── .pre-commit-config.yaml ├── MANIFEST.in ├── README.md ├── dev-requirements.txt ├── license.txt ├── non_profit ├── __init__.py ├── config │ ├── __init__.py │ ├── desktop.py │ └── docs.py ├── hooks.py ├── modules.txt ├── non_profit │ ├── __init__.py │ ├── custom_doctype │ │ ├── __init__.py │ │ └── payment_entry.py │ ├── doctype │ │ ├── __init__.py │ │ ├── certification_application │ │ │ ├── __init__.py │ │ │ ├── certification_application.js │ │ │ ├── certification_application.json │ │ │ ├── certification_application.py │ │ │ └── test_certification_application.py │ │ ├── certified_consultant │ │ │ ├── __init__.py │ │ │ ├── certified_consultant.js │ │ │ ├── certified_consultant.json │ │ │ ├── certified_consultant.py │ │ │ └── test_certified_consultant.py │ │ ├── chapter │ │ │ ├── __init__.py │ │ │ ├── chapter.js │ │ │ ├── chapter.json │ │ │ ├── chapter.py │ │ │ ├── templates │ │ │ │ ├── chapter.html │ │ │ │ └── chapter_row.html │ │ │ └── test_chapter.py │ │ ├── chapter_member │ │ │ ├── __init__.py │ │ │ ├── chapter_member.json │ │ │ └── chapter_member.py │ │ ├── donation │ │ │ ├── __init__.py │ │ │ ├── donation.js │ │ │ ├── donation.json │ │ │ ├── donation.py │ │ │ ├── donation_dashboard.py │ │ │ └── test_donation.py │ │ ├── donor │ │ │ ├── __init__.py │ │ │ ├── donor.js │ │ │ ├── donor.json │ │ │ ├── donor.py │ │ │ ├── donor_list.js │ │ │ ├── test_donor.js │ │ │ └── test_donor.py │ │ ├── donor_type │ │ │ ├── __init__.py │ │ │ ├── donor_type.js │ │ │ ├── donor_type.json │ │ │ ├── donor_type.py │ │ │ └── test_donor_type.py │ │ ├── grant_application │ │ │ ├── __init__.py │ │ │ ├── grant_application.js │ │ │ ├── grant_application.json │ │ │ ├── grant_application.py │ │ │ ├── templates │ │ │ │ ├── grant_application.html │ │ │ │ └── grant_application_row.html │ │ │ ├── test_grant_application.js │ │ │ └── test_grant_application.py │ │ ├── member │ │ │ ├── __init__.py │ │ │ ├── member.js │ │ │ ├── member.json │ │ │ ├── member.py │ │ │ ├── member_dashboard.py │ │ │ ├── member_list.js │ │ │ ├── test_member.js │ │ │ └── test_member.py │ │ ├── membership │ │ │ ├── __init__.py │ │ │ ├── membership.js │ │ │ ├── membership.json │ │ │ ├── membership.py │ │ │ ├── membership_list.js │ │ │ └── test_membership.py │ │ ├── membership_type │ │ │ ├── __init__.py │ │ │ ├── membership_type.js │ │ │ ├── membership_type.json │ │ │ ├── membership_type.py │ │ │ ├── test_membership_type.js │ │ │ └── test_membership_type.py │ │ ├── non_profit_settings │ │ │ ├── __init__.py │ │ │ ├── non_profit_settings.js │ │ │ ├── non_profit_settings.json │ │ │ ├── non_profit_settings.py │ │ │ └── test_non_profit_settings.py │ │ ├── tax_exemption_80g_certificate │ │ │ ├── __init__.py │ │ │ ├── tax_exemption_80g_certificate.js │ │ │ ├── tax_exemption_80g_certificate.json │ │ │ ├── tax_exemption_80g_certificate.py │ │ │ └── test_tax_exemption_80g_certificate.py │ │ ├── tax_exemption_80g_certificate_detail │ │ │ ├── __init__.py │ │ │ ├── tax_exemption_80g_certificate_detail.json │ │ │ └── tax_exemption_80g_certificate_detail.py │ │ ├── volunteer │ │ │ ├── __init__.py │ │ │ ├── test_volunteer.js │ │ │ ├── test_volunteer.py │ │ │ ├── volunteer.js │ │ │ ├── volunteer.json │ │ │ └── volunteer.py │ │ ├── volunteer_skill │ │ │ ├── __init__.py │ │ │ ├── volunteer_skill.json │ │ │ └── volunteer_skill.py │ │ └── volunteer_type │ │ │ ├── __init__.py │ │ │ ├── test_volunteer_type.js │ │ │ ├── test_volunteer_type.py │ │ │ ├── volunteer_type.js │ │ │ ├── volunteer_type.json │ │ │ └── volunteer_type.py │ ├── print_format │ │ ├── 80g_certificate_for_donation │ │ │ ├── 80g_certificate_for_donation.json │ │ │ └── __init__.py │ │ └── 80g_certificate_for_membership │ │ │ ├── 80g_certificate_for_membership.json │ │ │ └── __init__.py │ ├── report │ │ ├── __init__.py │ │ └── expiring_memberships │ │ │ ├── __init__.py │ │ │ ├── expiring_memberships.js │ │ │ ├── expiring_memberships.json │ │ │ └── expiring_memberships.py │ ├── utils.py │ ├── web_form │ │ ├── __init__.py │ │ ├── certification_application │ │ │ ├── __init__.py │ │ │ ├── certification_application.js │ │ │ ├── certification_application.json │ │ │ └── certification_application.py │ │ ├── certification_application_usd │ │ │ ├── __init__.py │ │ │ ├── certification_application_usd.js │ │ │ ├── certification_application_usd.json │ │ │ └── certification_application_usd.py │ │ └── grant_application │ │ │ ├── __init__.py │ │ │ ├── grant_application.js │ │ │ ├── grant_application.json │ │ │ └── grant_application.py │ └── workspace │ │ └── non_profit │ │ └── non_profit.json ├── patches.txt ├── patches │ └── rename_non_profit_fields.py ├── setup.py └── templates │ ├── __init__.py │ └── pages │ ├── __init__.py │ └── non_profit │ ├── __init__.py │ ├── join-chapter.html │ ├── join_chapter.js │ ├── join_chapter.py │ ├── leave-chapter.html │ └── leave_chapter.py ├── requirements.txt └── setup.py /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # This is a comment. 2 | # Each line is a file pattern followed by one or more owners. 3 | 4 | # These owners will be the default owners for everything in 5 | # the repo. Unless a later match takes precedence. 6 | 7 | * @ruchamahabal 8 | * @ChillarAnand 9 | -------------------------------------------------------------------------------- /.github/helper/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | cd ~ || exit 6 | 7 | sudo apt-get install redis-server libcups2-dev -qq 8 | 9 | pip install frappe-bench 10 | 11 | git clone https://github.com/frappe/frappe --branch develop --depth 1 12 | bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench 13 | 14 | mkdir ~/frappe-bench/sites/test_site 15 | cp -r "${GITHUB_WORKSPACE}/.github/helper/site_config.json" ~/frappe-bench/sites/test_site/ 16 | 17 | mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'" 18 | mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'" 19 | 20 | mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe" 21 | 22 | mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'" 23 | mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'" 24 | 25 | mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'172.18.0.1' IDENTIFIED BY 'test_frappe'" 26 | mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'172.18.0.1'" 27 | 28 | mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'" 29 | mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES" 30 | 31 | 32 | cd ~/frappe-bench || exit 33 | 34 | 35 | sed -i 's/watch:/# watch:/g' Procfile 36 | sed -i 's/schedule:/# schedule:/g' Procfile 37 | sed -i 's/socketio:/# socketio:/g' Procfile 38 | sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile 39 | 40 | bench get-app erpnext --branch develop 41 | bench setup requirements --dev 42 | 43 | bench start & 44 | bench --site test_site reinstall --yes 45 | 46 | bench --verbose --site test_site install-app erpnext 47 | 48 | bench get-app healthcare "${GITHUB_WORKSPACE}" 49 | bench --verbose --site test_site install-app non_profit 50 | -------------------------------------------------------------------------------- /.github/helper/site_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "db_host": "127.0.0.1", 3 | "db_port": 3306, 4 | "db_name": "test_frappe", 5 | "db_password": "test_frappe", 6 | "auto_email_id": "test@example.com", 7 | "mail_server": "smtp.example.com", 8 | "mail_login": "test@example.com", 9 | "mail_password": "test", 10 | "admin_password": "admin", 11 | "root_login": "root", 12 | "root_password": "travis", 13 | "host_name": "http://test_site:8000", 14 | "install_apps": ["erpnext"], 15 | "throttle_user_limit": 100 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - develop 10 | 11 | jobs: 12 | linters: 13 | runs-on: ubuntu-latest 14 | steps: 15 | 16 | - name: Checkout Code Repository 17 | uses: actions/checkout@v2 18 | 19 | - name: Set up Python 3.8 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: 3.8 23 | 24 | - name: Install and Run Pre-commit 25 | uses: pre-commit/action@v2.0.0 26 | 27 | tests: 28 | runs-on: ubuntu-18.04 29 | timeout-minutes: 20 30 | 31 | strategy: 32 | fail-fast: false 33 | 34 | name: Server 35 | 36 | services: 37 | mysql: 38 | image: mariadb:10.3 39 | env: 40 | MYSQL_ALLOW_EMPTY_PASSWORD: YES 41 | ports: 42 | - 3306:3306 43 | options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 44 | 45 | steps: 46 | - name: Clone 47 | uses: actions/checkout@v2 48 | 49 | - name: Setup Python 50 | uses: actions/setup-python@v2 51 | with: 52 | python-version: 3.8 53 | 54 | - name: Setup Node 55 | uses: actions/setup-node@v2 56 | with: 57 | node-version: 14 58 | check-latest: true 59 | 60 | - name: Add to Hosts 61 | run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts 62 | 63 | - name: Cache pip 64 | uses: actions/cache@v2 65 | with: 66 | path: ~/.cache/pip 67 | key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt') }} 68 | restore-keys: | 69 | ${{ runner.os }}-pip- 70 | ${{ runner.os }}- 71 | - name: Cache node modules 72 | uses: actions/cache@v2 73 | env: 74 | cache-name: cache-node-modules 75 | with: 76 | path: ~/.npm 77 | key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} 78 | restore-keys: | 79 | ${{ runner.os }}-build-${{ env.cache-name }}- 80 | ${{ runner.os }}-build- 81 | ${{ runner.os }}- 82 | 83 | - name: Get yarn cache directory path 84 | id: yarn-cache-dir-path 85 | run: echo "::set-output name=dir::$(yarn cache dir)" 86 | 87 | - uses: actions/cache@v2 88 | id: yarn-cache 89 | with: 90 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 91 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 92 | restore-keys: | 93 | ${{ runner.os }}-yarn- 94 | 95 | - name: Install 96 | run: | 97 | bash ${GITHUB_WORKSPACE}/.github/helper/install.sh 98 | cd ~/frappe-bench/ && source env/bin/activate 99 | cd apps/non_profit && pip install -r dev-requirements.txt 100 | env: 101 | TYPE: server 102 | 103 | - name: Run Tests 104 | run: cd ~/frappe-bench/ && bench --site test_site run-tests --app non_profit 105 | env: 106 | TYPE: server 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.pyc 3 | *.egg-info 4 | *.swp 5 | tags 6 | non_profit/docs/current 7 | __pycache__ 8 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: 'node_modules|.git' 2 | default_stages: [commit] 3 | fail_fast: false 4 | 5 | 6 | repos: 7 | - repo: https://github.com/pre-commit/pre-commit-hooks 8 | rev: v4.0.1 9 | hooks: 10 | - id: trailing-whitespace 11 | files: "non_profit.*" 12 | exclude: ".*json$|.*txt$|.*csv|.*md" 13 | - id: check-yaml 14 | - id: no-commit-to-branch 15 | args: ['--branch', 'develop'] 16 | - id: check-merge-conflict 17 | - id: check-ast 18 | 19 | ci: 20 | autoupdate_schedule: weekly 21 | skip: [] 22 | submodules: false 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include MANIFEST.in 2 | include requirements.txt 3 | include *.json 4 | include *.md 5 | include *.py 6 | include *.txt 7 | recursive-include non_profit *.css 8 | recursive-include non_profit *.csv 9 | recursive-include non_profit *.html 10 | recursive-include non_profit *.ico 11 | recursive-include non_profit *.js 12 | recursive-include non_profit *.json 13 | recursive-include non_profit *.md 14 | recursive-include non_profit *.png 15 | recursive-include non_profit *.py 16 | recursive-include non_profit *.svg 17 | recursive-include non_profit *.txt 18 | recursive-exclude non_profit *.pyc -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Non Profit 2 | 3 | A Non profit app built on top of Frappe framework & ERPNext. 4 | 5 | 6 | People who change the world need the tools to do it! The Non Profit Modules of ERPNext is designed for a non-profit organization, so that they can deliver well on their noble cause of a better world. 7 | 8 | 9 | ### Installation 10 | 11 | Using bench, [install ERPNext](https://github.com/frappe/bench#installation) as mentioned here. 12 | 13 | Once ERPNext is installed, add non_profit app to your bench by running 14 | 15 | ```sh 16 | $ bench get-app non_profit 17 | ``` 18 | 19 | After that, you can install non_profit app on required site by running 20 | 21 | ```sh 22 | $ bench --site demo.com install-app non_profit 23 | ``` 24 | 25 | 26 | ### Documentation 27 | 28 | Read documentation at https://docs.erpnext.com/docs/v14/user/manual/en/non_profit/introduction 29 | 30 | 31 | ### License 32 | 33 | GNU GPL V3 34 | -------------------------------------------------------------------------------- /dev-requirements.txt: -------------------------------------------------------------------------------- 1 | pre-commit>=2.13.0,<3.0.0 2 | -------------------------------------------------------------------------------- /non_profit/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = '0.0.1' 2 | 3 | -------------------------------------------------------------------------------- /non_profit/config/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/config/__init__.py -------------------------------------------------------------------------------- /non_profit/config/desktop.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | def get_data(): 4 | return [ 5 | { 6 | "module_name": "Non Profit", 7 | "color": "grey", 8 | "icon": "octicon octicon-file-directory", 9 | "type": "module", 10 | "label": _("Non Profit") 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /non_profit/config/docs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Configuration for docs 3 | """ 4 | 5 | # source_link = "https://github.com/[org_name]/non_profit" 6 | # headline = "App that does everything" 7 | # sub_heading = "Yes, you got that right the first time, everything" 8 | 9 | def get_context(context): 10 | context.brand_html = "Non Profit" 11 | -------------------------------------------------------------------------------- /non_profit/hooks.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | app_name = "non_profit" 5 | app_title = "Non Profit" 6 | app_publisher = "Frappe" 7 | app_description = "Non Profit" 8 | app_icon = "octicon octicon-file-directory" 9 | app_color = "grey" 10 | app_email = "pandikunta@frappe.io" 11 | app_license = "MIT" 12 | 13 | required_apps = ["erpnext"] 14 | 15 | # Includes in 16 | # ------------------ 17 | 18 | # include js, css files in header of desk.html 19 | # app_include_css = "/assets/non_profit/css/non_profit.css" 20 | # app_include_js = "/assets/non_profit/js/non_profit.js" 21 | 22 | # include js, css files in header of web template 23 | # web_include_css = "/assets/non_profit/css/non_profit.css" 24 | # web_include_js = "/assets/non_profit/js/non_profit.js" 25 | 26 | # include custom scss in every website theme (without file extension ".scss") 27 | # website_theme_scss = "non_profit/public/scss/website" 28 | 29 | # include js, css files in header of web form 30 | # webform_include_js = {"doctype": "public/js/doctype.js"} 31 | # webform_include_css = {"doctype": "public/css/doctype.css"} 32 | 33 | # include js in page 34 | # page_js = {"page" : "public/js/file.js"} 35 | 36 | # include js in doctype views 37 | # doctype_js = {"doctype" : "public/js/doctype.js"} 38 | doctype_js = { 39 | "Sales Invoice": "public/js/payment_entry.js" 40 | } 41 | # doctype_list_js = {"doctype" : "public/js/doctype_list.js"} 42 | # doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"} 43 | # doctype_calendar_js = {"doctype" : "public/js/doctype_calendar.js"} 44 | 45 | # Home Pages 46 | # ---------- 47 | 48 | # application home page (will override Website Settings) 49 | # home_page = "login" 50 | 51 | # website user home page (by Role) 52 | # role_home_page = { 53 | # "Role": "home_page" 54 | # } 55 | 56 | # Generators 57 | # ---------- 58 | 59 | # automatically create page for each record of this doctype 60 | # website_generators = ["Web Page"] 61 | 62 | # Jinja 63 | # ---------- 64 | 65 | # add methods and filters to jinja environment 66 | # jinja = { 67 | # "methods": "non_profit.utils.jinja_methods", 68 | # "filters": "non_profit.utils.jinja_filters" 69 | # } 70 | 71 | # Installation 72 | # ------------ 73 | 74 | # before_install = "non_profit.install.before_install" 75 | after_install = "non_profit.setup.setup_non_profit" 76 | 77 | # Uninstallation 78 | # ------------ 79 | 80 | # before_uninstall = "non_profit.uninstall.before_uninstall" 81 | # after_uninstall = "non_profit.uninstall.after_uninstall" 82 | 83 | # Desk Notifications 84 | # ------------------ 85 | # See frappe.core.notifications.get_notification_config 86 | 87 | # notification_config = "non_profit.notifications.get_notification_config" 88 | 89 | # Permissions 90 | # ----------- 91 | # Permissions evaluated in scripted ways 92 | 93 | # permission_query_conditions = { 94 | # "Event": "frappe.desk.doctype.event.event.get_permission_query_conditions", 95 | # } 96 | # 97 | # has_permission = { 98 | # "Event": "frappe.desk.doctype.event.event.has_permission", 99 | # } 100 | 101 | # DocType Class 102 | # --------------- 103 | # Override standard doctype classes 104 | 105 | override_doctype_class = { 106 | "Payment Entry": "non_profit.non_profit.custom_doctype.payment_entry.NonProfitPaymentEntry", 107 | } 108 | 109 | # Document Events 110 | # --------------- 111 | # Hook on document methods and events 112 | 113 | # doc_events = { 114 | # "*": { 115 | # "on_update": "method", 116 | # "on_cancel": "method", 117 | # "on_trash": "method" 118 | # } 119 | # } 120 | 121 | # Scheduled Tasks 122 | # --------------- 123 | 124 | scheduler_events = { 125 | "daily": [ 126 | "non_profit.non_profit.doctype.membership.membership.set_expired_status", 127 | ], 128 | } 129 | 130 | # Testing 131 | # ------- 132 | 133 | before_tests = "non_profit.non_profit.utils.before_tests" 134 | 135 | # Overriding Methods 136 | # ------------------------------ 137 | # 138 | # override_whitelisted_methods = { 139 | # "frappe.desk.doctype.event.event.get_events": "non_profit.event.get_events" 140 | # } 141 | # 142 | # each overriding function accepts a `data` argument; 143 | # generated from the base implementation of the doctype dashboard, 144 | # along with any modifications made in other Frappe apps 145 | # override_doctype_dashboards = { 146 | # "Task": "non_profit.task.get_dashboard_data" 147 | # } 148 | 149 | # exempt linked doctypes from being automatically cancelled 150 | # 151 | # auto_cancel_exempted_doctypes = ["Auto Repeat"] 152 | 153 | 154 | # User Data Protection 155 | # -------------------- 156 | 157 | # user_data_fields = [ 158 | # { 159 | # "doctype": "{doctype_1}", 160 | # "filter_by": "{filter_by}", 161 | # "redact_fields": ["{field_1}", "{field_2}"], 162 | # "partial": 1, 163 | # }, 164 | # { 165 | # "doctype": "{doctype_2}", 166 | # "filter_by": "{filter_by}", 167 | # "partial": 1, 168 | # }, 169 | # { 170 | # "doctype": "{doctype_3}", 171 | # "strict": False, 172 | # }, 173 | # { 174 | # "doctype": "{doctype_4}" 175 | # } 176 | # ] 177 | 178 | # Authentication and authorization 179 | # -------------------------------- 180 | 181 | # auth_hooks = [ 182 | # "non_profit.auth.validate" 183 | # ] 184 | 185 | 186 | global_search_doctypes = { 187 | "Non Profit": [ 188 | {'doctype': 'Certified Consultant', 'index': 1}, 189 | {'doctype': 'Certification Application', 'index': 2}, 190 | {'doctype': 'Volunteer', 'index': 3}, 191 | {'doctype': 'Membership', 'index': 4}, 192 | {'doctype': 'Member', 'index': 5}, 193 | {'doctype': 'Donor', 'index': 6}, 194 | {'doctype': 'Chapter', 'index': 7}, 195 | {'doctype': 'Grant Application', 'index': 8}, 196 | {'doctype': 'Volunteer Type', 'index': 9}, 197 | {'doctype': 'Donor Type', 'index': 10}, 198 | {'doctype': 'Membership Type', 'index': 11} 199 | ] 200 | } 201 | 202 | standard_portal_menu_items = [ 203 | {"title": _("Certification"), "route": "/certification", 204 | "reference_doctype": "Certification Application", "role": "Non Profit Portal User"}, 205 | ] 206 | -------------------------------------------------------------------------------- /non_profit/modules.txt: -------------------------------------------------------------------------------- 1 | Non Profit -------------------------------------------------------------------------------- /non_profit/non_profit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/custom_doctype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/custom_doctype/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/custom_doctype/payment_entry.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | import erpnext 3 | 4 | from frappe import _, scrub 5 | from frappe.utils.data import comma_or, flt, getdate 6 | 7 | from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import \ 8 | get_party_account_based_on_invoice_discounting 9 | from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account 10 | from erpnext.accounts.doctype.payment_entry.payment_entry import PaymentEntry, get_outstanding_on_journal_entry 11 | 12 | from erpnext.accounts.party import get_party_account 13 | from erpnext.accounts.utils import get_account_currency 14 | from erpnext.setup.utils import get_exchange_rate 15 | 16 | 17 | class NonProfitPaymentEntry(PaymentEntry): 18 | def validate_reference_documents(self): 19 | if self.party_type == "Student": 20 | valid_reference_doctypes = ("Fees", "Journal Entry") 21 | elif self.party_type == "Customer": 22 | valid_reference_doctypes = ("Sales Order", "Sales Invoice", "Journal Entry", "Dunning") 23 | elif self.party_type == "Supplier": 24 | valid_reference_doctypes = ("Purchase Order", "Purchase Invoice", "Journal Entry") 25 | elif self.party_type == "Employee": 26 | valid_reference_doctypes = ("Expense Claim", "Journal Entry", "Employee Advance") 27 | elif self.party_type == "Shareholder": 28 | valid_reference_doctypes = ("Journal Entry") 29 | elif self.party_type == "Donor": 30 | valid_reference_doctypes = ("Donation") 31 | 32 | for d in self.get("references"): 33 | if not d.allocated_amount: 34 | continue 35 | if d.reference_doctype not in valid_reference_doctypes: 36 | frappe.throw(_("Reference Doctype must be one of {0}") 37 | .format(comma_or(valid_reference_doctypes))) 38 | elif d.reference_name: 39 | if not frappe.db.exists(d.reference_doctype, d.reference_name): 40 | frappe.throw(_("{0} {1} does not exist").format(d.reference_doctype, d.reference_name)) 41 | else: 42 | ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name) 43 | if d.reference_doctype != "Journal Entry": 44 | if self.party != ref_doc.get(scrub(self.party_type)): 45 | frappe.throw(_("{0} {1} is not associated with {2} {3}") 46 | .format(d.reference_doctype, d.reference_name, self.party_type, self.party)) 47 | else: 48 | self.validate_journal_entry() 49 | if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim", "Fees"): 50 | if self.party_type == "Customer": 51 | ref_party_account = get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to 52 | elif self.party_type == "Student": 53 | ref_party_account = ref_doc.receivable_account 54 | elif self.party_type=="Supplier": 55 | ref_party_account = ref_doc.credit_to 56 | elif self.party_type=="Employee": 57 | ref_party_account = ref_doc.payable_account 58 | if ref_party_account != self.party_account: 59 | frappe.throw(_("{0} {1} is associated with {2}, but Party Account is {3}") 60 | .format(d.reference_doctype, d.reference_name, ref_party_account, self.party_account)) 61 | if ref_doc.docstatus != 1: 62 | frappe.throw(_("{0} {1} must be submitted") 63 | .format(d.reference_doctype, d.reference_name)) 64 | 65 | def set_missing_ref_details( 66 | self, 67 | force: bool = False, 68 | update_ref_details_only_for: list | None = None, 69 | reference_exchange_details: dict | None = None, 70 | ) -> None: 71 | for d in self.get("references"): 72 | if d.allocated_amount: 73 | if ( 74 | update_ref_details_only_for 75 | and (d.reference_doctype, d.reference_name) not in update_ref_details_only_for 76 | ): 77 | continue 78 | 79 | ref_details = get_payment_reference_details(d.reference_doctype, d.reference_name, self.party_account_currency, 80 | self.party_type, self.party) 81 | 82 | # Only update exchange rate when the reference is Journal Entry 83 | if ( 84 | reference_exchange_details 85 | and d.reference_doctype == reference_exchange_details.reference_doctype 86 | and d.reference_name == reference_exchange_details.reference_name 87 | ): 88 | ref_details.update({"exchange_rate": reference_exchange_details.exchange_rate}) 89 | 90 | for field, value in ref_details.items(): 91 | if d.exchange_gain_loss: 92 | # for cases where gain/loss is booked into invoice 93 | # exchange_gain_loss is calculated from invoice & populated 94 | # and row.exchange_rate is already set to payment entry's exchange rate 95 | # refer -> `update_reference_in_payment_entry()` in utils.py 96 | continue 97 | 98 | if field == 'exchange_rate' or not d.get(field) or force: 99 | d.db_set(field, value) 100 | 101 | 102 | @frappe.whitelist() 103 | def get_donation_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None): 104 | reference_doc = None 105 | doc = frappe.get_doc(dt, dn) 106 | 107 | party_account = get_party_account("Donor", doc.get("donor"), doc.company) 108 | party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) 109 | grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(party_amount, doc) 110 | 111 | # bank or cash 112 | bank = get_bank_cash_account(doc, bank_account) 113 | 114 | paid_amount, received_amount = set_paid_amount_and_received_amount( 115 | party_account_currency, bank, outstanding_amount, bank_amount, doc) 116 | 117 | pe = frappe.new_doc("Payment Entry") 118 | pe.payment_type = "Receive" 119 | pe.company = doc.company 120 | pe.cost_center = doc.get("cost_center") 121 | pe.posting_date = getdate() 122 | pe.mode_of_payment = doc.get("mode_of_payment") 123 | pe.party_type = "Donor" 124 | pe.party = doc.get("donor") 125 | pe.contact_person = doc.get("contact_person") 126 | pe.contact_email = doc.get("contact_email") 127 | 128 | pe.paid_from = party_account 129 | pe.paid_to = bank.account 130 | pe.paid_from_account_currency = party_account_currency 131 | pe.paid_to_account_currency = bank.account_currency 132 | pe.paid_amount = paid_amount 133 | pe.received_amount = received_amount 134 | pe.letter_head = doc.get("letter_head") 135 | 136 | pe.append("references", { 137 | 'reference_doctype': dt, 138 | 'reference_name': dn, 139 | "bill_no": doc.get("bill_no"), 140 | "due_date": doc.get("due_date"), 141 | 'total_amount': grand_total, 142 | 'outstanding_amount': outstanding_amount, 143 | 'allocated_amount': outstanding_amount 144 | }) 145 | 146 | pe.setup_party_account_field() 147 | pe.set_missing_values() 148 | 149 | if party_account and bank: 150 | pe.set_exchange_rate(ref_doc=reference_doc) 151 | pe.set_amounts() 152 | 153 | return pe 154 | 155 | 156 | def set_grand_total_and_outstanding_amount(party_amount, doc): 157 | grand_total = outstanding_amount = 0 158 | if party_amount: 159 | grand_total = outstanding_amount = party_amount 160 | else: 161 | grand_total = doc.amount 162 | outstanding_amount = doc.amount 163 | 164 | return grand_total, outstanding_amount 165 | 166 | 167 | def get_bank_cash_account(doc, bank_account): 168 | bank = get_default_bank_cash_account(doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), 169 | account=bank_account) 170 | 171 | if not bank: 172 | bank = get_default_bank_cash_account(doc.company, "Cash", mode_of_payment=doc.get("mode_of_payment"), 173 | account=bank_account) 174 | 175 | return bank 176 | 177 | 178 | def set_paid_amount_and_received_amount(party_account_currency, bank, outstanding_amount, bank_amount, doc): 179 | paid_amount = received_amount = 0 180 | if party_account_currency == bank.account_currency: 181 | paid_amount = received_amount = abs(outstanding_amount) 182 | else: 183 | paid_amount = abs(outstanding_amount) 184 | if bank_amount: 185 | received_amount = bank_amount 186 | else: 187 | received_amount = paid_amount * doc.get('conversion_rate', 1) 188 | 189 | return paid_amount, received_amount 190 | 191 | 192 | @frappe.whitelist() 193 | def get_payment_reference_details(reference_doctype, reference_name, party_account_currency, party_type=None, party=None): 194 | total_amount = outstanding_amount = exchange_rate = bill_no = None 195 | ref_doc = frappe.get_doc(reference_doctype, reference_name) 196 | company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company) 197 | 198 | if reference_doctype == "Fees": 199 | total_amount = ref_doc.get("grand_total") 200 | exchange_rate = 1 201 | outstanding_amount = ref_doc.get("outstanding_amount") 202 | elif reference_doctype == "Donation": 203 | total_amount = ref_doc.get("amount") 204 | outstanding_amount = total_amount 205 | exchange_rate = 1 206 | elif reference_doctype == "Dunning": 207 | total_amount = ref_doc.get("dunning_amount") 208 | exchange_rate = 1 209 | outstanding_amount = ref_doc.get("dunning_amount") 210 | elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1: 211 | total_amount = ref_doc.get("total_amount") 212 | if ref_doc.multi_currency: 213 | exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) 214 | else: 215 | exchange_rate = 1 216 | outstanding_amount = get_outstanding_on_journal_entry(reference_name, party_type, party) 217 | elif reference_doctype != "Journal Entry": 218 | if ref_doc.doctype == "Expense Claim": 219 | total_amount = flt(ref_doc.total_sanctioned_amount) + flt(ref_doc.total_taxes_and_charges) 220 | elif ref_doc.doctype == "Employee Advance": 221 | total_amount = ref_doc.advance_amount 222 | exchange_rate = ref_doc.get("exchange_rate") 223 | if party_account_currency != ref_doc.currency: 224 | total_amount = flt(total_amount) * flt(exchange_rate) 225 | elif ref_doc.doctype == "Gratuity": 226 | total_amount = ref_doc.amount 227 | if not total_amount: 228 | if party_account_currency == company_currency: 229 | total_amount = ref_doc.base_grand_total 230 | exchange_rate = 1 231 | else: 232 | total_amount = ref_doc.grand_total 233 | if not exchange_rate: 234 | # Get the exchange rate from the original ref doc 235 | # or get it based on the posting date of the ref doc. 236 | exchange_rate = ref_doc.get("conversion_rate") or \ 237 | get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date) 238 | if reference_doctype in ("Sales Invoice", "Purchase Invoice"): 239 | outstanding_amount = ref_doc.get("outstanding_amount") 240 | bill_no = ref_doc.get("bill_no") 241 | elif reference_doctype == "Expense Claim": 242 | outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) + flt(ref_doc.get("total_taxes_and_charges"))\ 243 | - flt(ref_doc.get("total_amount_reimbursed")) - flt(ref_doc.get("total_advance_amount")) 244 | elif reference_doctype == "Employee Advance": 245 | outstanding_amount = (flt(ref_doc.advance_amount) - flt(ref_doc.paid_amount)) 246 | if party_account_currency != ref_doc.currency: 247 | outstanding_amount = flt(outstanding_amount) * flt(exchange_rate) 248 | if party_account_currency == company_currency: 249 | exchange_rate = 1 250 | elif reference_doctype == "Gratuity": 251 | outstanding_amount = ref_doc.amount - flt(ref_doc.paid_amount) 252 | else: 253 | outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid) 254 | else: 255 | # Get the exchange rate based on the posting date of the ref doc. 256 | exchange_rate = get_exchange_rate(party_account_currency, 257 | company_currency, ref_doc.posting_date) 258 | 259 | return frappe._dict({ 260 | "due_date": ref_doc.get("due_date"), 261 | "total_amount": flt(total_amount), 262 | "outstanding_amount": flt(outstanding_amount), 263 | "exchange_rate": flt(exchange_rate), 264 | "bill_no": bill_no 265 | }) 266 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certification_application/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/certification_application/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certification_application/certification_application.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Certification Application', { 5 | refresh: function(frm) { 6 | 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certification_application/certification_application.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow_copy": 0, 3 | "allow_events_in_timeline": 0, 4 | "allow_guest_to_view": 0, 5 | "allow_import": 0, 6 | "allow_rename": 0, 7 | "autoname": "NPO-CAPP-.YYYY.-.#####", 8 | "beta": 0, 9 | "creation": "2018-06-08 16:12:42.091729", 10 | "custom": 0, 11 | "docstatus": 0, 12 | "doctype": "DocType", 13 | "document_type": "", 14 | "editable_grid": 1, 15 | "engine": "InnoDB", 16 | "fields": [ 17 | { 18 | "allow_bulk_edit": 0, 19 | "allow_in_quick_entry": 0, 20 | "allow_on_submit": 0, 21 | "bold": 0, 22 | "collapsible": 0, 23 | "columns": 0, 24 | "fieldname": "name_of_applicant", 25 | "fieldtype": "Data", 26 | "hidden": 0, 27 | "ignore_user_permissions": 0, 28 | "ignore_xss_filter": 0, 29 | "in_filter": 0, 30 | "in_global_search": 0, 31 | "in_list_view": 1, 32 | "in_standard_filter": 0, 33 | "label": "Name of Applicant", 34 | "length": 0, 35 | "no_copy": 0, 36 | "permlevel": 0, 37 | "precision": "", 38 | "print_hide": 0, 39 | "print_hide_if_no_value": 0, 40 | "read_only": 0, 41 | "remember_last_selected_value": 0, 42 | "report_hide": 0, 43 | "reqd": 1, 44 | "search_index": 0, 45 | "set_only_once": 0, 46 | "translatable": 0, 47 | "unique": 0 48 | }, 49 | { 50 | "allow_bulk_edit": 0, 51 | "allow_in_quick_entry": 0, 52 | "allow_on_submit": 0, 53 | "bold": 0, 54 | "collapsible": 0, 55 | "columns": 0, 56 | "fieldname": "email", 57 | "fieldtype": "Link", 58 | "hidden": 0, 59 | "ignore_user_permissions": 0, 60 | "ignore_xss_filter": 0, 61 | "in_filter": 0, 62 | "in_global_search": 0, 63 | "in_list_view": 1, 64 | "in_standard_filter": 0, 65 | "label": "Email", 66 | "length": 0, 67 | "no_copy": 0, 68 | "options": "User", 69 | "permlevel": 0, 70 | "precision": "", 71 | "print_hide": 0, 72 | "print_hide_if_no_value": 0, 73 | "read_only": 0, 74 | "remember_last_selected_value": 0, 75 | "report_hide": 0, 76 | "reqd": 1, 77 | "search_index": 0, 78 | "set_only_once": 0, 79 | "translatable": 0, 80 | "unique": 0 81 | }, 82 | { 83 | "allow_bulk_edit": 0, 84 | "allow_in_quick_entry": 0, 85 | "allow_on_submit": 0, 86 | "bold": 0, 87 | "collapsible": 0, 88 | "columns": 0, 89 | "fieldname": "column_break_1", 90 | "fieldtype": "Column Break", 91 | "hidden": 0, 92 | "ignore_user_permissions": 0, 93 | "ignore_xss_filter": 0, 94 | "in_filter": 0, 95 | "in_global_search": 0, 96 | "in_list_view": 0, 97 | "in_standard_filter": 0, 98 | "length": 0, 99 | "no_copy": 0, 100 | "permlevel": 0, 101 | "precision": "", 102 | "print_hide": 0, 103 | "print_hide_if_no_value": 0, 104 | "read_only": 0, 105 | "remember_last_selected_value": 0, 106 | "report_hide": 0, 107 | "reqd": 0, 108 | "search_index": 0, 109 | "set_only_once": 0, 110 | "translatable": 0, 111 | "unique": 0 112 | }, 113 | { 114 | "allow_bulk_edit": 0, 115 | "allow_in_quick_entry": 0, 116 | "allow_on_submit": 0, 117 | "bold": 0, 118 | "collapsible": 0, 119 | "columns": 0, 120 | "fieldname": "certification_status", 121 | "fieldtype": "Select", 122 | "hidden": 0, 123 | "ignore_user_permissions": 0, 124 | "ignore_xss_filter": 0, 125 | "in_filter": 0, 126 | "in_global_search": 0, 127 | "in_list_view": 0, 128 | "in_standard_filter": 0, 129 | "label": "Certification Status", 130 | "length": 0, 131 | "no_copy": 0, 132 | "options": "Yet to appear\nCertified\nNot Certified", 133 | "permlevel": 0, 134 | "precision": "", 135 | "print_hide": 0, 136 | "print_hide_if_no_value": 0, 137 | "read_only": 0, 138 | "remember_last_selected_value": 0, 139 | "report_hide": 0, 140 | "reqd": 0, 141 | "search_index": 0, 142 | "set_only_once": 0, 143 | "translatable": 0, 144 | "unique": 0 145 | }, 146 | { 147 | "allow_bulk_edit": 0, 148 | "allow_in_quick_entry": 0, 149 | "allow_on_submit": 0, 150 | "bold": 0, 151 | "collapsible": 0, 152 | "columns": 0, 153 | "fieldname": "payment_details", 154 | "fieldtype": "Section Break", 155 | "hidden": 0, 156 | "ignore_user_permissions": 0, 157 | "ignore_xss_filter": 0, 158 | "in_filter": 0, 159 | "in_global_search": 0, 160 | "in_list_view": 0, 161 | "in_standard_filter": 0, 162 | "label": "Payment Details", 163 | "length": 0, 164 | "no_copy": 0, 165 | "permlevel": 0, 166 | "precision": "", 167 | "print_hide": 0, 168 | "print_hide_if_no_value": 0, 169 | "read_only": 0, 170 | "remember_last_selected_value": 0, 171 | "report_hide": 0, 172 | "reqd": 0, 173 | "search_index": 0, 174 | "set_only_once": 0, 175 | "translatable": 0, 176 | "unique": 0 177 | }, 178 | { 179 | "allow_bulk_edit": 0, 180 | "allow_in_quick_entry": 0, 181 | "allow_on_submit": 0, 182 | "bold": 0, 183 | "collapsible": 0, 184 | "columns": 0, 185 | "fieldname": "paid", 186 | "fieldtype": "Check", 187 | "hidden": 0, 188 | "ignore_user_permissions": 0, 189 | "ignore_xss_filter": 0, 190 | "in_filter": 0, 191 | "in_global_search": 0, 192 | "in_list_view": 0, 193 | "in_standard_filter": 0, 194 | "label": "Paid", 195 | "length": 0, 196 | "no_copy": 0, 197 | "permlevel": 0, 198 | "precision": "", 199 | "print_hide": 0, 200 | "print_hide_if_no_value": 0, 201 | "read_only": 0, 202 | "remember_last_selected_value": 0, 203 | "report_hide": 0, 204 | "reqd": 0, 205 | "search_index": 0, 206 | "set_only_once": 0, 207 | "translatable": 0, 208 | "unique": 0 209 | }, 210 | { 211 | "allow_bulk_edit": 0, 212 | "allow_in_quick_entry": 0, 213 | "allow_on_submit": 0, 214 | "bold": 0, 215 | "collapsible": 0, 216 | "columns": 0, 217 | "fieldname": "currency", 218 | "fieldtype": "Select", 219 | "hidden": 0, 220 | "ignore_user_permissions": 0, 221 | "ignore_xss_filter": 0, 222 | "in_filter": 0, 223 | "in_global_search": 0, 224 | "in_list_view": 0, 225 | "in_standard_filter": 0, 226 | "label": "Currency", 227 | "length": 0, 228 | "no_copy": 0, 229 | "options": "USD\nINR", 230 | "permlevel": 0, 231 | "precision": "", 232 | "print_hide": 0, 233 | "print_hide_if_no_value": 0, 234 | "read_only": 0, 235 | "remember_last_selected_value": 0, 236 | "report_hide": 0, 237 | "reqd": 0, 238 | "search_index": 0, 239 | "set_only_once": 0, 240 | "translatable": 0, 241 | "unique": 0 242 | }, 243 | { 244 | "allow_bulk_edit": 0, 245 | "allow_in_quick_entry": 0, 246 | "allow_on_submit": 0, 247 | "bold": 0, 248 | "collapsible": 0, 249 | "columns": 0, 250 | "fieldname": "amount", 251 | "fieldtype": "Float", 252 | "hidden": 0, 253 | "ignore_user_permissions": 0, 254 | "ignore_xss_filter": 0, 255 | "in_filter": 0, 256 | "in_global_search": 0, 257 | "in_list_view": 0, 258 | "in_standard_filter": 0, 259 | "label": "Amount", 260 | "length": 0, 261 | "no_copy": 0, 262 | "permlevel": 0, 263 | "precision": "", 264 | "print_hide": 0, 265 | "print_hide_if_no_value": 0, 266 | "read_only": 0, 267 | "remember_last_selected_value": 0, 268 | "report_hide": 0, 269 | "reqd": 0, 270 | "search_index": 0, 271 | "set_only_once": 0, 272 | "translatable": 0, 273 | "unique": 0 274 | } 275 | ], 276 | "has_web_view": 0, 277 | "hide_heading": 0, 278 | "hide_toolbar": 0, 279 | "idx": 0, 280 | "image_view": 0, 281 | "in_create": 0, 282 | "is_submittable": 0, 283 | "issingle": 0, 284 | "istable": 0, 285 | "max_attachments": 0, 286 | "modified": "2018-11-04 03:36:35.337403", 287 | "modified_by": "Administrator", 288 | "module": "Non Profit", 289 | "name": "Certification Application", 290 | "name_case": "", 291 | "owner": "Administrator", 292 | "permissions": [ 293 | { 294 | "amend": 0, 295 | "cancel": 0, 296 | "create": 1, 297 | "delete": 1, 298 | "email": 1, 299 | "export": 1, 300 | "if_owner": 0, 301 | "import": 0, 302 | "permlevel": 0, 303 | "print": 1, 304 | "read": 1, 305 | "report": 1, 306 | "role": "System Manager", 307 | "set_user_permissions": 0, 308 | "share": 1, 309 | "submit": 0, 310 | "write": 1 311 | } 312 | ], 313 | "quick_entry": 0, 314 | "read_only": 0, 315 | "read_only_onload": 0, 316 | "restrict_to_domain": "Non Profit", 317 | "show_name_in_global_search": 0, 318 | "sort_field": "modified", 319 | "sort_order": "DESC", 320 | "track_changes": 1, 321 | "track_seen": 0, 322 | "track_views": 0 323 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certification_application/certification_application.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class CertificationApplication(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certification_application/test_certification_application.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestCertificationApplication(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certified_consultant/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/certified_consultant/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certified_consultant/certified_consultant.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Certified Consultant', { 5 | refresh: function(frm) { 6 | 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certified_consultant/certified_consultant.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class CertifiedConsultant(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/certified_consultant/test_certified_consultant.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestCertifiedConsultant(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/chapter/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter/chapter.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Chapter', { 5 | refresh: function() { 6 | 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter/chapter.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow_copy": 0, 3 | "allow_guest_to_view": 1, 4 | "allow_import": 0, 5 | "allow_rename": 1, 6 | "autoname": "prompt", 7 | "beta": 0, 8 | "creation": "2017-09-14 13:36:03.904702", 9 | "custom": 0, 10 | "docstatus": 0, 11 | "doctype": "DocType", 12 | "document_type": "", 13 | "editable_grid": 1, 14 | "engine": "InnoDB", 15 | "fields": [ 16 | { 17 | "allow_bulk_edit": 0, 18 | "allow_on_submit": 0, 19 | "bold": 0, 20 | "collapsible": 0, 21 | "columns": 0, 22 | "fieldname": "chapter_head", 23 | "fieldtype": "Link", 24 | "hidden": 0, 25 | "ignore_user_permissions": 0, 26 | "ignore_xss_filter": 0, 27 | "in_filter": 0, 28 | "in_global_search": 0, 29 | "in_list_view": 1, 30 | "in_standard_filter": 0, 31 | "label": "Chapter Head", 32 | "length": 0, 33 | "no_copy": 0, 34 | "options": "Member", 35 | "permlevel": 0, 36 | "precision": "", 37 | "print_hide": 0, 38 | "print_hide_if_no_value": 0, 39 | "read_only": 0, 40 | "remember_last_selected_value": 0, 41 | "report_hide": 0, 42 | "reqd": 1, 43 | "search_index": 0, 44 | "set_only_once": 0, 45 | "unique": 0 46 | }, 47 | { 48 | "allow_bulk_edit": 0, 49 | "allow_on_submit": 0, 50 | "bold": 0, 51 | "collapsible": 0, 52 | "columns": 0, 53 | "fieldname": "column_break_3", 54 | "fieldtype": "Column Break", 55 | "hidden": 0, 56 | "ignore_user_permissions": 0, 57 | "ignore_xss_filter": 0, 58 | "in_filter": 0, 59 | "in_global_search": 0, 60 | "in_list_view": 0, 61 | "in_standard_filter": 0, 62 | "length": 0, 63 | "no_copy": 0, 64 | "permlevel": 0, 65 | "precision": "", 66 | "print_hide": 0, 67 | "print_hide_if_no_value": 0, 68 | "read_only": 0, 69 | "remember_last_selected_value": 0, 70 | "report_hide": 0, 71 | "reqd": 0, 72 | "search_index": 0, 73 | "set_only_once": 0, 74 | "unique": 0 75 | }, 76 | { 77 | "allow_bulk_edit": 0, 78 | "allow_on_submit": 0, 79 | "bold": 0, 80 | "collapsible": 0, 81 | "columns": 0, 82 | "fieldname": "region", 83 | "fieldtype": "Data", 84 | "hidden": 0, 85 | "ignore_user_permissions": 0, 86 | "ignore_xss_filter": 0, 87 | "in_filter": 0, 88 | "in_global_search": 0, 89 | "in_list_view": 0, 90 | "in_standard_filter": 0, 91 | "label": "Region", 92 | "length": 0, 93 | "no_copy": 0, 94 | "permlevel": 0, 95 | "precision": "", 96 | "print_hide": 0, 97 | "print_hide_if_no_value": 0, 98 | "read_only": 0, 99 | "remember_last_selected_value": 0, 100 | "report_hide": 0, 101 | "reqd": 1, 102 | "search_index": 0, 103 | "set_only_once": 0, 104 | "unique": 0 105 | }, 106 | { 107 | "allow_bulk_edit": 0, 108 | "allow_on_submit": 0, 109 | "bold": 0, 110 | "collapsible": 0, 111 | "columns": 0, 112 | "fieldname": "section_break_5", 113 | "fieldtype": "Section Break", 114 | "hidden": 0, 115 | "ignore_user_permissions": 0, 116 | "ignore_xss_filter": 0, 117 | "in_filter": 0, 118 | "in_global_search": 0, 119 | "in_list_view": 0, 120 | "in_standard_filter": 0, 121 | "length": 0, 122 | "no_copy": 0, 123 | "permlevel": 0, 124 | "precision": "", 125 | "print_hide": 0, 126 | "print_hide_if_no_value": 0, 127 | "read_only": 0, 128 | "remember_last_selected_value": 0, 129 | "report_hide": 0, 130 | "reqd": 0, 131 | "search_index": 0, 132 | "set_only_once": 0, 133 | "unique": 0 134 | }, 135 | { 136 | "allow_bulk_edit": 0, 137 | "allow_on_submit": 0, 138 | "bold": 0, 139 | "collapsible": 0, 140 | "columns": 0, 141 | "fieldname": "introduction", 142 | "fieldtype": "Text Editor", 143 | "hidden": 0, 144 | "ignore_user_permissions": 0, 145 | "ignore_xss_filter": 0, 146 | "in_filter": 0, 147 | "in_global_search": 0, 148 | "in_list_view": 1, 149 | "in_standard_filter": 0, 150 | "label": "Introduction", 151 | "length": 0, 152 | "no_copy": 0, 153 | "permlevel": 0, 154 | "precision": "", 155 | "print_hide": 0, 156 | "print_hide_if_no_value": 0, 157 | "read_only": 0, 158 | "remember_last_selected_value": 0, 159 | "report_hide": 0, 160 | "reqd": 1, 161 | "search_index": 0, 162 | "set_only_once": 0, 163 | "unique": 0 164 | }, 165 | { 166 | "allow_bulk_edit": 0, 167 | "allow_on_submit": 0, 168 | "bold": 0, 169 | "collapsible": 0, 170 | "columns": 0, 171 | "fieldname": "meetup_embed_html", 172 | "fieldtype": "Code", 173 | "hidden": 0, 174 | "ignore_user_permissions": 0, 175 | "ignore_xss_filter": 0, 176 | "in_filter": 0, 177 | "in_global_search": 0, 178 | "in_list_view": 0, 179 | "in_standard_filter": 0, 180 | "label": "Meetup Embed HTML", 181 | "length": 0, 182 | "no_copy": 0, 183 | "permlevel": 0, 184 | "precision": "", 185 | "print_hide": 0, 186 | "print_hide_if_no_value": 0, 187 | "read_only": 0, 188 | "remember_last_selected_value": 0, 189 | "report_hide": 0, 190 | "reqd": 0, 191 | "search_index": 0, 192 | "set_only_once": 0, 193 | "unique": 0 194 | }, 195 | { 196 | "allow_bulk_edit": 0, 197 | "allow_on_submit": 0, 198 | "bold": 0, 199 | "collapsible": 0, 200 | "columns": 0, 201 | "fieldname": "address", 202 | "fieldtype": "Text", 203 | "hidden": 0, 204 | "ignore_user_permissions": 0, 205 | "ignore_xss_filter": 0, 206 | "in_filter": 0, 207 | "in_global_search": 0, 208 | "in_list_view": 0, 209 | "in_standard_filter": 0, 210 | "label": "Address", 211 | "length": 0, 212 | "no_copy": 0, 213 | "permlevel": 0, 214 | "precision": "", 215 | "print_hide": 0, 216 | "print_hide_if_no_value": 0, 217 | "read_only": 0, 218 | "remember_last_selected_value": 0, 219 | "report_hide": 0, 220 | "reqd": 0, 221 | "search_index": 0, 222 | "set_only_once": 0, 223 | "unique": 0 224 | }, 225 | { 226 | "allow_bulk_edit": 0, 227 | "allow_on_submit": 0, 228 | "bold": 0, 229 | "collapsible": 0, 230 | "columns": 0, 231 | "description": "chapters/chapter_name\nleave blank automatically set after saving chapter.", 232 | "fieldname": "route", 233 | "fieldtype": "Data", 234 | "hidden": 0, 235 | "ignore_user_permissions": 0, 236 | "ignore_xss_filter": 0, 237 | "in_filter": 0, 238 | "in_global_search": 0, 239 | "in_list_view": 1, 240 | "in_standard_filter": 0, 241 | "label": "Route", 242 | "length": 0, 243 | "no_copy": 0, 244 | "permlevel": 0, 245 | "precision": "", 246 | "print_hide": 0, 247 | "print_hide_if_no_value": 0, 248 | "read_only": 0, 249 | "remember_last_selected_value": 0, 250 | "report_hide": 0, 251 | "reqd": 0, 252 | "search_index": 0, 253 | "set_only_once": 0, 254 | "unique": 0 255 | }, 256 | { 257 | "allow_bulk_edit": 0, 258 | "allow_on_submit": 0, 259 | "bold": 0, 260 | "collapsible": 0, 261 | "columns": 0, 262 | "fieldname": "published", 263 | "fieldtype": "Check", 264 | "hidden": 0, 265 | "ignore_user_permissions": 0, 266 | "ignore_xss_filter": 0, 267 | "in_filter": 0, 268 | "in_global_search": 0, 269 | "in_list_view": 0, 270 | "in_standard_filter": 0, 271 | "label": "Published", 272 | "length": 0, 273 | "no_copy": 0, 274 | "permlevel": 0, 275 | "precision": "", 276 | "print_hide": 0, 277 | "print_hide_if_no_value": 0, 278 | "read_only": 0, 279 | "remember_last_selected_value": 0, 280 | "report_hide": 0, 281 | "reqd": 0, 282 | "search_index": 0, 283 | "set_only_once": 0, 284 | "unique": 0 285 | }, 286 | { 287 | "allow_bulk_edit": 0, 288 | "allow_on_submit": 0, 289 | "bold": 0, 290 | "collapsible": 1, 291 | "columns": 0, 292 | "fieldname": "chapter_members", 293 | "fieldtype": "Section Break", 294 | "hidden": 0, 295 | "ignore_user_permissions": 0, 296 | "ignore_xss_filter": 0, 297 | "in_filter": 0, 298 | "in_global_search": 0, 299 | "in_list_view": 0, 300 | "in_standard_filter": 0, 301 | "label": "Chapter Members", 302 | "length": 0, 303 | "no_copy": 0, 304 | "permlevel": 0, 305 | "precision": "", 306 | "print_hide": 0, 307 | "print_hide_if_no_value": 0, 308 | "read_only": 0, 309 | "remember_last_selected_value": 0, 310 | "report_hide": 0, 311 | "reqd": 0, 312 | "search_index": 0, 313 | "set_only_once": 0, 314 | "unique": 0 315 | }, 316 | { 317 | "allow_bulk_edit": 0, 318 | "allow_on_submit": 0, 319 | "bold": 0, 320 | "collapsible": 0, 321 | "columns": 0, 322 | "fieldname": "members", 323 | "fieldtype": "Table", 324 | "hidden": 0, 325 | "ignore_user_permissions": 0, 326 | "ignore_xss_filter": 0, 327 | "in_filter": 0, 328 | "in_global_search": 0, 329 | "in_list_view": 0, 330 | "in_standard_filter": 0, 331 | "label": "Members", 332 | "length": 0, 333 | "no_copy": 0, 334 | "options": "Chapter Member", 335 | "permlevel": 0, 336 | "precision": "", 337 | "print_hide": 0, 338 | "print_hide_if_no_value": 0, 339 | "read_only": 0, 340 | "remember_last_selected_value": 0, 341 | "report_hide": 0, 342 | "reqd": 0, 343 | "search_index": 0, 344 | "set_only_once": 0, 345 | "unique": 0 346 | } 347 | ], 348 | "has_web_view": 1, 349 | "hide_heading": 0, 350 | "hide_toolbar": 0, 351 | "idx": 0, 352 | "image_view": 0, 353 | "in_create": 0, 354 | "is_published_field": "published", 355 | "is_submittable": 0, 356 | "issingle": 0, 357 | "istable": 0, 358 | "max_attachments": 0, 359 | "modified": "2017-12-14 12:59:31.424240", 360 | "modified_by": "Administrator", 361 | "module": "Non Profit", 362 | "name": "Chapter", 363 | "name_case": "Title Case", 364 | "owner": "Administrator", 365 | "permissions": [ 366 | { 367 | "amend": 0, 368 | "apply_user_permissions": 0, 369 | "cancel": 0, 370 | "create": 1, 371 | "delete": 1, 372 | "email": 1, 373 | "export": 1, 374 | "if_owner": 0, 375 | "import": 0, 376 | "permlevel": 0, 377 | "print": 1, 378 | "read": 1, 379 | "report": 1, 380 | "role": "Non Profit Manager", 381 | "set_user_permissions": 0, 382 | "share": 1, 383 | "submit": 0, 384 | "write": 1 385 | } 386 | ], 387 | "quick_entry": 1, 388 | "read_only": 0, 389 | "read_only_onload": 0, 390 | "restrict_to_domain": "Non Profit", 391 | "route": "chapters", 392 | "show_name_in_global_search": 0, 393 | "sort_field": "modified", 394 | "sort_order": "DESC", 395 | "track_changes": 1, 396 | "track_seen": 0 397 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter/chapter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | import frappe 6 | from frappe.website.website_generator import WebsiteGenerator 7 | 8 | 9 | class Chapter(WebsiteGenerator): 10 | _website = frappe._dict( 11 | condition_field = "published", 12 | ) 13 | 14 | def get_context(self, context): 15 | context.no_cache = True 16 | context.show_sidebar = True 17 | context.parents = [dict(label='View All Chapters', 18 | route='chapters', title='View Chapters')] 19 | 20 | def validate(self): 21 | if not self.route: #pylint: disable=E0203 22 | self.route = 'chapters/' + self.scrub(self.name) 23 | 24 | def enable(self): 25 | chapter = frappe.get_doc('Chapter', frappe.form_dict.name) 26 | chapter.append('members', dict(enable=self.value)) 27 | chapter.save(ignore_permissions=1) 28 | frappe.db.commit() 29 | 30 | 31 | def get_list_context(context): 32 | context.allow_guest = True 33 | context.no_cache = True 34 | context.show_sidebar = True 35 | context.title = 'All Chapters' 36 | context.no_breadcrumbs = True 37 | context.order_by = 'creation desc' 38 | 39 | 40 | @frappe.whitelist() 41 | def leave(title, user_id, leave_reason): 42 | chapter = frappe.get_doc("Chapter", title) 43 | for member in chapter.members: 44 | if member.user == user_id: 45 | member.enabled = 0 46 | member.leave_reason = leave_reason 47 | chapter.save(ignore_permissions=1) 48 | frappe.db.commit() 49 | return "Thank you for Feedback" 50 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter/templates/chapter.html: -------------------------------------------------------------------------------- 1 | {% extends "templates/web.html" %} 2 | 3 | {% block page_content %} 4 |

{{ title }}

5 |

{{ introduction }}

6 | {% if meetup_embed_html %} 7 | {{ meetup_embed_html }} 8 | {% endif %} 9 |

Member Details

10 | 11 | {% if members %} 12 | 13 | {% set index = [1] %} 14 | {% for user in members %} 15 | {% if user.enabled == 1 %} 16 | 17 | 40 | 41 | {% set __ = index.append(1) %} 42 | {% endif %} 43 | {% endfor %} 44 |
18 |
19 |
20 |
21 |
22 | {{ index|length }}. {{ frappe.db.get_value('User', user.user, 'full_name') }}
23 |
24 |
25 | {% if user.website_url %} 26 | {{ user.website_url | truncate (50) or '' }} 27 | {% endif %} 28 |
29 |
30 |
31 |

32 |
33 | {% if user.introduction %} 34 | {{ user.introduction }} 35 | {% endif %} 36 |
37 |
38 | 39 |
45 | {% else %} 46 |

No member yet.

47 | {% endif %} 48 | 49 |

Chapter Head

50 |
51 | 52 | 53 | {% set doc = frappe.get_doc('Member',chapter_head) %} 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 |
Name{{ doc.member_name }}
Email{{ frappe.db.get_value('User', doc.email, 'email') or '' }}
Phone{{ frappe.db.get_value('User', doc.email, 'phone') or '' }}
67 |
68 | 69 | {% if address %} 70 |

Address

71 |
72 |

{{ address or ''}}

73 |
74 | {% endif %} 75 | 76 |

Join this Chapter

77 |

Leave this Chapter

78 | 79 | {% endblock %} 80 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter/templates/chapter_row.html: -------------------------------------------------------------------------------- 1 | {% if doc.published %} 2 |
3 | 4 |

{{ doc.name }}

5 |

6 | Chapter Head : {{ frappe.db.get_value('User', chapter_head, 'full_name') }} 7 | 8 | {% if members %} 9 | {% set index = [] %} 10 | {% for user in members %} 11 | {% if user.enabled == 1 %} 12 | {% set __ = index.append(1) %} 13 | {% endif %} 14 | {% endfor %} 15 | Members: {{ index|length }} 16 | {% else %} 17 | Members: 0 18 | {% endif %} 19 | 20 | 21 |

22 |

{{ html2text(doc.introduction) | truncate (200) }}

23 |
24 |
25 | {% endif %} 26 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter/test_chapter.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestChapter(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter_member/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/chapter_member/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter_member/chapter_member.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow_copy": 0, 3 | "allow_guest_to_view": 0, 4 | "allow_import": 0, 5 | "allow_rename": 0, 6 | "beta": 0, 7 | "creation": "2017-09-14 13:38:04.296375", 8 | "custom": 0, 9 | "docstatus": 0, 10 | "doctype": "DocType", 11 | "document_type": "", 12 | "editable_grid": 1, 13 | "engine": "InnoDB", 14 | "fields": [ 15 | { 16 | "allow_bulk_edit": 0, 17 | "allow_on_submit": 0, 18 | "bold": 0, 19 | "collapsible": 0, 20 | "columns": 0, 21 | "fieldname": "user", 22 | "fieldtype": "Link", 23 | "hidden": 0, 24 | "ignore_user_permissions": 0, 25 | "ignore_xss_filter": 0, 26 | "in_filter": 0, 27 | "in_global_search": 0, 28 | "in_list_view": 1, 29 | "in_standard_filter": 0, 30 | "label": "User", 31 | "length": 0, 32 | "no_copy": 0, 33 | "options": "User", 34 | "permlevel": 0, 35 | "precision": "", 36 | "print_hide": 0, 37 | "print_hide_if_no_value": 0, 38 | "read_only": 0, 39 | "remember_last_selected_value": 0, 40 | "report_hide": 0, 41 | "reqd": 1, 42 | "search_index": 0, 43 | "set_only_once": 0, 44 | "translatable": 0, 45 | "unique": 0 46 | }, 47 | { 48 | "allow_bulk_edit": 0, 49 | "allow_on_submit": 0, 50 | "bold": 0, 51 | "collapsible": 0, 52 | "columns": 0, 53 | "fieldname": "introduction", 54 | "fieldtype": "Data", 55 | "hidden": 0, 56 | "ignore_user_permissions": 0, 57 | "ignore_xss_filter": 0, 58 | "in_filter": 0, 59 | "in_global_search": 0, 60 | "in_list_view": 1, 61 | "in_standard_filter": 0, 62 | "label": "Introduction", 63 | "length": 0, 64 | "no_copy": 0, 65 | "permlevel": 0, 66 | "precision": "", 67 | "print_hide": 0, 68 | "print_hide_if_no_value": 0, 69 | "read_only": 0, 70 | "remember_last_selected_value": 0, 71 | "report_hide": 0, 72 | "reqd": 0, 73 | "search_index": 0, 74 | "set_only_once": 0, 75 | "translatable": 0, 76 | "unique": 0 77 | }, 78 | { 79 | "allow_bulk_edit": 0, 80 | "allow_on_submit": 0, 81 | "bold": 0, 82 | "collapsible": 0, 83 | "columns": 0, 84 | "fieldname": "website_url", 85 | "fieldtype": "Data", 86 | "hidden": 0, 87 | "ignore_user_permissions": 0, 88 | "ignore_xss_filter": 0, 89 | "in_filter": 0, 90 | "in_global_search": 0, 91 | "in_list_view": 1, 92 | "in_standard_filter": 0, 93 | "label": "Website URL", 94 | "length": 0, 95 | "no_copy": 0, 96 | "permlevel": 0, 97 | "precision": "", 98 | "print_hide": 0, 99 | "print_hide_if_no_value": 0, 100 | "read_only": 0, 101 | "remember_last_selected_value": 0, 102 | "report_hide": 0, 103 | "reqd": 0, 104 | "search_index": 0, 105 | "set_only_once": 0, 106 | "translatable": 0, 107 | "unique": 0 108 | }, 109 | { 110 | "allow_bulk_edit": 0, 111 | "allow_on_submit": 0, 112 | "bold": 0, 113 | "collapsible": 0, 114 | "columns": 2, 115 | "default": "1", 116 | "fieldname": "enabled", 117 | "fieldtype": "Check", 118 | "hidden": 0, 119 | "ignore_user_permissions": 0, 120 | "ignore_xss_filter": 0, 121 | "in_filter": 0, 122 | "in_global_search": 0, 123 | "in_list_view": 1, 124 | "in_standard_filter": 0, 125 | "label": "Enabled", 126 | "length": 0, 127 | "no_copy": 0, 128 | "permlevel": 0, 129 | "precision": "", 130 | "print_hide": 0, 131 | "print_hide_if_no_value": 0, 132 | "read_only": 0, 133 | "remember_last_selected_value": 0, 134 | "report_hide": 0, 135 | "reqd": 0, 136 | "search_index": 0, 137 | "set_only_once": 0, 138 | "translatable": 0, 139 | "unique": 0 140 | }, 141 | { 142 | "allow_bulk_edit": 0, 143 | "allow_on_submit": 0, 144 | "bold": 0, 145 | "collapsible": 0, 146 | "columns": 0, 147 | "fieldname": "leave_reason", 148 | "fieldtype": "Data", 149 | "hidden": 0, 150 | "ignore_user_permissions": 0, 151 | "ignore_xss_filter": 0, 152 | "in_filter": 0, 153 | "in_global_search": 0, 154 | "in_list_view": 0, 155 | "in_standard_filter": 0, 156 | "label": "Leave Reason", 157 | "length": 0, 158 | "no_copy": 0, 159 | "permlevel": 0, 160 | "precision": "", 161 | "print_hide": 0, 162 | "print_hide_if_no_value": 0, 163 | "read_only": 0, 164 | "remember_last_selected_value": 0, 165 | "report_hide": 0, 166 | "reqd": 0, 167 | "search_index": 0, 168 | "set_only_once": 0, 169 | "translatable": 0, 170 | "unique": 0 171 | } 172 | ], 173 | "has_web_view": 0, 174 | "hide_heading": 0, 175 | "hide_toolbar": 0, 176 | "idx": 0, 177 | "image_view": 0, 178 | "in_create": 0, 179 | "is_submittable": 0, 180 | "issingle": 0, 181 | "istable": 1, 182 | "max_attachments": 0, 183 | "modified": "2018-03-07 05:36:51.664816", 184 | "modified_by": "Administrator", 185 | "module": "Non Profit", 186 | "name": "Chapter Member", 187 | "name_case": "", 188 | "owner": "Administrator", 189 | "permissions": [], 190 | "quick_entry": 1, 191 | "read_only": 0, 192 | "read_only_onload": 0, 193 | "restrict_to_domain": "Non Profit", 194 | "show_name_in_global_search": 0, 195 | "sort_field": "modified", 196 | "sort_order": "DESC", 197 | "track_changes": 1, 198 | "track_seen": 0 199 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/chapter_member/chapter_member.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class ChapterMember(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/donation/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donation/donation.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Donation', { 5 | refresh: function(frm) { 6 | if (frm.doc.docstatus === 1 && !frm.doc.paid) { 7 | frm.add_custom_button(__('Create Payment Entry'), function() { 8 | frm.events.make_payment_entry(frm); 9 | }); 10 | } 11 | }, 12 | 13 | make_payment_entry: function(frm) { 14 | return frappe.call({ 15 | method: 'non_profit.non_profit.custom_doctype.payment_entry.get_donation_payment_entry', 16 | args: { 17 | 'dt': frm.doc.doctype, 18 | 'dn': frm.doc.name 19 | }, 20 | callback: function(r) { 21 | var doc = frappe.model.sync(r.message); 22 | frappe.set_route('Form', doc[0].doctype, doc[0].name); 23 | } 24 | }); 25 | }, 26 | }); 27 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donation/donation.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "naming_series:", 4 | "creation": "2021-02-17 10:28:52.645731", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "naming_series", 10 | "donor", 11 | "donor_name", 12 | "email", 13 | "column_break_4", 14 | "company", 15 | "date", 16 | "payment_details_section", 17 | "paid", 18 | "amount", 19 | "mode_of_payment", 20 | "column_break_12", 21 | "payment_id", 22 | "amended_from" 23 | ], 24 | "fields": [ 25 | { 26 | "fieldname": "donor", 27 | "fieldtype": "Link", 28 | "label": "Donor", 29 | "options": "Donor", 30 | "reqd": 1 31 | }, 32 | { 33 | "fetch_from": "donor.donor_name", 34 | "fieldname": "donor_name", 35 | "fieldtype": "Data", 36 | "in_list_view": 1, 37 | "in_standard_filter": 1, 38 | "label": "Donor Name", 39 | "read_only": 1 40 | }, 41 | { 42 | "fetch_from": "donor.email", 43 | "fieldname": "email", 44 | "fieldtype": "Data", 45 | "in_list_view": 1, 46 | "in_standard_filter": 1, 47 | "label": "Email", 48 | "read_only": 1 49 | }, 50 | { 51 | "fieldname": "column_break_4", 52 | "fieldtype": "Column Break" 53 | }, 54 | { 55 | "fieldname": "date", 56 | "fieldtype": "Date", 57 | "label": "Date", 58 | "reqd": 1 59 | }, 60 | { 61 | "fieldname": "payment_details_section", 62 | "fieldtype": "Section Break", 63 | "label": "Payment Details" 64 | }, 65 | { 66 | "fieldname": "amount", 67 | "fieldtype": "Currency", 68 | "label": "Amount", 69 | "reqd": 1 70 | }, 71 | { 72 | "fieldname": "mode_of_payment", 73 | "fieldtype": "Link", 74 | "label": "Mode of Payment", 75 | "options": "Mode of Payment" 76 | }, 77 | { 78 | "fieldname": "naming_series", 79 | "fieldtype": "Select", 80 | "label": "Naming Series", 81 | "options": "NPO-DTN-.YYYY.-" 82 | }, 83 | { 84 | "default": "0", 85 | "fieldname": "paid", 86 | "fieldtype": "Check", 87 | "in_list_view": 1, 88 | "in_standard_filter": 1, 89 | "label": "Paid" 90 | }, 91 | { 92 | "fieldname": "company", 93 | "fieldtype": "Link", 94 | "label": "Company", 95 | "options": "Company", 96 | "reqd": 1 97 | }, 98 | { 99 | "fieldname": "amended_from", 100 | "fieldtype": "Link", 101 | "label": "Amended From", 102 | "no_copy": 1, 103 | "options": "Donation", 104 | "print_hide": 1, 105 | "read_only": 1 106 | }, 107 | { 108 | "fieldname": "payment_id", 109 | "fieldtype": "Data", 110 | "label": "Payment ID" 111 | }, 112 | { 113 | "fieldname": "column_break_12", 114 | "fieldtype": "Column Break" 115 | } 116 | ], 117 | "index_web_pages_for_search": 1, 118 | "is_submittable": 1, 119 | "links": [], 120 | "modified": "2022-03-16 17:18:45.611741", 121 | "modified_by": "Administrator", 122 | "module": "Non Profit", 123 | "name": "Donation", 124 | "owner": "Administrator", 125 | "permissions": [ 126 | { 127 | "create": 1, 128 | "delete": 1, 129 | "email": 1, 130 | "export": 1, 131 | "print": 1, 132 | "read": 1, 133 | "report": 1, 134 | "role": "System Manager", 135 | "select": 1, 136 | "share": 1, 137 | "submit": 1, 138 | "write": 1 139 | }, 140 | { 141 | "create": 1, 142 | "delete": 1, 143 | "email": 1, 144 | "export": 1, 145 | "print": 1, 146 | "read": 1, 147 | "report": 1, 148 | "role": "Non Profit Manager", 149 | "select": 1, 150 | "share": 1, 151 | "submit": 1, 152 | "write": 1 153 | } 154 | ], 155 | "search_fields": "donor_name, email", 156 | "sort_field": "modified", 157 | "sort_order": "DESC", 158 | "title_field": "donor_name", 159 | "track_changes": 1 160 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donation/donation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | import json 6 | 7 | import frappe 8 | from frappe import _ 9 | from frappe.email import sendmail_to_system_managers 10 | from frappe.model.document import Document 11 | from frappe.utils import flt, get_link_to_form, getdate 12 | 13 | from non_profit.non_profit.doctype.membership.membership import verify_signature 14 | 15 | 16 | class Donation(Document): 17 | def validate(self): 18 | if not self.donor or not frappe.db.exists('Donor', self.donor): 19 | # for web forms 20 | user_type = frappe.db.get_value('User', frappe.session.user, 'user_type') 21 | if user_type == 'Website User': 22 | self.create_donor_for_website_user() 23 | else: 24 | frappe.throw(_('Please select a Member')) 25 | 26 | def create_donor_for_website_user(self): 27 | donor_name = frappe.get_value('Donor', dict(email=frappe.session.user)) 28 | 29 | if not donor_name: 30 | user = frappe.get_doc('User', frappe.session.user) 31 | donor = frappe.get_doc(dict( 32 | doctype='Donor', 33 | donor_type=self.get('donor_type'), 34 | email=frappe.session.user, 35 | member_name=user.get_fullname() 36 | )).insert(ignore_permissions=True) 37 | donor_name = donor.name 38 | 39 | if self.get('__islocal'): 40 | self.donor = donor_name 41 | 42 | def on_payment_authorized(self, *args, **kwargs): 43 | self.db_set("paid", 1) 44 | self.load_from_db() 45 | self.create_payment_entry() 46 | 47 | def create_payment_entry(self, date=None): 48 | settings = frappe.get_doc('Non Profit Settings') 49 | if not settings.automate_donation_payment_entries: 50 | return 51 | 52 | if not settings.donation_payment_account: 53 | frappe.throw(_('You need to set Payment Account for Donation in {0}').format( 54 | get_link_to_form('Non Profit Settings', 'Non Profit Settings'))) 55 | 56 | from non_profit.non_profit.custom_doctype.payment_entry import get_donation_payment_entry 57 | 58 | frappe.flags.ignore_account_permission = True 59 | pe = get_donation_payment_entry(dt=self.doctype, dn=self.name) 60 | frappe.flags.ignore_account_permission = False 61 | pe.paid_from = settings.donation_debit_account 62 | pe.paid_to = settings.donation_payment_account 63 | pe.posting_date = date or getdate() 64 | pe.reference_no = self.name 65 | pe.reference_date = date or getdate() 66 | pe.flags.ignore_mandatory = True 67 | pe.insert() 68 | pe.submit() 69 | 70 | def on_cancel(self): 71 | self.ignore_linked_doctypes = ( 72 | "GL Entry", 73 | "Stock Ledger Entry", 74 | "Payment Ledger Entry", 75 | "Repost Payment Ledger", 76 | "Repost Payment Ledger Items", 77 | "Repost Accounting Ledger", 78 | "Repost Accounting Ledger Items", 79 | "Unreconcile Payment", 80 | "Unreconcile Payment Entries", 81 | ) 82 | 83 | 84 | @frappe.whitelist(allow_guest=True) 85 | def capture_razorpay_donations(*args, **kwargs): 86 | """ 87 | Creates Donation from Razorpay Webhook Request Data on payment.captured event 88 | Creates Donor from email if not found 89 | """ 90 | data = frappe.request.get_data(as_text=True) 91 | 92 | try: 93 | verify_signature(data, endpoint='Donation') 94 | except Exception as e: 95 | log = frappe.log_error(e, 'Donation Webhook Verification Error') 96 | notify_failure(log) 97 | return { 'status': 'Failed', 'reason': e } 98 | 99 | if isinstance(data, str): 100 | data = json.loads(data) 101 | data = frappe._dict(data) 102 | 103 | payment = data.payload.get('payment', {}).get('entity', {}) 104 | payment = frappe._dict(payment) 105 | 106 | try: 107 | if not data.event == 'payment.captured': 108 | return 109 | 110 | # to avoid capturing subscription payments as donations 111 | if payment.invoice_id or ( 112 | payment.description and "subscription" in str(payment.description).lower() 113 | ): 114 | return 115 | 116 | donor = get_donor(payment.email) 117 | if not donor: 118 | donor = create_donor(payment) 119 | 120 | donation = create_razorpay_donation(donor, payment) 121 | donation.run_method('create_payment_entry') 122 | 123 | except Exception as e: 124 | message = '{0}\n\n{1}\n\n{2}: {3}'.format(e, frappe.get_traceback(), _('Payment ID'), payment.id) 125 | log = frappe.log_error(message, _('Error creating donation entry for {0}').format(donor.name)) 126 | notify_failure(log) 127 | return { 'status': 'Failed', 'reason': e } 128 | 129 | return { 'status': 'Success' } 130 | 131 | 132 | def create_razorpay_donation(donor, payment): 133 | if not frappe.db.exists('Mode of Payment', payment.method): 134 | create_mode_of_payment(payment.method) 135 | 136 | company = get_company_for_donations() 137 | donation = frappe.get_doc({ 138 | 'doctype': 'Donation', 139 | 'company': company, 140 | 'donor': donor.name, 141 | 'donor_name': donor.donor_name, 142 | 'email': donor.email, 143 | 'date': getdate(), 144 | 'amount': flt(payment.amount) / 100, # Convert to rupees from paise 145 | 'mode_of_payment': payment.method, 146 | 'payment_id': payment.id 147 | }).insert(ignore_mandatory=True) 148 | 149 | donation.submit() 150 | return donation 151 | 152 | 153 | def get_donor(email): 154 | donors = frappe.get_all('Donor', 155 | filters={'email': email}, 156 | order_by='creation desc') 157 | 158 | try: 159 | return frappe.get_doc('Donor', donors[0]['name']) 160 | except Exception: 161 | return None 162 | 163 | 164 | @frappe.whitelist() 165 | def create_donor(payment): 166 | donor_details = frappe._dict(payment) 167 | donor_type = frappe.db.get_single_value('Non Profit Settings', 'default_donor_type') 168 | 169 | donor = frappe.new_doc('Donor') 170 | donor.update({ 171 | 'donor_name': donor_details.email, 172 | 'donor_type': donor_type, 173 | 'email': donor_details.email, 174 | 'contact': donor_details.contact 175 | }) 176 | 177 | if donor_details.get('notes'): 178 | donor = get_additional_notes(donor, donor_details) 179 | 180 | donor.insert(ignore_mandatory=True) 181 | return donor 182 | 183 | 184 | def get_company_for_donations(): 185 | company = frappe.db.get_single_value('Non Profit Settings', 'donation_company') 186 | if not company: 187 | from non_profit.non_profit.utils import get_company 188 | company = get_company() 189 | return company 190 | 191 | 192 | def get_additional_notes(donor, donor_details): 193 | if type(donor_details.notes) == dict: 194 | for k, v in donor_details.notes.items(): 195 | notes = '\n'.join('{}: {}'.format(k, v)) 196 | 197 | # extract donor name from notes 198 | if 'name' in k.lower(): 199 | donor.update({ 200 | 'donor_name': donor_details.notes.get(k) 201 | }) 202 | 203 | # extract pan from notes 204 | if 'pan' in k.lower(): 205 | donor.update({ 206 | 'pan_number': donor_details.notes.get(k) 207 | }) 208 | 209 | donor.add_comment('Comment', notes) 210 | 211 | elif type(donor_details.notes) == str: 212 | donor.add_comment('Comment', donor_details.notes) 213 | 214 | return donor 215 | 216 | 217 | def create_mode_of_payment(method): 218 | frappe.get_doc({ 219 | 'doctype': 'Mode of Payment', 220 | 'mode_of_payment': method 221 | }).insert(ignore_mandatory=True) 222 | 223 | 224 | def notify_failure(log): 225 | try: 226 | content = ''' 227 | Dear System Manager, 228 | Razorpay webhook for creating donation failed due to some reason. 229 | Please check the error log linked below 230 | Error Log: {0} 231 | Regards, Administrator 232 | '''.format(get_link_to_form('Error Log', log.name)) 233 | 234 | sendmail_to_system_managers(_('[Important] [ERPNext] Razorpay donation webhook failed, please check.'), content) 235 | except Exception: 236 | pass 237 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donation/donation_dashboard.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return { 6 | 'fieldname': 'donation', 7 | 'non_standard_fieldnames': { 8 | 'Payment Entry': 'reference_name' 9 | }, 10 | 'transactions': [ 11 | { 12 | 'label': _('Payment'), 13 | 'items': ['Payment Entry'] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donation/test_donation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | import unittest 4 | 5 | import frappe 6 | 7 | from non_profit.non_profit.doctype.donation.donation import create_razorpay_donation 8 | 9 | 10 | class TestDonation(unittest.TestCase): 11 | def setUp(self): 12 | create_donor_type() 13 | settings = frappe.get_doc('Non Profit Settings') 14 | settings.company = '_Test Company' 15 | settings.donation_company = '_Test Company' 16 | settings.default_donor_type = '_Test Donor' 17 | settings.automate_donation_payment_entries = 1 18 | settings.donation_debit_account = 'Debtors - _TC' 19 | settings.donation_payment_account = 'Cash - _TC' 20 | settings.creation_user = 'Administrator' 21 | settings.flags.ignore_permissions = True 22 | settings.save() 23 | 24 | def test_payment_entry_for_donations(self): 25 | donor = create_donor() 26 | create_mode_of_payment() 27 | payment = frappe._dict({ 28 | 'amount': 100, 29 | 'method': 'Debit Card', 30 | 'id': 'pay_MeXAmsgeKOhq7O' 31 | }) 32 | donation = create_razorpay_donation(donor, payment) 33 | 34 | self.assertTrue(donation.name) 35 | 36 | # Naive test to check if at all payment entry is generated 37 | # This method is actually triggered from Payment Gateway 38 | # In any case if details were missing, this would throw an error 39 | donation.on_payment_authorized(status='Completed') 40 | donation.reload() 41 | 42 | self.assertEqual(donation.paid, 1) 43 | self.assertTrue(frappe.db.exists('Payment Entry', {'reference_no': donation.name})) 44 | 45 | 46 | def create_donor_type(): 47 | if not frappe.db.exists('Donor Type', '_Test Donor'): 48 | frappe.get_doc({ 49 | 'doctype': 'Donor Type', 50 | 'donor_type': '_Test Donor' 51 | }).insert() 52 | 53 | 54 | def create_donor(): 55 | donor = frappe.db.exists('Donor', 'donor@test.com') 56 | if donor: 57 | return frappe.get_doc('Donor', 'donor@test.com') 58 | else: 59 | return frappe.get_doc({ 60 | 'doctype': 'Donor', 61 | 'donor_name': '_Test Donor', 62 | 'donor_type': '_Test Donor', 63 | 'email': 'donor@test.com' 64 | }).insert() 65 | 66 | 67 | def create_mode_of_payment(): 68 | if not frappe.db.exists('Mode of Payment', 'Debit Card'): 69 | frappe.get_doc({ 70 | 'doctype': 'Mode of Payment', 71 | 'mode_of_payment': 'Debit Card', 72 | 'accounts': [{ 73 | 'company': '_Test Company', 74 | 'default_account': 'Cash - _TC' 75 | }] 76 | }).insert() 77 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/donor/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor/donor.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Donor', { 5 | refresh: function(frm) { 6 | frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Donor'}; 7 | 8 | frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); 9 | 10 | if(!frm.doc.__islocal) { 11 | frappe.contacts.render_address_and_contact(frm); 12 | } else { 13 | frappe.contacts.clear_address_and_contact(frm); 14 | } 15 | 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor/donor.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "autoname": "field:email", 5 | "creation": "2017-09-19 16:20:27.510196", 6 | "doctype": "DocType", 7 | "editable_grid": 1, 8 | "engine": "InnoDB", 9 | "field_order": [ 10 | "donor_name", 11 | "column_break_5", 12 | "donor_type", 13 | "email", 14 | "image", 15 | "address_contacts", 16 | "address_html", 17 | "column_break_9", 18 | "contact_html" 19 | ], 20 | "fields": [ 21 | { 22 | "fieldname": "donor_name", 23 | "fieldtype": "Data", 24 | "in_list_view": 1, 25 | "label": "Donor Name", 26 | "reqd": 1 27 | }, 28 | { 29 | "fieldname": "column_break_5", 30 | "fieldtype": "Column Break" 31 | }, 32 | { 33 | "fieldname": "donor_type", 34 | "fieldtype": "Link", 35 | "in_list_view": 1, 36 | "label": "Donor Type", 37 | "options": "Donor Type", 38 | "reqd": 1 39 | }, 40 | { 41 | "fieldname": "email", 42 | "fieldtype": "Data", 43 | "in_list_view": 1, 44 | "label": "Email", 45 | "reqd": 1, 46 | "unique": 1 47 | }, 48 | { 49 | "fieldname": "image", 50 | "fieldtype": "Attach Image", 51 | "hidden": 1, 52 | "label": "Image", 53 | "no_copy": 1, 54 | "print_hide": 1 55 | }, 56 | { 57 | "depends_on": "eval:!doc.__islocal;", 58 | "fieldname": "address_contacts", 59 | "fieldtype": "Section Break", 60 | "label": "Address and Contact", 61 | "options": "fa fa-map-marker" 62 | }, 63 | { 64 | "fieldname": "address_html", 65 | "fieldtype": "HTML", 66 | "label": "Address HTML" 67 | }, 68 | { 69 | "fieldname": "column_break_9", 70 | "fieldtype": "Column Break" 71 | }, 72 | { 73 | "fieldname": "contact_html", 74 | "fieldtype": "HTML", 75 | "label": "Contact HTML" 76 | } 77 | ], 78 | "image_field": "image", 79 | "links": [ 80 | { 81 | "link_doctype": "Donation", 82 | "link_fieldname": "donor" 83 | } 84 | ], 85 | "modified": "2021-02-17 16:36:33.470731", 86 | "modified_by": "Administrator", 87 | "module": "Non Profit", 88 | "name": "Donor", 89 | "owner": "Administrator", 90 | "permissions": [ 91 | { 92 | "create": 1, 93 | "delete": 1, 94 | "email": 1, 95 | "export": 1, 96 | "print": 1, 97 | "read": 1, 98 | "report": 1, 99 | "role": "Non Profit Manager", 100 | "share": 1, 101 | "write": 1 102 | } 103 | ], 104 | "quick_entry": 1, 105 | "restrict_to_domain": "Non Profit", 106 | "sort_field": "modified", 107 | "sort_order": "DESC", 108 | "title_field": "donor_name", 109 | "track_changes": 1 110 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor/donor.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.contacts.address_and_contact import load_address_and_contact 6 | from frappe.model.document import Document 7 | 8 | 9 | class Donor(Document): 10 | def onload(self): 11 | """Load address and contacts in `__onload`""" 12 | load_address_and_contact(self) 13 | 14 | def validate(self): 15 | from frappe.utils import validate_email_address 16 | if self.email: 17 | validate_email_address(self.email.strip(), True) 18 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor/donor_list.js: -------------------------------------------------------------------------------- 1 | frappe.listview_settings['Donor'] = { 2 | add_fields: ["donor_name", "donor_type", "image"], 3 | }; 4 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor/test_donor.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // rename this file from _test_[name] to test_[name] to activate 3 | // and remove above this line 4 | 5 | QUnit.test("test: Donor", function (assert) { 6 | let done = assert.async(); 7 | 8 | // number of asserts 9 | assert.expect(3); 10 | 11 | frappe.run_serially([ 12 | // insert a new Member 13 | () => frappe.tests.make('Donor', [ 14 | // values to be set 15 | {donor_name: 'Test Donor'}, 16 | {donor_type: 'Test Organization'}, 17 | {email: 'test@example.com'} 18 | ]), 19 | () => { 20 | assert.equal(cur_frm.doc.donor_name, 'Test Donor'); 21 | assert.equal(cur_frm.doc.donor_type, 'Test Organization'); 22 | assert.equal(cur_frm.doc.email, 'test@example.com'); 23 | }, 24 | () => done() 25 | ]); 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor/test_donor.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestDonor(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor_type/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/donor_type/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor_type/donor_type.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Donor Type', { 5 | refresh: function() { 6 | 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor_type/donor_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow_copy": 0, 3 | "allow_guest_to_view": 0, 4 | "allow_import": 0, 5 | "allow_rename": 0, 6 | "autoname": "field:donor_type", 7 | "beta": 0, 8 | "creation": "2017-09-19 16:19:16.639635", 9 | "custom": 0, 10 | "docstatus": 0, 11 | "doctype": "DocType", 12 | "document_type": "", 13 | "editable_grid": 1, 14 | "engine": "InnoDB", 15 | "fields": [ 16 | { 17 | "allow_bulk_edit": 0, 18 | "allow_on_submit": 0, 19 | "bold": 0, 20 | "collapsible": 0, 21 | "columns": 0, 22 | "fieldname": "donor_type", 23 | "fieldtype": "Data", 24 | "hidden": 0, 25 | "ignore_user_permissions": 0, 26 | "ignore_xss_filter": 0, 27 | "in_filter": 0, 28 | "in_global_search": 0, 29 | "in_list_view": 1, 30 | "in_standard_filter": 1, 31 | "label": "Donor Type", 32 | "length": 0, 33 | "no_copy": 0, 34 | "permlevel": 0, 35 | "precision": "", 36 | "print_hide": 0, 37 | "print_hide_if_no_value": 0, 38 | "read_only": 0, 39 | "remember_last_selected_value": 0, 40 | "report_hide": 0, 41 | "reqd": 1, 42 | "search_index": 0, 43 | "set_only_once": 0, 44 | "unique": 0 45 | } 46 | ], 47 | "has_web_view": 0, 48 | "hide_heading": 0, 49 | "hide_toolbar": 0, 50 | "idx": 0, 51 | "image_view": 0, 52 | "in_create": 0, 53 | "is_submittable": 0, 54 | "issingle": 0, 55 | "istable": 0, 56 | "max_attachments": 0, 57 | "modified": "2017-12-05 07:04:36.757595", 58 | "modified_by": "Administrator", 59 | "module": "Non Profit", 60 | "name": "Donor Type", 61 | "name_case": "", 62 | "owner": "Administrator", 63 | "permissions": [ 64 | { 65 | "amend": 0, 66 | "apply_user_permissions": 0, 67 | "cancel": 0, 68 | "create": 1, 69 | "delete": 1, 70 | "email": 1, 71 | "export": 1, 72 | "if_owner": 0, 73 | "import": 0, 74 | "permlevel": 0, 75 | "print": 1, 76 | "read": 1, 77 | "report": 1, 78 | "role": "Non Profit Manager", 79 | "set_user_permissions": 0, 80 | "share": 1, 81 | "submit": 0, 82 | "write": 1 83 | } 84 | ], 85 | "quick_entry": 1, 86 | "read_only": 0, 87 | "read_only_onload": 0, 88 | "restrict_to_domain": "Non Profit", 89 | "show_name_in_global_search": 0, 90 | "sort_field": "modified", 91 | "sort_order": "DESC", 92 | "track_changes": 1, 93 | "track_seen": 0 94 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor_type/donor_type.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class DonorType(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/donor_type/test_donor_type.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestDonorType(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/grant_application/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/grant_application/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/grant_application/grant_application.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Grant Application', { 5 | refresh: function(frm) { 6 | frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Grant Application'}; 7 | 8 | frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); 9 | 10 | if(!frm.doc.__islocal) { 11 | frappe.contacts.render_address_and_contact(frm); 12 | } else { 13 | frappe.contacts.clear_address_and_contact(frm); 14 | } 15 | 16 | if(frm.doc.status == 'Received' && !frm.doc.email_notification_sent){ 17 | frm.add_custom_button(__("Send Grant Review Email"), function() { 18 | frappe.call({ 19 | method: "non_profit.non_profit.doctype.grant_application.grant_application.send_grant_review_emails", 20 | args: { 21 | grant_application: frm.doc.name 22 | } 23 | }); 24 | }); 25 | } 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/grant_application/grant_application.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | import frappe 6 | from frappe import _ 7 | from frappe.contacts.address_and_contact import load_address_and_contact 8 | from frappe.utils import get_url 9 | from frappe.website.website_generator import WebsiteGenerator 10 | 11 | 12 | class GrantApplication(WebsiteGenerator): 13 | _website = frappe._dict( 14 | condition_field = "published", 15 | ) 16 | 17 | def validate(self): 18 | if not self.route: #pylint: disable=E0203 19 | self.route = 'grant-application/' + self.scrub(self.name) 20 | 21 | def onload(self): 22 | """Load address and contacts in `__onload`""" 23 | load_address_and_contact(self) 24 | 25 | def get_context(self, context): 26 | context.no_cache = True 27 | context.show_sidebar = True 28 | context.parents = [dict(label='View All Grant Applications', 29 | route='grant-application', title='View Grants')] 30 | 31 | def get_list_context(context): 32 | context.allow_guest = True 33 | context.no_cache = True 34 | context.no_breadcrumbs = True 35 | context.show_sidebar = True 36 | context.order_by = 'creation desc' 37 | context.introduction =''' 38 | Apply for new Grant Application''' 39 | 40 | @frappe.whitelist() 41 | def send_grant_review_emails(grant_application): 42 | grant = frappe.get_doc("Grant Application", grant_application) 43 | url = get_url('grant-application/{0}'.format(grant_application)) 44 | frappe.sendmail( 45 | recipients= grant.assessment_manager, 46 | sender=frappe.session.user, 47 | subject='Grant Application for {0}'.format(grant.applicant_name), 48 | message='

Please Review this grant application


' + url, 49 | reference_doctype=grant.doctype, 50 | reference_name=grant.name 51 | ) 52 | 53 | grant.status = 'In Progress' 54 | grant.email_notification_sent = 1 55 | grant.save() 56 | frappe.db.commit() 57 | 58 | frappe.msgprint(_("Review Invitation Sent")) 59 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/grant_application/templates/grant_application.html: -------------------------------------------------------------------------------- 1 | {% extends "templates/web.html" %} 2 | 3 | {% block page_content %} 4 |

{{ applicant_name }}

5 | {% if frappe.user == owner %} 6 |

Edit Grant

7 | {% endif %} 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
Organization/Indvidual{{ applicant_type }}
Grant Applicant Name{{ applicant_name}}
Date{{ frappe.format_date(creation) }}
Status{{ status }}
Email{{ email }}
31 |

Q. Please outline your current situation and why you are applying for a grant?

32 |

{{ grant_description }}

33 |

Q. Requested grant amount

34 |

{{ amount }}

35 |

Q. Have you recevied grant from us before?

36 |

{{ has_any_past_grant_record }}

37 |

Contact

38 | {% if frappe.user != 'Guest' %} 39 | 40 | {% if contact_person %} 41 | 42 | 43 | 44 | 45 | {% endif %} 46 | 47 | 48 | 49 | 50 |
Contact Person{{ contact_person }}
Email{{ email }}
51 | {% else %} 52 |

You must register and login to view contact details

53 | {% endif %} 54 |
55 | {% if frappe.session.user == assessment_manager %} 56 | {% if assessment_scale %} 57 |

Assessment Review done

58 | {% endif %} 59 | {% else %} 60 |


Post a New Grant

61 | {% endif %} 62 | {% endblock %} 63 | {% block style %} 64 | 65 | 68 | {% endblock %} 69 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/grant_application/templates/grant_application_row.html: -------------------------------------------------------------------------------- 1 | {% if doc.published %} 2 |
4 | 5 |

{{ doc.name }}

6 |

7 | {{ frappe.format_date(doc.creation) }} 8 |

9 |
10 |
11 | {% endif %} 12 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/grant_application/test_grant_application.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // rename this file from _test_[name] to test_[name] to activate 3 | // and remove above this line 4 | 5 | QUnit.test("test: Grant Application", function (assert) { 6 | let done = assert.async(); 7 | 8 | // number of asserts 9 | assert.expect(4); 10 | 11 | frappe.run_serially([ 12 | // insert a new Member 13 | () => frappe.tests.make('Grant Application', [ 14 | // values to be set 15 | {applicant_name: 'Test Organization'}, 16 | {contact_person:'Test Applicant'}, 17 | {email: 'test@example.com'}, 18 | {grant_description:'Test message'}, 19 | {amount: 150000} 20 | ]), 21 | () => { 22 | assert.equal(cur_frm.doc.applicant_name, 'Test Organization'); 23 | assert.equal(cur_frm.doc.contact_person, 'Test Applicant'); 24 | assert.equal(cur_frm.doc.email, 'test@example.com'); 25 | assert.equal(cur_frm.doc.amount, 150000); 26 | }, 27 | () => done() 28 | ]); 29 | 30 | }); 31 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/grant_application/test_grant_application.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestGrantApplication(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/member/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/member/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/member/member.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Member', { 5 | setup: function(frm) { 6 | frappe.db.get_single_value('Non Profit Settings', 'enable_razorpay_for_memberships').then(val => { 7 | if (val && (frm.doc.subscription_id || frm.doc.customer_id)) { 8 | frm.set_df_property('razorpay_details_section', 'hidden', false); 9 | } 10 | }) 11 | }, 12 | 13 | refresh: function(frm) { 14 | 15 | frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Member'}; 16 | 17 | frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); 18 | 19 | if(!frm.doc.__islocal) { 20 | frappe.contacts.render_address_and_contact(frm); 21 | 22 | // custom buttons 23 | frm.add_custom_button(__('Accounting Ledger'), function() { 24 | if (frm.doc.customer) { 25 | frappe.set_route('query-report', 'General Ledger', {party_type: 'Customer', party: frm.doc.customer}); 26 | } else { 27 | frappe.set_route('query-report', 'General Ledger', {party_type: 'Member', party: frm.doc.name}); 28 | } 29 | }); 30 | 31 | frm.add_custom_button(__('Accounts Receivable'), function() { 32 | frappe.set_route('query-report', 'Accounts Receivable', {customer: frm.doc.customer}); 33 | }); 34 | 35 | if (!frm.doc.customer) { 36 | frm.add_custom_button(__('Create Customer'), () => { 37 | frm.call('make_customer_and_link').then(() => { 38 | frm.reload_doc(); 39 | }); 40 | }); 41 | } 42 | 43 | // indicator 44 | erpnext.utils.set_party_dashboard_indicators(frm); 45 | 46 | } else { 47 | frappe.contacts.clear_address_and_contact(frm); 48 | } 49 | 50 | frappe.call({ 51 | method:"frappe.client.get_value", 52 | args:{ 53 | 'doctype':"Membership", 54 | 'filters':{'member': frm.doc.name}, 55 | 'fieldname':[ 56 | 'to_date' 57 | ] 58 | }, 59 | callback: function (data) { 60 | if(data.message) { 61 | frappe.model.set_value(frm.doctype,frm.docname, 62 | "membership_expiry_date", data.message.to_date); 63 | } 64 | } 65 | }); 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/member/member.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "autoname": "naming_series:", 5 | "creation": "2017-09-11 09:24:52.898356", 6 | "doctype": "DocType", 7 | "editable_grid": 1, 8 | "engine": "InnoDB", 9 | "field_order": [ 10 | "naming_series", 11 | "member_name", 12 | "membership_expiry_date", 13 | "column_break_5", 14 | "membership_type", 15 | "email_id", 16 | "image", 17 | "customer_section", 18 | "customer", 19 | "customer_name", 20 | "supplier_section", 21 | "supplier", 22 | "address_contacts", 23 | "address_html", 24 | "column_break_9", 25 | "contact_html", 26 | "razorpay_details_section", 27 | "subscription_id", 28 | "customer_id", 29 | "subscription_status", 30 | "column_break_21", 31 | "subscription_start", 32 | "subscription_end" 33 | ], 34 | "fields": [ 35 | { 36 | "fieldname": "naming_series", 37 | "fieldtype": "Select", 38 | "label": "Series", 39 | "options": "NPO-MEM-.YYYY.-", 40 | "reqd": 1 41 | }, 42 | { 43 | "fieldname": "member_name", 44 | "fieldtype": "Data", 45 | "in_list_view": 1, 46 | "label": "Member Name", 47 | "reqd": 1 48 | }, 49 | { 50 | "fieldname": "membership_expiry_date", 51 | "fieldtype": "Date", 52 | "label": "Membership Expiry Date" 53 | }, 54 | { 55 | "fieldname": "column_break_5", 56 | "fieldtype": "Column Break" 57 | }, 58 | { 59 | "fieldname": "membership_type", 60 | "fieldtype": "Link", 61 | "in_list_view": 1, 62 | "label": "Membership Type", 63 | "options": "Membership Type", 64 | "reqd": 1 65 | }, 66 | { 67 | "fieldname": "image", 68 | "fieldtype": "Attach Image", 69 | "hidden": 1, 70 | "label": "Image", 71 | "no_copy": 1, 72 | "print_hide": 1 73 | }, 74 | { 75 | "collapsible": 1, 76 | "fieldname": "customer_section", 77 | "fieldtype": "Section Break", 78 | "label": "Customer" 79 | }, 80 | { 81 | "fieldname": "customer", 82 | "fieldtype": "Link", 83 | "label": "Customer", 84 | "options": "Customer" 85 | }, 86 | { 87 | "fetch_from": "customer.customer_name", 88 | "fieldname": "customer_name", 89 | "fieldtype": "Data", 90 | "label": "Customer Name", 91 | "read_only": 1 92 | }, 93 | { 94 | "collapsible": 1, 95 | "fieldname": "supplier_section", 96 | "fieldtype": "Section Break", 97 | "label": "Supplier" 98 | }, 99 | { 100 | "fieldname": "supplier", 101 | "fieldtype": "Link", 102 | "label": "Supplier", 103 | "options": "Supplier" 104 | }, 105 | { 106 | "depends_on": "eval:!doc.__islocal;", 107 | "fieldname": "address_contacts", 108 | "fieldtype": "Section Break", 109 | "label": "Address and Contact", 110 | "options": "fa fa-map-marker" 111 | }, 112 | { 113 | "fieldname": "address_html", 114 | "fieldtype": "HTML", 115 | "label": "Address HTML" 116 | }, 117 | { 118 | "fieldname": "column_break_9", 119 | "fieldtype": "Column Break" 120 | }, 121 | { 122 | "fieldname": "contact_html", 123 | "fieldtype": "HTML", 124 | "label": "Contact HTML" 125 | }, 126 | { 127 | "fieldname": "email_id", 128 | "fieldtype": "Data", 129 | "label": "Email Address", 130 | "options": "Email" 131 | }, 132 | { 133 | "fieldname": "subscription_id", 134 | "fieldtype": "Data", 135 | "label": "Subscription ID", 136 | "read_only": 1 137 | }, 138 | { 139 | "fieldname": "customer_id", 140 | "fieldtype": "Data", 141 | "label": "Customer ID", 142 | "read_only": 1 143 | }, 144 | { 145 | "fieldname": "razorpay_details_section", 146 | "fieldtype": "Section Break", 147 | "hidden": 1, 148 | "label": "Razorpay Details" 149 | }, 150 | { 151 | "fieldname": "column_break_21", 152 | "fieldtype": "Column Break" 153 | }, 154 | { 155 | "fieldname": "subscription_start", 156 | "fieldtype": "Date", 157 | "label": "Subscription Start " 158 | }, 159 | { 160 | "fieldname": "subscription_end", 161 | "fieldtype": "Date", 162 | "label": "Subscription End" 163 | }, 164 | { 165 | "fieldname": "subscription_status", 166 | "fieldtype": "Select", 167 | "label": "Subscription Status", 168 | "options": "\nActive\nHalted" 169 | } 170 | ], 171 | "image_field": "image", 172 | "links": [], 173 | "modified": "2021-07-11 14:27:26.368039", 174 | "modified_by": "Administrator", 175 | "module": "Non Profit", 176 | "name": "Member", 177 | "owner": "Administrator", 178 | "permissions": [ 179 | { 180 | "create": 1, 181 | "delete": 1, 182 | "email": 1, 183 | "export": 1, 184 | "print": 1, 185 | "read": 1, 186 | "report": 1, 187 | "role": "Non Profit Manager", 188 | "share": 1, 189 | "write": 1 190 | }, 191 | { 192 | "create": 1, 193 | "delete": 1, 194 | "email": 1, 195 | "export": 1, 196 | "print": 1, 197 | "read": 1, 198 | "report": 1, 199 | "role": "Non Profit Member", 200 | "share": 1, 201 | "write": 1 202 | } 203 | ], 204 | "quick_entry": 1, 205 | "restrict_to_domain": "Non Profit", 206 | "sort_field": "modified", 207 | "sort_order": "DESC", 208 | "title_field": "member_name", 209 | "track_changes": 1 210 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/member/member.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | import frappe 6 | from frappe import _ 7 | from frappe.contacts.address_and_contact import load_address_and_contact 8 | from frappe.model.document import Document 9 | from frappe.utils import cint, get_link_to_form 10 | 11 | from payments.utils import get_payment_gateway_controller 12 | 13 | from non_profit.non_profit.doctype.membership_type.membership_type import get_membership_type 14 | 15 | 16 | class Member(Document): 17 | def onload(self): 18 | """Load address and contacts in `__onload`""" 19 | load_address_and_contact(self) 20 | 21 | 22 | def validate(self): 23 | if self.email_id: 24 | self.validate_email_type(self.email_id) 25 | 26 | def validate_email_type(self, email): 27 | from frappe.utils import validate_email_address 28 | validate_email_address(email.strip(), True) 29 | 30 | def setup_subscription(self): 31 | non_profit_settings = frappe.get_doc('Non Profit Settings') 32 | if not non_profit_settings.enable_razorpay_for_memberships: 33 | frappe.throw(_('Please check Enable Razorpay for Memberships in {0} to setup subscription')).format( 34 | get_link_to_form('Non Profit Settings', 'Non Profit Settings')) 35 | 36 | controller = get_payment_gateway_controller("Razorpay") 37 | settings = controller.get_settings({}) 38 | 39 | plan_id = frappe.get_value("Membership Type", self.membership_type, "razorpay_plan_id") 40 | 41 | if not plan_id: 42 | frappe.throw(_("Please setup Razorpay Plan ID")) 43 | 44 | subscription_details = { 45 | "plan_id": plan_id, 46 | "billing_frequency": cint(non_profit_settings.billing_frequency), 47 | "customer_notify": 1 48 | } 49 | 50 | args = { 51 | 'subscription_details': subscription_details 52 | } 53 | 54 | subscription = controller.setup_subscription(settings, **args) 55 | 56 | return subscription 57 | 58 | @frappe.whitelist() 59 | def make_customer_and_link(self): 60 | if self.customer: 61 | frappe.msgprint(_("A customer is already linked to this Member")) 62 | 63 | customer = create_customer(frappe._dict({ 64 | 'fullname': self.member_name, 65 | 'email': self.email_id, 66 | 'phone': None 67 | })) 68 | 69 | self.customer = customer 70 | self.save() 71 | frappe.msgprint(_("Customer {0} has been created succesfully.").format(self.customer)) 72 | 73 | 74 | def get_or_create_member(user_details): 75 | member_list = frappe.get_all("Member", filters={'email': user_details.email, 'membership_type': user_details.plan_id}) 76 | if member_list and member_list[0]: 77 | return member_list[0]['name'] 78 | else: 79 | return create_member(user_details) 80 | 81 | def create_member(user_details): 82 | user_details = frappe._dict(user_details) 83 | member = frappe.new_doc("Member") 84 | member.update({ 85 | "member_name": user_details.fullname, 86 | "email_id": user_details.email, 87 | "pan_number": user_details.pan or None, 88 | "membership_type": user_details.plan_id, 89 | "customer_id": user_details.customer_id or None, 90 | "subscription_id": user_details.subscription_id or None, 91 | "subscription_status": user_details.subscription_status or "" 92 | }) 93 | 94 | member.insert(ignore_permissions=True) 95 | member.customer = create_customer(user_details, member.name) 96 | member.save(ignore_permissions=True) 97 | 98 | return member 99 | 100 | def create_customer(user_details, member=None): 101 | customer = frappe.new_doc("Customer") 102 | customer.customer_name = user_details.fullname 103 | customer.customer_type = "Individual" 104 | customer.customer_group = frappe.db.get_single_value("Selling Settings", "customer_group") 105 | customer.territory = frappe.db.get_single_value("Selling Settings", "territory") 106 | customer.flags.ignore_mandatory = True 107 | customer.insert(ignore_permissions=True) 108 | 109 | try: 110 | frappe.db.savepoint("contact_creation") 111 | contact = frappe.new_doc("Contact") 112 | contact.first_name = user_details.fullname 113 | if user_details.mobile: 114 | contact.add_phone(user_details.mobile, is_primary_phone=1, is_primary_mobile_no=1) 115 | if user_details.email: 116 | contact.add_email(user_details.email, is_primary=1) 117 | contact.insert(ignore_permissions=True) 118 | 119 | contact.append("links", { 120 | "link_doctype": "Customer", 121 | "link_name": customer.name 122 | }) 123 | 124 | if member: 125 | contact.append("links", { 126 | "link_doctype": "Member", 127 | "link_name": member 128 | }) 129 | 130 | contact.save(ignore_permissions=True) 131 | 132 | except frappe.DuplicateEntryError: 133 | return customer.name 134 | 135 | except Exception as e: 136 | frappe.db.rollback(save_point="contact_creation") 137 | frappe.log_error(frappe.get_traceback(), _("Contact Creation Failed")) 138 | pass 139 | 140 | return customer.name 141 | 142 | @frappe.whitelist(allow_guest=True) 143 | def create_member_subscription_order(user_details): 144 | """Create Member subscription and order for payment 145 | 146 | Args: 147 | user_details (TYPE): Description 148 | 149 | Returns: 150 | Dictionary: Dictionary with subscription details 151 | { 152 | 'subscription_details': { 153 | 'plan_id': 'plan_EXwyxDYDCj3X4v', 154 | 'billing_frequency': 24, 155 | 'customer_notify': 1 156 | }, 157 | 'subscription_id': 'sub_EZycCvXFvqnC6p' 158 | } 159 | """ 160 | 161 | user_details = frappe._dict(user_details) 162 | member = get_or_create_member(user_details) 163 | 164 | subscription = member.setup_subscription() 165 | 166 | member.subscription_id = subscription.get('subscription_id') 167 | member.save(ignore_permissions=True) 168 | 169 | return subscription 170 | 171 | @frappe.whitelist() 172 | def register_member(fullname, email, rzpay_plan_id, subscription_id, pan=None, mobile=None): 173 | plan = get_membership_type(rzpay_plan_id) 174 | if not plan: 175 | raise frappe.DoesNotExistError 176 | 177 | member = frappe.db.exists("Member", {'email': email, 'subscription_id': subscription_id }) 178 | if member: 179 | return member 180 | else: 181 | member = create_member(dict( 182 | fullname=fullname, 183 | email=email, 184 | plan_id=plan, 185 | subscription_id=subscription_id, 186 | pan=pan, 187 | mobile=mobile 188 | )) 189 | 190 | return member.name 191 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/member/member_dashboard.py: -------------------------------------------------------------------------------- 1 | from frappe import _ 2 | 3 | 4 | def get_data(): 5 | return { 6 | 'heatmap': True, 7 | 'heatmap_message': _('Member Activity'), 8 | 'fieldname': 'member', 9 | 'non_standard_fieldnames': { 10 | 'Bank Account': 'party' 11 | }, 12 | 'transactions': [ 13 | { 14 | 'label': _('Membership Details'), 15 | 'items': ['Membership'] 16 | }, 17 | { 18 | 'label': _('Fee'), 19 | 'items': ['Bank Account'] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/member/member_list.js: -------------------------------------------------------------------------------- 1 | frappe.listview_settings['Member'] = { 2 | add_fields: ["member_name", "membership_type", "image"], 3 | }; 4 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/member/test_member.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // rename this file from _test_[name] to test_[name] to activate 3 | // and remove above this line 4 | 5 | QUnit.test("test: Member", function (assert) { 6 | let done = assert.async(); 7 | 8 | // number of asserts 9 | assert.expect(2); 10 | 11 | frappe.run_serially([ 12 | // insert a new Member 13 | () => frappe.tests.make('Member', [ 14 | // values to be set 15 | {member_name: 'Test Member'}, 16 | {membership_type: 'Gold'}, 17 | {email: 'test@example.com'} 18 | ]), 19 | () => { 20 | assert.equal(cur_frm.doc.membership_type, 'Gold'); 21 | assert.equal(cur_frm.doc.email, 'test@example.com'); 22 | }, 23 | () => done() 24 | ]); 25 | 26 | }); 27 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/member/test_member.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestMember(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/membership/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership/membership.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Membership', { 5 | setup: function(frm) { 6 | frappe.db.get_single_value("Non Profit Settings", "enable_razorpay_for_memberships").then(val => { 7 | if (val) frm.set_df_property("razorpay_details_section", "hidden", false); 8 | }) 9 | }, 10 | 11 | refresh: function(frm) { 12 | if (frm.doc.__islocal) 13 | return; 14 | 15 | !frm.doc.invoice && frm.add_custom_button("Generate Invoice", () => { 16 | frm.call({ 17 | doc: frm.doc, 18 | method: "generate_invoice", 19 | args: {save: true}, 20 | freeze: true, 21 | freeze_message: __("Creating Membership Invoice"), 22 | callback: function(r) { 23 | if (r.invoice) 24 | frm.reload_doc(); 25 | } 26 | }); 27 | }); 28 | 29 | frappe.db.get_single_value("Non Profit Settings", "send_email").then(val => { 30 | if (val) frm.add_custom_button("Send Acknowledgement", () => { 31 | frm.call("send_acknowlement").then(() => { 32 | frm.reload_doc(); 33 | }); 34 | }); 35 | }) 36 | }, 37 | 38 | onload: function(frm) { 39 | frm.add_fetch("membership_type", "amount", "amount"); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership/membership.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "NPO-MSH-.YYYY.-.#####", 4 | "creation": "2017-09-11 11:39:18.492184", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "member", 10 | "member_name", 11 | "membership_type", 12 | "column_break_3", 13 | "company", 14 | "membership_status", 15 | "membership_validity_section", 16 | "from_date", 17 | "to_date", 18 | "column_break_8", 19 | "member_since_date", 20 | "payment_details", 21 | "paid", 22 | "currency", 23 | "amount", 24 | "column_break_16", 25 | "invoice", 26 | "razorpay_details_section", 27 | "subscription_id", 28 | "column_break_19", 29 | "payment_id" 30 | ], 31 | "fields": [ 32 | { 33 | "fieldname": "member", 34 | "fieldtype": "Link", 35 | "label": "Member", 36 | "options": "Member" 37 | }, 38 | { 39 | "fieldname": "membership_type", 40 | "fieldtype": "Link", 41 | "in_list_view": 1, 42 | "label": "Membership Type", 43 | "options": "Membership Type", 44 | "reqd": 1 45 | }, 46 | { 47 | "fieldname": "column_break_3", 48 | "fieldtype": "Column Break" 49 | }, 50 | { 51 | "fieldname": "membership_status", 52 | "fieldtype": "Select", 53 | "in_list_view": 1, 54 | "in_standard_filter": 1, 55 | "label": "Membership Status", 56 | "options": "New\nCurrent\nExpired\nPending\nCancelled" 57 | }, 58 | { 59 | "fieldname": "membership_validity_section", 60 | "fieldtype": "Section Break", 61 | "label": "Validity" 62 | }, 63 | { 64 | "fieldname": "from_date", 65 | "fieldtype": "Date", 66 | "in_list_view": 1, 67 | "label": "From", 68 | "reqd": 1 69 | }, 70 | { 71 | "fieldname": "to_date", 72 | "fieldtype": "Date", 73 | "in_list_view": 1, 74 | "label": "To", 75 | "reqd": 1 76 | }, 77 | { 78 | "fieldname": "column_break_8", 79 | "fieldtype": "Column Break" 80 | }, 81 | { 82 | "fieldname": "member_since_date", 83 | "fieldtype": "Date", 84 | "label": "Member Since" 85 | }, 86 | { 87 | "fieldname": "payment_details", 88 | "fieldtype": "Section Break", 89 | "label": "Payment Details" 90 | }, 91 | { 92 | "default": "0", 93 | "fieldname": "paid", 94 | "fieldtype": "Check", 95 | "label": "Paid" 96 | }, 97 | { 98 | "fieldname": "currency", 99 | "fieldtype": "Link", 100 | "label": "Currency", 101 | "options": "Currency" 102 | }, 103 | { 104 | "fieldname": "amount", 105 | "fieldtype": "Float", 106 | "label": "Amount" 107 | }, 108 | { 109 | "fieldname": "razorpay_details_section", 110 | "fieldtype": "Section Break", 111 | "label": "Razorpay Details" 112 | }, 113 | { 114 | "fieldname": "subscription_id", 115 | "fieldtype": "Data", 116 | "label": "Subscription ID" 117 | }, 118 | { 119 | "fieldname": "payment_id", 120 | "fieldtype": "Data", 121 | "label": "Payment ID" 122 | }, 123 | { 124 | "fieldname": "invoice", 125 | "fieldtype": "Link", 126 | "label": "Invoice", 127 | "options": "Sales Invoice" 128 | }, 129 | { 130 | "fetch_from": "member.member_name", 131 | "fieldname": "member_name", 132 | "fieldtype": "Data", 133 | "label": "Member Name", 134 | "read_only": 1 135 | }, 136 | { 137 | "fieldname": "company", 138 | "fieldtype": "Link", 139 | "label": "Company", 140 | "options": "Company", 141 | "reqd": 1 142 | }, 143 | { 144 | "fieldname": "column_break_19", 145 | "fieldtype": "Column Break" 146 | }, 147 | { 148 | "fieldname": "column_break_16", 149 | "fieldtype": "Column Break" 150 | } 151 | ], 152 | "index_web_pages_for_search": 1, 153 | "links": [], 154 | "modified": "2022-03-16 17:37:28.672916", 155 | "modified_by": "Administrator", 156 | "module": "Non Profit", 157 | "name": "Membership", 158 | "owner": "Administrator", 159 | "permissions": [ 160 | { 161 | "create": 1, 162 | "delete": 1, 163 | "email": 1, 164 | "export": 1, 165 | "print": 1, 166 | "read": 1, 167 | "report": 1, 168 | "role": "Non Profit Manager", 169 | "share": 1, 170 | "write": 1 171 | }, 172 | { 173 | "create": 1, 174 | "delete": 1, 175 | "email": 1, 176 | "export": 1, 177 | "print": 1, 178 | "read": 1, 179 | "report": 1, 180 | "role": "Non Profit Member", 181 | "share": 1, 182 | "write": 1 183 | } 184 | ], 185 | "restrict_to_domain": "Non Profit", 186 | "search_fields": "member, member_name", 187 | "sort_field": "modified", 188 | "sort_order": "DESC", 189 | "title_field": "member_name", 190 | "track_changes": 1 191 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership/membership_list.js: -------------------------------------------------------------------------------- 1 | frappe.listview_settings['Membership'] = { 2 | get_indicator: function(doc) { 3 | if (doc.membership_status == 'New') { 4 | return [__('New'), 'blue', 'membership_status,=,New']; 5 | } else if (doc.membership_status === 'Current') { 6 | return [__('Current'), 'green', 'membership_status,=,Current']; 7 | } else if (doc.membership_status === 'Pending') { 8 | return [__('Pending'), 'yellow', 'membership_status,=,Pending']; 9 | } else if (doc.membership_status === 'Expired') { 10 | return [__('Expired'), 'grey', 'membership_status,=,Expired']; 11 | } else { 12 | return [__('Cancelled'), 'red', 'membership_status,=,Cancelled']; 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership/test_membership.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import frappe 5 | from frappe.utils import add_months, nowdate 6 | from frappe.tests.utils import FrappeTestCase 7 | 8 | import erpnext 9 | from non_profit.non_profit.doctype.member.member import create_member 10 | from non_profit.non_profit.doctype.membership.membership import update_halted_razorpay_subscription 11 | 12 | 13 | class TestMembership(FrappeTestCase): 14 | def setUp(self): 15 | frappe.db.delete('Customer') 16 | plan = setup_membership() 17 | # make test member 18 | self.member_doc = create_member( 19 | frappe._dict({ 20 | "fullname": "_Test_Member", 21 | "email": "_test_member_erpnext@example.com", 22 | "plan_id": plan.name, 23 | "subscription_id": "sub_DEX6xcJ1HSW4CR", 24 | "customer_id": "cust_C0WlbKhp3aLA7W", 25 | "subscription_status": "Active", 26 | }) 27 | ) 28 | self.member_doc.make_customer_and_link() 29 | self.member = self.member_doc.name 30 | 31 | def test_auto_generate_invoice_and_payment_entry(self): 32 | entry = make_membership(self.member) 33 | 34 | # Naive test to see if at all invoice was generated and attached to member 35 | # In any case if details were missing, the invoicing would throw an error 36 | invoice = entry.generate_invoice(save=True) 37 | self.assertEqual(invoice.name, entry.invoice) 38 | 39 | def test_renew_within_30_days(self): 40 | # create a membership for two months 41 | # Should work fine 42 | make_membership(self.member, { "from_date": nowdate() }) 43 | make_membership(self.member, { "from_date": add_months(nowdate(), 1) }) 44 | 45 | from frappe.utils.user import add_role 46 | add_role("test@example.com", "Non Profit Manager") 47 | frappe.set_user("test@example.com") 48 | 49 | # create next membership with expiry not within 30 days 50 | self.assertRaises(frappe.ValidationError, make_membership, self.member, { 51 | "from_date": add_months(nowdate(), 2), 52 | }) 53 | 54 | frappe.set_user("Administrator") 55 | # create the same membership but as administrator 56 | make_membership(self.member, { 57 | "from_date": add_months(nowdate(), 2), 58 | "to_date": add_months(nowdate(), 3), 59 | }) 60 | 61 | def test_halted_memberships(self): 62 | make_membership(self.member, { 63 | "from_date": add_months(nowdate(), 2), 64 | "to_date": add_months(nowdate(), 3) 65 | }) 66 | 67 | self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Active") 68 | payload = get_subscription_payload() 69 | update_halted_razorpay_subscription(data=payload) 70 | self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Halted") 71 | 72 | def tearDown(self): 73 | frappe.db.rollback() 74 | 75 | def set_config(key, value): 76 | frappe.db.set_value("Non Profit Settings", None, key, value) 77 | 78 | def make_membership(member, payload={}): 79 | data = { 80 | "doctype": "Membership", 81 | "member": member, 82 | "membership_status": "Current", 83 | "membership_type": "_rzpy_test_milythm", 84 | "currency": "INR", 85 | "paid": 1, 86 | "from_date": nowdate(), 87 | "amount": 100 88 | } 89 | data.update(payload) 90 | membership = frappe.get_doc(data) 91 | membership.insert(ignore_permissions=True, ignore_if_duplicate=True) 92 | return membership 93 | 94 | def create_item(item_code): 95 | if not frappe.db.exists("Item", item_code): 96 | item = frappe.new_doc("Item") 97 | item.item_code = item_code 98 | item.item_name = item_code 99 | item.stock_uom = "Nos" 100 | item.description = item_code 101 | item.item_group = "All Item Groups" 102 | item.is_stock_item = 0 103 | item.save() 104 | else: 105 | item = frappe.get_doc("Item", item_code) 106 | return item 107 | 108 | def setup_membership(): 109 | # Get default company 110 | company = frappe.get_doc("Company", erpnext.get_default_company()) 111 | 112 | # update non profit settings 113 | settings = frappe.get_doc("Non Profit Settings") 114 | # Enable razorpay 115 | settings.enable_razorpay_for_memberships = 1 116 | settings.billing_cycle = "Monthly" 117 | settings.billing_frequency = 24 118 | # Enable invoicing 119 | settings.allow_invoicing = 1 120 | settings.automate_membership_payment_entries = 1 121 | settings.company = company.name 122 | settings.donation_company = company.name 123 | settings.membership_payment_account = company.default_cash_account 124 | settings.membership_debit_account = company.default_receivable_account 125 | settings.flags.ignore_mandatory = True 126 | settings.save() 127 | 128 | # make test plan 129 | if not frappe.db.exists("Membership Type", "_rzpy_test_milythm"): 130 | plan = frappe.new_doc("Membership Type") 131 | plan.membership_type = "_rzpy_test_milythm" 132 | plan.amount = 100 133 | plan.razorpay_plan_id = "_rzpy_test_milythm" 134 | plan.linked_item = create_item("_Test Item for Non Profit Membership").name 135 | plan.insert() 136 | else: 137 | plan = frappe.get_doc("Membership Type", "_rzpy_test_milythm") 138 | 139 | return plan 140 | 141 | def get_subscription_payload(): 142 | return { 143 | "entity": "event", 144 | "account_id": "acc_BFQ7uQEaa7j2z7", 145 | "event": "subscription.halted", 146 | "contains": [ 147 | "subscription" 148 | ], 149 | "payload": { 150 | "subscription": { 151 | "entity": { 152 | "id": "sub_DEX6xcJ1HSW4CR", 153 | "entity": "subscription", 154 | "plan_id": "_rzpy_test_milythm", 155 | "customer_id": "cust_C0WlbKhp3aLA7W", 156 | "status": "halted", 157 | "notes": { 158 | "Important": "Notes for Internal Reference" 159 | }, 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership_type/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/membership_type/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership_type/membership_type.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Membership Type', { 5 | refresh: function (frm) { 6 | frappe.db.get_single_value('Non Profit Settings', 'enable_razorpay_for_memberships').then(val => { 7 | if (val) frm.set_df_property('razorpay_plan_id', 'hidden', false); 8 | }); 9 | 10 | frappe.db.get_single_value('Non Profit Settings', 'allow_invoicing').then(val => { 11 | if (val) frm.set_df_property('linked_item', 'hidden', false); 12 | }); 13 | 14 | frm.set_query('linked_item', () => { 15 | return { 16 | filters: { 17 | is_stock_item: 0 18 | } 19 | }; 20 | }); 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership_type/membership_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "field:membership_type", 4 | "creation": "2017-09-18 12:56:56.343999", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "membership_type", 10 | "amount", 11 | "razorpay_plan_id", 12 | "linked_item" 13 | ], 14 | "fields": [ 15 | { 16 | "fieldname": "membership_type", 17 | "fieldtype": "Data", 18 | "in_list_view": 1, 19 | "in_standard_filter": 1, 20 | "label": "Membership Type", 21 | "reqd": 1, 22 | "unique": 1 23 | }, 24 | { 25 | "fieldname": "amount", 26 | "fieldtype": "Float", 27 | "in_list_view": 1, 28 | "label": "Amount", 29 | "reqd": 1 30 | }, 31 | { 32 | "fieldname": "razorpay_plan_id", 33 | "fieldtype": "Data", 34 | "hidden": 1, 35 | "label": "Razorpay Plan ID", 36 | "unique": 1 37 | }, 38 | { 39 | "fieldname": "linked_item", 40 | "fieldtype": "Link", 41 | "label": "Linked Item", 42 | "options": "Item", 43 | "unique": 1 44 | } 45 | ], 46 | "links": [], 47 | "modified": "2020-08-05 15:21:43.595745", 48 | "modified_by": "Administrator", 49 | "module": "Non Profit", 50 | "name": "Membership Type", 51 | "owner": "Administrator", 52 | "permissions": [ 53 | { 54 | "create": 1, 55 | "delete": 1, 56 | "email": 1, 57 | "export": 1, 58 | "print": 1, 59 | "read": 1, 60 | "report": 1, 61 | "role": "Non Profit Manager", 62 | "share": 1, 63 | "write": 1 64 | } 65 | ], 66 | "quick_entry": 1, 67 | "restrict_to_domain": "Non Profit", 68 | "sort_field": "modified", 69 | "sort_order": "DESC", 70 | "track_changes": 1 71 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership_type/membership_type.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | import frappe 6 | from frappe import _ 7 | from frappe.model.document import Document 8 | 9 | 10 | class MembershipType(Document): 11 | def validate(self): 12 | if self.linked_item: 13 | is_stock_item = frappe.db.get_value("Item", self.linked_item, "is_stock_item") 14 | if is_stock_item: 15 | frappe.throw(_("The Linked Item should be a service item")) 16 | 17 | def get_membership_type(razorpay_id): 18 | return frappe.db.exists("Membership Type", {"razorpay_plan_id": razorpay_id}) 19 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership_type/test_membership_type.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // rename this file from _test_[name] to test_[name] to activate 3 | // and remove above this line 4 | 5 | QUnit.test("test: Membership Type", function (assert) { 6 | let done = assert.async(); 7 | 8 | // number of asserts 9 | assert.expect(2); 10 | 11 | frappe.run_serially([ 12 | // insert a new Member 13 | () => frappe.tests.make('Membership Type', [ 14 | // values to be set 15 | {membership_type: 'Gold'}, 16 | {amount:50000} 17 | ]), 18 | () => { 19 | assert.equal(cur_frm.doc.membership_type, 'Gold'); 20 | assert.equal(cur_frm.doc.amount, '50000'); 21 | }, 22 | () => done() 23 | ]); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/membership_type/test_membership_type.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestMembershipType(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/non_profit_settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/non_profit_settings/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/non_profit_settings/non_profit_settings.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on("Non Profit Settings", { 5 | refresh: function(frm) { 6 | frm.set_query("inv_print_format", function() { 7 | return { 8 | filters: { 9 | "doc_type": "Sales Invoice" 10 | } 11 | }; 12 | }); 13 | 14 | frm.set_query("membership_print_format", function() { 15 | return { 16 | filters: { 17 | "doc_type": "Membership" 18 | } 19 | }; 20 | }); 21 | 22 | frm.set_query("membership_debit_account", function() { 23 | return { 24 | filters: { 25 | "account_type": "Receivable", 26 | "is_group": 0, 27 | "company": frm.doc.company 28 | } 29 | }; 30 | }); 31 | 32 | frm.set_query("donation_debit_account", function() { 33 | return { 34 | filters: { 35 | "account_type": "Receivable", 36 | "is_group": 0, 37 | "company": frm.doc.donation_company 38 | } 39 | }; 40 | }); 41 | 42 | frm.set_query("membership_payment_account", function () { 43 | var account_types = ["Bank", "Cash"]; 44 | return { 45 | filters: { 46 | "account_type": ["in", account_types], 47 | "is_group": 0, 48 | "company": frm.doc.company 49 | } 50 | }; 51 | }); 52 | 53 | frm.set_query("donation_payment_account", function () { 54 | var account_types = ["Bank", "Cash"]; 55 | return { 56 | filters: { 57 | "account_type": ["in", account_types], 58 | "is_group": 0, 59 | "company": frm.doc.donation_company 60 | } 61 | }; 62 | }); 63 | 64 | let docs_url = "https://docs.erpnext.com/docs/user/manual/en/non_profit/membership"; 65 | 66 | frm.set_intro(__("You can learn more about memberships in the manual. ") + `${__('ERPNext Docs')}`, true); 67 | frm.trigger("setup_buttons_for_membership"); 68 | frm.trigger("setup_buttons_for_donation"); 69 | }, 70 | 71 | setup_buttons_for_membership: function(frm) { 72 | let label; 73 | 74 | if (frm.doc.membership_webhook_secret) { 75 | 76 | frm.add_custom_button(__("Copy Webhook URL"), () => { 77 | frappe.utils.copy_to_clipboard(`https://${frappe.boot.sitename}/api/method/non_profit.non_profit.doctype.membership.membership.trigger_razorpay_subscription`); 78 | }, __("Memberships")); 79 | 80 | frm.add_custom_button(__("Revoke Key"), () => { 81 | frm.call("revoke_key", { 82 | key: "membership_webhook_secret" 83 | }).then(() => { 84 | frm.refresh(); 85 | }); 86 | }, __("Memberships")); 87 | 88 | label = __("Regenerate Webhook Secret"); 89 | 90 | } else { 91 | label = __("Generate Webhook Secret"); 92 | } 93 | 94 | frm.add_custom_button(label, () => { 95 | frm.call("generate_webhook_secret", { 96 | field: "membership_webhook_secret" 97 | }).then(() => { 98 | frm.refresh(); 99 | }); 100 | }, __("Memberships")); 101 | }, 102 | 103 | setup_buttons_for_donation: function(frm) { 104 | let label; 105 | 106 | if (frm.doc.donation_webhook_secret) { 107 | label = __("Regenerate Webhook Secret"); 108 | 109 | frm.add_custom_button(__("Copy Webhook URL"), () => { 110 | frappe.utils.copy_to_clipboard(`https://${frappe.boot.sitename}/api/method/non_profit.non_profit.doctype.donation.donation.capture_razorpay_donations`); 111 | }, __("Donations")); 112 | 113 | frm.add_custom_button(__("Revoke Key"), () => { 114 | frm.call("revoke_key", { 115 | key: "donation_webhook_secret" 116 | }).then(() => { 117 | frm.refresh(); 118 | }); 119 | }, __("Donations")); 120 | 121 | } else { 122 | label = __("Generate Webhook Secret"); 123 | } 124 | 125 | frm.add_custom_button(label, () => { 126 | frm.call("generate_webhook_secret", { 127 | field: "donation_webhook_secret" 128 | }).then(() => { 129 | frm.refresh(); 130 | }); 131 | }, __("Donations")); 132 | } 133 | }); 134 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/non_profit_settings/non_profit_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2020-03-29 12:57:03.005120", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "enable_razorpay_for_memberships", 9 | "razorpay_settings_section", 10 | "billing_cycle", 11 | "billing_frequency", 12 | "membership_webhook_secret", 13 | "column_break_6", 14 | "allow_invoicing", 15 | "automate_membership_invoicing", 16 | "automate_membership_payment_entries", 17 | "company", 18 | "membership_debit_account", 19 | "membership_payment_account", 20 | "column_break_9", 21 | "send_email", 22 | "send_invoice", 23 | "membership_print_format", 24 | "inv_print_format", 25 | "email_template", 26 | "donation_settings_section", 27 | "donation_company", 28 | "default_donor_type", 29 | "donation_webhook_secret", 30 | "column_break_22", 31 | "automate_donation_payment_entries", 32 | "donation_debit_account", 33 | "donation_payment_account", 34 | "section_break_27", 35 | "creation_user" 36 | ], 37 | "fields": [ 38 | { 39 | "fieldname": "billing_cycle", 40 | "fieldtype": "Select", 41 | "label": "Billing Cycle", 42 | "options": "Monthly\nYearly" 43 | }, 44 | { 45 | "depends_on": "eval:doc.enable_razorpay_for_memberships", 46 | "fieldname": "razorpay_settings_section", 47 | "fieldtype": "Section Break", 48 | "label": "RazorPay Settings for Memberships" 49 | }, 50 | { 51 | "description": "The number of billing cycles for which the customer should be charged. For example, if a customer is buying a 1-year membership that should be billed on a monthly basis, this value should be 12.", 52 | "fieldname": "billing_frequency", 53 | "fieldtype": "Int", 54 | "label": "Billing Frequency" 55 | }, 56 | { 57 | "fieldname": "column_break_6", 58 | "fieldtype": "Section Break", 59 | "label": "Membership Invoicing" 60 | }, 61 | { 62 | "fieldname": "column_break_9", 63 | "fieldtype": "Column Break" 64 | }, 65 | { 66 | "description": "This company will be set for the Memberships created via webhook.", 67 | "fieldname": "company", 68 | "fieldtype": "Link", 69 | "in_list_view": 1, 70 | "label": "Company", 71 | "options": "Company", 72 | "reqd": 1 73 | }, 74 | { 75 | "default": "0", 76 | "depends_on": "eval:doc.allow_invoicing && doc.send_email", 77 | "fieldname": "send_invoice", 78 | "fieldtype": "Check", 79 | "label": "Send Invoice with Email" 80 | }, 81 | { 82 | "default": "0", 83 | "fieldname": "send_email", 84 | "fieldtype": "Check", 85 | "label": "Send Membership Acknowledgement" 86 | }, 87 | { 88 | "depends_on": "eval: doc.send_invoice", 89 | "fieldname": "inv_print_format", 90 | "fieldtype": "Link", 91 | "label": "Invoice Print Format", 92 | "mandatory_depends_on": "eval: doc.send_invoice", 93 | "options": "Print Format" 94 | }, 95 | { 96 | "depends_on": "eval:doc.send_email", 97 | "fieldname": "membership_print_format", 98 | "fieldtype": "Link", 99 | "label": "Membership Print Format", 100 | "options": "Print Format" 101 | }, 102 | { 103 | "depends_on": "eval:doc.send_email", 104 | "fieldname": "email_template", 105 | "fieldtype": "Link", 106 | "label": "Email Template", 107 | "mandatory_depends_on": "eval:doc.send_email", 108 | "options": "Email Template" 109 | }, 110 | { 111 | "default": "0", 112 | "fieldname": "allow_invoicing", 113 | "fieldtype": "Check", 114 | "label": "Allow Invoicing for Memberships", 115 | "mandatory_depends_on": "eval:doc.send_invoice || doc.make_payment_entry" 116 | }, 117 | { 118 | "default": "0", 119 | "depends_on": "eval:doc.allow_invoicing", 120 | "description": "Automatically create an invoice when payment is authorized from a web form entry", 121 | "fieldname": "automate_membership_invoicing", 122 | "fieldtype": "Check", 123 | "label": "Automate Invoicing for Web Forms" 124 | }, 125 | { 126 | "default": "0", 127 | "depends_on": "eval:doc.allow_invoicing", 128 | "description": "Auto creates Payment Entry for Sales Invoices created for Membership from web forms.", 129 | "fieldname": "automate_membership_payment_entries", 130 | "fieldtype": "Check", 131 | "label": "Automate Payment Entry Creation" 132 | }, 133 | { 134 | "default": "0", 135 | "fieldname": "enable_razorpay_for_memberships", 136 | "fieldtype": "Check", 137 | "label": "Enable RazorPay For Memberships" 138 | }, 139 | { 140 | "depends_on": "eval:doc.automate_membership_payment_entries", 141 | "description": "Account for accepting membership payments", 142 | "fieldname": "membership_payment_account", 143 | "fieldtype": "Link", 144 | "label": "Membership Payment To", 145 | "mandatory_depends_on": "eval:doc.automate_membership_payment_entries", 146 | "options": "Account" 147 | }, 148 | { 149 | "fieldname": "membership_webhook_secret", 150 | "fieldtype": "Password", 151 | "label": "Membership Webhook Secret", 152 | "read_only": 1 153 | }, 154 | { 155 | "fieldname": "donation_webhook_secret", 156 | "fieldtype": "Password", 157 | "label": "Donation Webhook Secret", 158 | "read_only": 1 159 | }, 160 | { 161 | "depends_on": "automate_donation_payment_entries", 162 | "description": "Account for accepting donation payments", 163 | "fieldname": "donation_payment_account", 164 | "fieldtype": "Link", 165 | "label": "Donation Payment To", 166 | "mandatory_depends_on": "automate_donation_payment_entries", 167 | "options": "Account" 168 | }, 169 | { 170 | "default": "0", 171 | "description": "Auto creates Payment Entry for Donations created from web forms.", 172 | "fieldname": "automate_donation_payment_entries", 173 | "fieldtype": "Check", 174 | "label": "Automate Donation Payment Entries" 175 | }, 176 | { 177 | "depends_on": "eval:doc.allow_invoicing", 178 | "fieldname": "membership_debit_account", 179 | "fieldtype": "Link", 180 | "label": "Debit Account", 181 | "mandatory_depends_on": "eval:doc.allow_invoicing", 182 | "options": "Account" 183 | }, 184 | { 185 | "depends_on": "automate_donation_payment_entries", 186 | "fieldname": "donation_debit_account", 187 | "fieldtype": "Link", 188 | "label": "Debit Account", 189 | "mandatory_depends_on": "automate_donation_payment_entries", 190 | "options": "Account" 191 | }, 192 | { 193 | "description": "This company will be set for the Donations created via webhook.", 194 | "fieldname": "donation_company", 195 | "fieldtype": "Link", 196 | "in_list_view": 1, 197 | "label": "Company", 198 | "options": "Company", 199 | "reqd": 1 200 | }, 201 | { 202 | "fieldname": "donation_settings_section", 203 | "fieldtype": "Section Break", 204 | "label": "Donation Settings" 205 | }, 206 | { 207 | "fieldname": "column_break_22", 208 | "fieldtype": "Column Break" 209 | }, 210 | { 211 | "description": "This Donor Type will be set for the Donor created via Donation web form entry.", 212 | "fieldname": "default_donor_type", 213 | "fieldtype": "Link", 214 | "in_list_view": 1, 215 | "label": "Default Donor Type", 216 | "options": "Donor Type", 217 | "reqd": 1 218 | }, 219 | { 220 | "fieldname": "section_break_27", 221 | "fieldtype": "Section Break" 222 | }, 223 | { 224 | "description": "The user that will be used to create Donations, Memberships, Invoices, and Payment Entries. This user should have the relevant permissions.", 225 | "fieldname": "creation_user", 226 | "fieldtype": "Link", 227 | "label": "Creation User", 228 | "options": "User", 229 | "reqd": 1 230 | } 231 | ], 232 | "index_web_pages_for_search": 1, 233 | "issingle": 1, 234 | "links": [], 235 | "modified": "2021-03-11 10:43:38.124240", 236 | "modified_by": "Administrator", 237 | "module": "Non Profit", 238 | "name": "Non Profit Settings", 239 | "owner": "Administrator", 240 | "permissions": [ 241 | { 242 | "create": 1, 243 | "delete": 1, 244 | "email": 1, 245 | "print": 1, 246 | "read": 1, 247 | "role": "System Manager", 248 | "share": 1, 249 | "write": 1 250 | }, 251 | { 252 | "create": 1, 253 | "delete": 1, 254 | "email": 1, 255 | "print": 1, 256 | "read": 1, 257 | "role": "Non Profit Manager", 258 | "share": 1, 259 | "write": 1 260 | }, 261 | { 262 | "email": 1, 263 | "print": 1, 264 | "read": 1, 265 | "role": "Non Profit Member", 266 | "share": 1 267 | } 268 | ], 269 | "quick_entry": 1, 270 | "sort_field": "modified", 271 | "sort_order": "DESC", 272 | "track_changes": 1 273 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/non_profit_settings/non_profit_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | import frappe 6 | from frappe import _ 7 | from frappe.model.document import Document 8 | 9 | from payments.utils import get_payment_gateway_controller 10 | 11 | class NonProfitSettings(Document): 12 | @frappe.whitelist() 13 | def generate_webhook_secret(self, field="membership_webhook_secret"): 14 | key = frappe.generate_hash(length=20) 15 | self.set(field, key) 16 | self.save() 17 | 18 | secret_for = "Membership" if field == "membership_webhook_secret" else "Donation" 19 | 20 | frappe.msgprint( 21 | _("Here is your webhook secret for {0} API, this will be shown to you only once.").format(secret_for) + "

" + key, 22 | _("Webhook Secret") 23 | ) 24 | 25 | @frappe.whitelist() 26 | def revoke_key(self, key): 27 | self.set(key, None) 28 | self.save() 29 | 30 | def get_webhook_secret(self, endpoint="Membership"): 31 | fieldname = "membership_webhook_secret" if endpoint == "Membership" else "donation_webhook_secret" 32 | return self.get_password(fieldname=fieldname, raise_exception=False) 33 | 34 | @frappe.whitelist() 35 | def get_plans_for_membership(*args, **kwargs): 36 | controller = get_payment_gateway_controller("Razorpay") 37 | plans = controller.get_plans() 38 | return [plan.get("item") for plan in plans.get("items")] 39 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/non_profit_settings/test_non_profit_settings.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | # import frappe 5 | import unittest 6 | 7 | 8 | class TestNonProfitSettings(unittest.TestCase): 9 | pass 10 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/tax_exemption_80g_certificate/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/tax_exemption_80g_certificate/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Tax Exemption 80G Certificate', { 5 | refresh: function(frm) { 6 | if (frm.doc.donor) { 7 | frm.set_query('donation', function() { 8 | return { 9 | filters: { 10 | docstatus: 1, 11 | donor: frm.doc.donor 12 | } 13 | }; 14 | }); 15 | } 16 | }, 17 | 18 | recipient: function(frm) { 19 | if (frm.doc.recipient === 'Donor') { 20 | frm.set_value({ 21 | 'member': '', 22 | 'member_name': '', 23 | 'member_email': '', 24 | 'member_pan_number': '', 25 | 'fiscal_year': '', 26 | 'total': 0, 27 | 'payments': [] 28 | }); 29 | } else { 30 | frm.set_value({ 31 | 'donor': '', 32 | 'donor_name': '', 33 | 'donor_email': '', 34 | 'donor_pan_number': '', 35 | 'donation': '', 36 | 'date_of_donation': '', 37 | 'amount': 0, 38 | 'mode_of_payment': '', 39 | 'payment_id': '' 40 | }); 41 | } 42 | }, 43 | 44 | get_payments: function(frm) { 45 | frm.call({ 46 | doc: frm.doc, 47 | method: 'get_payments', 48 | freeze: true 49 | }); 50 | }, 51 | 52 | company: function(frm) { 53 | if ((frm.doc.member || frm.doc.donor) && frm.doc.company) { 54 | frm.call({ 55 | doc: frm.doc, 56 | method: 'set_company_address', 57 | freeze: true 58 | }); 59 | } 60 | }, 61 | 62 | donation: function(frm) { 63 | if (frm.doc.recipient === 'Donor' && !frm.doc.donor) { 64 | frappe.msgprint(__('Please select donor first')); 65 | } 66 | } 67 | }); 68 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "autoname": "naming_series:", 4 | "creation": "2021-02-15 12:37:21.577042", 5 | "doctype": "DocType", 6 | "editable_grid": 1, 7 | "engine": "InnoDB", 8 | "field_order": [ 9 | "naming_series", 10 | "recipient", 11 | "member", 12 | "member_name", 13 | "member_email", 14 | "member_pan_number", 15 | "donor", 16 | "donor_name", 17 | "donor_email", 18 | "donor_pan_number", 19 | "column_break_4", 20 | "date", 21 | "fiscal_year", 22 | "section_break_11", 23 | "company", 24 | "company_address", 25 | "company_address_display", 26 | "column_break_14", 27 | "company_pan_number", 28 | "company_80g_number", 29 | "company_80g_wef", 30 | "title", 31 | "section_break_6", 32 | "get_payments", 33 | "payments", 34 | "total", 35 | "donation_details_section", 36 | "donation", 37 | "date_of_donation", 38 | "amount", 39 | "column_break_27", 40 | "mode_of_payment", 41 | "payment_id" 42 | ], 43 | "fields": [ 44 | { 45 | "fieldname": "recipient", 46 | "fieldtype": "Select", 47 | "in_list_view": 1, 48 | "label": "Certificate Recipient", 49 | "options": "Member\nDonor", 50 | "reqd": 1 51 | }, 52 | { 53 | "depends_on": "eval:doc.recipient === \"Member\";", 54 | "fieldname": "member", 55 | "fieldtype": "Link", 56 | "in_list_view": 1, 57 | "label": "Member", 58 | "mandatory_depends_on": "eval:doc.recipient === \"Member\";", 59 | "options": "Member" 60 | }, 61 | { 62 | "depends_on": "eval:doc.recipient === \"Member\";", 63 | "fetch_from": "member.member_name", 64 | "fieldname": "member_name", 65 | "fieldtype": "Data", 66 | "label": "Member Name", 67 | "read_only": 1 68 | }, 69 | { 70 | "depends_on": "eval:doc.recipient === \"Donor\";", 71 | "fieldname": "donor", 72 | "fieldtype": "Link", 73 | "in_list_view": 1, 74 | "label": "Donor", 75 | "mandatory_depends_on": "eval:doc.recipient === \"Donor\";", 76 | "options": "Donor" 77 | }, 78 | { 79 | "fieldname": "column_break_4", 80 | "fieldtype": "Column Break" 81 | }, 82 | { 83 | "fieldname": "date", 84 | "fieldtype": "Date", 85 | "label": "Date", 86 | "reqd": 1 87 | }, 88 | { 89 | "depends_on": "eval:doc.recipient === \"Member\";", 90 | "fieldname": "section_break_6", 91 | "fieldtype": "Section Break" 92 | }, 93 | { 94 | "fieldname": "payments", 95 | "fieldtype": "Table", 96 | "label": "Payments", 97 | "options": "Tax Exemption 80G Certificate Detail" 98 | }, 99 | { 100 | "fieldname": "total", 101 | "fieldtype": "Currency", 102 | "in_list_view": 1, 103 | "label": "Total", 104 | "read_only": 1 105 | }, 106 | { 107 | "depends_on": "eval:doc.recipient === \"Member\";", 108 | "fieldname": "fiscal_year", 109 | "fieldtype": "Link", 110 | "in_list_view": 1, 111 | "label": "Fiscal Year", 112 | "options": "Fiscal Year" 113 | }, 114 | { 115 | "fieldname": "company", 116 | "fieldtype": "Link", 117 | "label": "Company", 118 | "options": "Company", 119 | "reqd": 1 120 | }, 121 | { 122 | "fieldname": "get_payments", 123 | "fieldtype": "Button", 124 | "label": "Get Memberships" 125 | }, 126 | { 127 | "fieldname": "naming_series", 128 | "fieldtype": "Select", 129 | "label": "Naming Series", 130 | "options": "NPO-80G-.YYYY.-" 131 | }, 132 | { 133 | "fieldname": "section_break_11", 134 | "fieldtype": "Section Break", 135 | "label": "Company Details" 136 | }, 137 | { 138 | "fieldname": "company_address", 139 | "fieldtype": "Link", 140 | "label": "Company Address", 141 | "options": "Address" 142 | }, 143 | { 144 | "fieldname": "column_break_14", 145 | "fieldtype": "Column Break" 146 | }, 147 | { 148 | "fetch_from": "company.pan_details", 149 | "fieldname": "company_pan_number", 150 | "fieldtype": "Data", 151 | "label": "PAN Number", 152 | "read_only": 1 153 | }, 154 | { 155 | "fieldname": "company_address_display", 156 | "fieldtype": "Small Text", 157 | "hidden": 1, 158 | "label": "Company Address Display", 159 | "print_hide": 1, 160 | "read_only": 1 161 | }, 162 | { 163 | "fetch_from": "company.company_80g_number", 164 | "fieldname": "company_80g_number", 165 | "fieldtype": "Data", 166 | "label": "80G Number", 167 | "read_only": 1 168 | }, 169 | { 170 | "fetch_from": "company.with_effect_from", 171 | "fieldname": "company_80g_wef", 172 | "fieldtype": "Date", 173 | "label": "80G With Effect From", 174 | "read_only": 1 175 | }, 176 | { 177 | "depends_on": "eval:doc.recipient === \"Donor\";", 178 | "fieldname": "donation_details_section", 179 | "fieldtype": "Section Break", 180 | "label": "Donation Details" 181 | }, 182 | { 183 | "fieldname": "donation", 184 | "fieldtype": "Link", 185 | "label": "Donation", 186 | "mandatory_depends_on": "eval:doc.recipient === \"Donor\";", 187 | "options": "Donation" 188 | }, 189 | { 190 | "fetch_from": "donation.amount", 191 | "fieldname": "amount", 192 | "fieldtype": "Currency", 193 | "label": "Amount", 194 | "read_only": 1 195 | }, 196 | { 197 | "fetch_from": "donation.mode_of_payment", 198 | "fieldname": "mode_of_payment", 199 | "fieldtype": "Link", 200 | "label": "Mode of Payment", 201 | "options": "Mode of Payment", 202 | "read_only": 1 203 | }, 204 | { 205 | "fetch_from": "donation.date", 206 | "fieldname": "date_of_donation", 207 | "fieldtype": "Date", 208 | "label": "Date of Donation", 209 | "read_only": 1 210 | }, 211 | { 212 | "fieldname": "column_break_27", 213 | "fieldtype": "Column Break" 214 | }, 215 | { 216 | "depends_on": "eval:doc.recipient === \"Donor\";", 217 | "fetch_from": "donor.donor_name", 218 | "fieldname": "donor_name", 219 | "fieldtype": "Data", 220 | "label": "Donor Name", 221 | "read_only": 1 222 | }, 223 | { 224 | "depends_on": "eval:doc.recipient === \"Donor\";", 225 | "fetch_from": "donor.email", 226 | "fieldname": "donor_email", 227 | "fieldtype": "Data", 228 | "label": "Email", 229 | "read_only": 1 230 | }, 231 | { 232 | "depends_on": "eval:doc.recipient === \"Member\";", 233 | "fetch_from": "member.email_id", 234 | "fieldname": "member_email", 235 | "fieldtype": "Data", 236 | "in_list_view": 1, 237 | "label": "Email", 238 | "read_only": 1 239 | }, 240 | { 241 | "depends_on": "eval:doc.recipient === \"Member\";", 242 | "fetch_from": "member.pan_number", 243 | "fieldname": "member_pan_number", 244 | "fieldtype": "Data", 245 | "label": "PAN Details", 246 | "read_only": 1 247 | }, 248 | { 249 | "depends_on": "eval:doc.recipient === \"Donor\";", 250 | "fetch_from": "donor.pan_number", 251 | "fieldname": "donor_pan_number", 252 | "fieldtype": "Data", 253 | "label": "PAN Details", 254 | "read_only": 1 255 | }, 256 | { 257 | "fieldname": "title", 258 | "fieldtype": "Data", 259 | "hidden": 1, 260 | "label": "Title", 261 | "print_hide": 1 262 | }, 263 | { 264 | "fetch_from": "donation.payment_id", 265 | "fieldname": "payment_id", 266 | "fieldtype": "Data", 267 | "label": "Payment ID", 268 | "read_only": 1 269 | } 270 | ], 271 | "index_web_pages_for_search": 1, 272 | "links": [], 273 | "modified": "2022-03-16 17:21:39.831059", 274 | "modified_by": "Administrator", 275 | "module": "Non Profit", 276 | "name": "Tax Exemption 80G Certificate", 277 | "owner": "Administrator", 278 | "permissions": [ 279 | { 280 | "create": 1, 281 | "delete": 1, 282 | "email": 1, 283 | "export": 1, 284 | "print": 1, 285 | "read": 1, 286 | "report": 1, 287 | "role": "System Manager", 288 | "share": 1, 289 | "write": 1 290 | } 291 | ], 292 | "search_fields": "member, member_name", 293 | "sort_field": "modified", 294 | "sort_order": "DESC", 295 | "title_field": "title", 296 | "track_changes": 1 297 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/tax_exemption_80g_certificate/tax_exemption_80g_certificate.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | import frappe 6 | from frappe import _ 7 | from frappe.contacts.doctype.address.address import get_company_address 8 | from frappe.model.document import Document 9 | from frappe.utils import flt, get_link_to_form 10 | 11 | from erpnext.accounts.utils import get_fiscal_year 12 | 13 | 14 | class TaxExemption80GCertificate(Document): 15 | def validate(self): 16 | self.validate_duplicates() 17 | self.validate_company_details() 18 | self.set_company_address() 19 | self.calculate_total() 20 | self.set_title() 21 | 22 | def validate_duplicates(self): 23 | if self.recipient == 'Donor': 24 | certificate = frappe.db.exists(self.doctype, { 25 | 'donation': self.donation, 26 | 'name': ('!=', self.name) 27 | }) 28 | if certificate: 29 | frappe.throw(_('An 80G Certificate {0} already exists for the donation {1}').format( 30 | get_link_to_form(self.doctype, certificate), frappe.bold(self.donation) 31 | ), title=_('Duplicate Certificate')) 32 | 33 | def validate_company_details(self): 34 | fields = ['company_80g_number', 'with_effect_from', 'pan_details'] 35 | company_details = frappe.db.get_value('Company', self.company, fields, as_dict=True) 36 | if not company_details.company_80g_number: 37 | frappe.throw(_('Please set the {0} for company {1}').format(frappe.bold('80G Number'), 38 | get_link_to_form('Company', self.company))) 39 | 40 | if not company_details.pan_details: 41 | frappe.throw(_('Please set the {0} for company {1}').format(frappe.bold('PAN Number'), 42 | get_link_to_form('Company', self.company))) 43 | 44 | @frappe.whitelist() 45 | def set_company_address(self): 46 | address = get_company_address(self.company) 47 | self.company_address = address.company_address 48 | self.company_address_display = address.company_address_display 49 | 50 | def calculate_total(self): 51 | if self.recipient == 'Donor': 52 | return 53 | 54 | total = 0 55 | for entry in self.payments: 56 | total += flt(entry.amount) 57 | self.total = total 58 | 59 | def set_title(self): 60 | if self.recipient == 'Member': 61 | self.title = self.member_name 62 | else: 63 | self.title = self.donor_name 64 | 65 | @frappe.whitelist() 66 | def get_payments(self): 67 | if not self.member: 68 | frappe.throw(_('Please select a Member first.')) 69 | 70 | fiscal_year = get_fiscal_year(fiscal_year=self.fiscal_year, as_dict=True) 71 | 72 | memberships = frappe.db.get_all('Membership', { 73 | 'member': self.member, 74 | 'from_date': ['between', (fiscal_year.year_start_date, fiscal_year.year_end_date)], 75 | 'membership_status': ('!=', 'Cancelled') 76 | }, ['from_date', 'amount', 'name', 'invoice', 'payment_id'], order_by='from_date') 77 | 78 | if not memberships: 79 | frappe.msgprint(_('No Membership Payments found against the Member {0}').format(self.member)) 80 | 81 | total = 0 82 | self.payments = [] 83 | 84 | for doc in memberships: 85 | self.append('payments', { 86 | 'date': doc.from_date, 87 | 'amount': doc.amount, 88 | 'invoice_id': doc.invoice, 89 | 'payment_id': doc.payment_id, 90 | 'membership': doc.name 91 | }) 92 | total += flt(doc.amount) 93 | 94 | self.total = total 95 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import frappe 5 | from frappe.utils import getdate 6 | from frappe.tests.utils import FrappeTestCase 7 | 8 | from erpnext.accounts.utils import get_fiscal_year 9 | from non_profit.non_profit.doctype.donation.donation import create_razorpay_donation 10 | from non_profit.non_profit.doctype.donation.test_donation import ( 11 | create_donor, 12 | create_donor_type, 13 | create_mode_of_payment, 14 | ) 15 | from non_profit.non_profit.doctype.member.member import create_member 16 | from non_profit.non_profit.doctype.membership.test_membership import make_membership, setup_membership 17 | 18 | 19 | class TestTaxExemption80GCertificate(FrappeTestCase): 20 | def setUp(self): 21 | frappe.db.sql('delete from `tabTax Exemption 80G Certificate`') 22 | frappe.db.sql('delete from `tabMembership`') 23 | create_donor_type() 24 | settings = frappe.get_doc('Non Profit Settings') 25 | settings.company = '_Test Company' 26 | settings.donation_company = '_Test Company' 27 | settings.default_donor_type = '_Test Donor' 28 | settings.creation_user = 'Administrator' 29 | settings.save() 30 | 31 | company = frappe.get_doc('Company', '_Test Company') 32 | company.pan_details = 'BBBTI3374C' 33 | company.company_80g_number = 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087' 34 | company.with_effect_from = getdate() 35 | company.save() 36 | 37 | def test_duplicate_donation_certificate(self): 38 | donor = create_donor() 39 | create_mode_of_payment() 40 | payment = frappe._dict({ 41 | 'amount': 100, # rzp sends data in paise 42 | 'method': 'Debit Card', 43 | 'id': 'pay_MeXAmsgeKOhq7O' 44 | }) 45 | donation = create_razorpay_donation(donor, payment) 46 | 47 | args = frappe._dict({ 48 | 'recipient': 'Donor', 49 | 'donor': donor.name, 50 | 'donation': donation.name 51 | }) 52 | certificate = create_80g_certificate(args) 53 | certificate.insert() 54 | 55 | # check company details 56 | self.assertEqual(certificate.company_pan_number, 'BBBTI3374C') 57 | self.assertEqual(certificate.company_80g_number, 'NQ.CIT(E)I2018-19/DEL-IE28615-27062018/10087') 58 | 59 | # check donation details 60 | self.assertEqual(certificate.amount, donation.amount) 61 | 62 | duplicate_certificate = create_80g_certificate(args) 63 | # duplicate validation 64 | self.assertRaises(frappe.ValidationError, duplicate_certificate.insert) 65 | 66 | def test_membership_80g_certificate(self): 67 | plan = setup_membership() 68 | 69 | # make test member 70 | member_doc = create_member(frappe._dict({ 71 | 'fullname': "_Test_Member", 72 | 'email': "_test_member_erpnext@example.com", 73 | 'plan_id': plan.name 74 | })) 75 | member_doc.make_customer_and_link() 76 | member = member_doc.name 77 | 78 | membership = make_membership(member, { "from_date": getdate() }) 79 | invoice = membership.generate_invoice(save=True) 80 | 81 | args = frappe._dict({ 82 | 'recipient': 'Member', 83 | 'member': member, 84 | 'fiscal_year': get_fiscal_year(getdate(), as_dict=True).get('name') 85 | }) 86 | certificate = create_80g_certificate(args) 87 | certificate.get_payments() 88 | certificate.insert() 89 | 90 | self.assertEqual(len(certificate.payments), 1) 91 | self.assertEqual(certificate.payments[0].amount, membership.amount) 92 | self.assertEqual(certificate.payments[0].invoice_id, invoice.name) 93 | 94 | 95 | def create_80g_certificate(args): 96 | certificate = frappe.get_doc({ 97 | 'doctype': 'Tax Exemption 80G Certificate', 98 | 'recipient': args.recipient, 99 | 'date': getdate(), 100 | 'company': '_Test Company' 101 | }) 102 | 103 | certificate.update(args) 104 | 105 | return certificate 106 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/tax_exemption_80g_certificate_detail/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/tax_exemption_80g_certificate_detail/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "creation": "2021-02-15 12:43:52.754124", 4 | "doctype": "DocType", 5 | "editable_grid": 1, 6 | "engine": "InnoDB", 7 | "field_order": [ 8 | "date", 9 | "amount", 10 | "invoice_id", 11 | "column_break_4", 12 | "payment_id", 13 | "membership" 14 | ], 15 | "fields": [ 16 | { 17 | "fieldname": "date", 18 | "fieldtype": "Date", 19 | "in_list_view": 1, 20 | "label": "Date", 21 | "reqd": 1 22 | }, 23 | { 24 | "fieldname": "amount", 25 | "fieldtype": "Currency", 26 | "in_list_view": 1, 27 | "label": "Amount", 28 | "reqd": 1 29 | }, 30 | { 31 | "fieldname": "invoice_id", 32 | "fieldtype": "Link", 33 | "in_list_view": 1, 34 | "label": "Invoice ID", 35 | "options": "Sales Invoice", 36 | "reqd": 1 37 | }, 38 | { 39 | "fieldname": "membership", 40 | "fieldtype": "Link", 41 | "in_list_view": 1, 42 | "label": "Membership", 43 | "options": "Membership" 44 | }, 45 | { 46 | "fieldname": "column_break_4", 47 | "fieldtype": "Column Break" 48 | }, 49 | { 50 | "fieldname": "payment_id", 51 | "fieldtype": "Data", 52 | "in_list_view": 1, 53 | "label": "Payment ID" 54 | } 55 | ], 56 | "index_web_pages_for_search": 1, 57 | "istable": 1, 58 | "links": [], 59 | "modified": "2022-03-17 11:55:24.621708", 60 | "modified_by": "Administrator", 61 | "module": "Non Profit", 62 | "name": "Tax Exemption 80G Certificate Detail", 63 | "owner": "Administrator", 64 | "permissions": [], 65 | "sort_field": "modified", 66 | "sort_order": "DESC", 67 | "track_changes": 1 68 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/tax_exemption_80g_certificate_detail/tax_exemption_80g_certificate_detail.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | # import frappe 6 | from frappe.model.document import Document 7 | 8 | 9 | class TaxExemption80GCertificateDetail(Document): 10 | pass 11 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/volunteer/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer/test_volunteer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // rename this file from _test_[name] to test_[name] to activate 3 | // and remove above this line 4 | 5 | QUnit.test("test: Volunteer", function (assert) { 6 | let done = assert.async(); 7 | 8 | // number of asserts 9 | assert.expect(4); 10 | 11 | frappe.run_serially([ 12 | // insert a new Member 13 | () => frappe.tests.make('Volunteer', [ 14 | // values to be set 15 | {volunteer_name: 'Test Volunteer'}, 16 | {volunteer_type:'Test Work'}, 17 | {email:'test@example.com'}, 18 | {'availability': 'Weekends'}, 19 | {volunteer_skills:[ 20 | [ 21 | {'volunteer_skills': 'Fundraiser'}, 22 | ] 23 | ]}, 24 | ]), 25 | () => { 26 | assert.equal(cur_frm.doc.volunteer_name, 'Test Volunteer'); 27 | assert.equal(cur_frm.doc.volunteer_type, 'Test Work'); 28 | assert.equal(cur_frm.doc.email, 'test@example.com'); 29 | assert.equal(cur_frm.doc.availability, 'Weekends'); 30 | }, 31 | () => done() 32 | ]); 33 | 34 | }); 35 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer/test_volunteer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestVolunteer(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer/volunteer.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Volunteer', { 5 | refresh: function(frm) { 6 | 7 | frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Volunteer'}; 8 | 9 | frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); 10 | 11 | if(!frm.doc.__islocal) { 12 | frappe.contacts.render_address_and_contact(frm); 13 | } else { 14 | frappe.contacts.clear_address_and_contact(frm); 15 | } 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer/volunteer.json: -------------------------------------------------------------------------------- 1 | { 2 | "actions": [], 3 | "allow_rename": 1, 4 | "autoname": "field:email", 5 | "creation": "2017-09-19 16:16:45.676019", 6 | "doctype": "DocType", 7 | "editable_grid": 1, 8 | "engine": "InnoDB", 9 | "field_order": [ 10 | "volunteer_name", 11 | "column_break_5", 12 | "volunteer_type", 13 | "email", 14 | "image", 15 | "address_contacts", 16 | "address_html", 17 | "column_break_9", 18 | "contact_html", 19 | "volunteer_availability_and_skills_details", 20 | "availability", 21 | "availability_timeslot", 22 | "column_break_12", 23 | "volunteer_skills", 24 | "section_break_15", 25 | "note" 26 | ], 27 | "fields": [ 28 | { 29 | "fieldname": "volunteer_name", 30 | "fieldtype": "Data", 31 | "in_list_view": 1, 32 | "label": "Volunteer Name", 33 | "reqd": 1 34 | }, 35 | { 36 | "fieldname": "column_break_5", 37 | "fieldtype": "Column Break" 38 | }, 39 | { 40 | "fieldname": "volunteer_type", 41 | "fieldtype": "Link", 42 | "in_list_view": 1, 43 | "label": "Volunteer Type", 44 | "options": "Volunteer Type", 45 | "reqd": 1 46 | }, 47 | { 48 | "fieldname": "email", 49 | "fieldtype": "Data", 50 | "in_list_view": 1, 51 | "label": "Email", 52 | "reqd": 1, 53 | "unique": 1 54 | }, 55 | { 56 | "fieldname": "image", 57 | "fieldtype": "Attach Image", 58 | "hidden": 1, 59 | "label": "Image", 60 | "no_copy": 1, 61 | "print_hide": 1 62 | }, 63 | { 64 | "depends_on": "eval:!doc.__islocal;", 65 | "fieldname": "address_contacts", 66 | "fieldtype": "Section Break", 67 | "label": "Address and Contact", 68 | "options": "fa fa-map-marker" 69 | }, 70 | { 71 | "fieldname": "address_html", 72 | "fieldtype": "HTML", 73 | "label": "Address HTML" 74 | }, 75 | { 76 | "fieldname": "column_break_9", 77 | "fieldtype": "Column Break" 78 | }, 79 | { 80 | "fieldname": "contact_html", 81 | "fieldtype": "HTML", 82 | "label": "Contact HTML" 83 | }, 84 | { 85 | "fieldname": "volunteer_availability_and_skills_details", 86 | "fieldtype": "Section Break", 87 | "label": "Availability and Skills" 88 | }, 89 | { 90 | "fieldname": "availability", 91 | "fieldtype": "Select", 92 | "label": "Availability", 93 | "options": "\nWeekly\nWeekdays\nWeekends" 94 | }, 95 | { 96 | "fieldname": "availability_timeslot", 97 | "fieldtype": "Select", 98 | "label": "Availability Timeslot", 99 | "options": "\nMorning\nAfternoon\nEvening\nAnytime" 100 | }, 101 | { 102 | "fieldname": "column_break_12", 103 | "fieldtype": "Column Break" 104 | }, 105 | { 106 | "fieldname": "volunteer_skills", 107 | "fieldtype": "Table", 108 | "label": "Volunteer Skills", 109 | "options": "Volunteer Skill" 110 | }, 111 | { 112 | "fieldname": "section_break_15", 113 | "fieldtype": "Section Break" 114 | }, 115 | { 116 | "fieldname": "note", 117 | "fieldtype": "Long Text", 118 | "label": "Note" 119 | } 120 | ], 121 | "image_field": "image", 122 | "links": [], 123 | "modified": "2020-09-16 23:45:15.595952", 124 | "modified_by": "Administrator", 125 | "module": "Non Profit", 126 | "name": "Volunteer", 127 | "owner": "Administrator", 128 | "permissions": [ 129 | { 130 | "create": 1, 131 | "delete": 1, 132 | "email": 1, 133 | "export": 1, 134 | "print": 1, 135 | "read": 1, 136 | "report": 1, 137 | "role": "Non Profit Manager", 138 | "share": 1, 139 | "write": 1 140 | } 141 | ], 142 | "quick_entry": 1, 143 | "restrict_to_domain": "Non Profit", 144 | "sort_field": "modified", 145 | "sort_order": "DESC", 146 | "title_field": "volunteer_name", 147 | "track_changes": 1 148 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer/volunteer.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.contacts.address_and_contact import load_address_and_contact 6 | from frappe.model.document import Document 7 | 8 | 9 | class Volunteer(Document): 10 | def onload(self): 11 | """Load address and contacts in `__onload`""" 12 | load_address_and_contact(self) 13 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_skill/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/volunteer_skill/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_skill/volunteer_skill.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow_copy": 0, 3 | "allow_guest_to_view": 0, 4 | "allow_import": 0, 5 | "allow_rename": 0, 6 | "beta": 0, 7 | "creation": "2017-09-20 15:26:26.453435", 8 | "custom": 0, 9 | "docstatus": 0, 10 | "doctype": "DocType", 11 | "document_type": "", 12 | "editable_grid": 1, 13 | "engine": "InnoDB", 14 | "fields": [ 15 | { 16 | "allow_bulk_edit": 0, 17 | "allow_on_submit": 0, 18 | "bold": 0, 19 | "collapsible": 0, 20 | "columns": 0, 21 | "fieldname": "volunteer_skill", 22 | "fieldtype": "Data", 23 | "hidden": 0, 24 | "ignore_user_permissions": 0, 25 | "ignore_xss_filter": 0, 26 | "in_filter": 0, 27 | "in_global_search": 0, 28 | "in_list_view": 1, 29 | "in_standard_filter": 0, 30 | "label": "Volunteer Skill", 31 | "length": 0, 32 | "no_copy": 0, 33 | "options": "", 34 | "permlevel": 0, 35 | "precision": "", 36 | "print_hide": 0, 37 | "print_hide_if_no_value": 0, 38 | "read_only": 0, 39 | "remember_last_selected_value": 0, 40 | "report_hide": 0, 41 | "reqd": 0, 42 | "search_index": 0, 43 | "set_only_once": 0, 44 | "unique": 0 45 | } 46 | ], 47 | "has_web_view": 0, 48 | "hide_heading": 0, 49 | "hide_toolbar": 0, 50 | "idx": 0, 51 | "image_view": 0, 52 | "in_create": 0, 53 | "is_submittable": 0, 54 | "issingle": 0, 55 | "istable": 1, 56 | "max_attachments": 0, 57 | "modified": "2017-12-06 11:54:14.396354", 58 | "modified_by": "Administrator", 59 | "module": "Non Profit", 60 | "name": "Volunteer Skill", 61 | "name_case": "", 62 | "owner": "Administrator", 63 | "permissions": [], 64 | "quick_entry": 1, 65 | "read_only": 0, 66 | "read_only_onload": 0, 67 | "restrict_to_domain": "Non Profit", 68 | "show_name_in_global_search": 0, 69 | "sort_field": "modified", 70 | "sort_order": "DESC", 71 | "track_changes": 1, 72 | "track_seen": 0 73 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_skill/volunteer_skill.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class VolunteerSkill(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_type/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/doctype/volunteer_type/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_type/test_volunteer_type.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | // rename this file from _test_[name] to test_[name] to activate 3 | // and remove above this line 4 | 5 | QUnit.test("test: Volunteer Type", function (assert) { 6 | let done = assert.async(); 7 | 8 | // number of asserts 9 | assert.expect(2); 10 | 11 | frappe.run_serially([ 12 | // insert a new Member 13 | () => { 14 | return frappe.tests.make('Volunteer Type', [ 15 | // values to be set 16 | {__newname: 'Test Work'}, 17 | {amount: 500} 18 | ]); 19 | }, 20 | () => { 21 | assert.equal(cur_frm.doc.name, 'Test Work'); 22 | assert.equal(cur_frm.doc.amount, 500); 23 | }, 24 | () => done() 25 | ]); 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_type/test_volunteer_type.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors 2 | # See license.txt 3 | 4 | import unittest 5 | 6 | 7 | class TestVolunteerType(unittest.TestCase): 8 | pass 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_type/volunteer_type.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Volunteer Type', { 5 | refresh: function() { 6 | 7 | } 8 | }); 9 | -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_type/volunteer_type.json: -------------------------------------------------------------------------------- 1 | { 2 | "allow_copy": 0, 3 | "allow_guest_to_view": 0, 4 | "allow_import": 0, 5 | "allow_rename": 0, 6 | "autoname": "prompt", 7 | "beta": 0, 8 | "creation": "2017-09-19 16:13:07.763273", 9 | "custom": 0, 10 | "docstatus": 0, 11 | "doctype": "DocType", 12 | "document_type": "", 13 | "editable_grid": 1, 14 | "engine": "InnoDB", 15 | "fields": [ 16 | { 17 | "allow_bulk_edit": 0, 18 | "allow_on_submit": 0, 19 | "bold": 0, 20 | "collapsible": 0, 21 | "columns": 0, 22 | "fieldname": "amount", 23 | "fieldtype": "Float", 24 | "hidden": 0, 25 | "ignore_user_permissions": 0, 26 | "ignore_xss_filter": 0, 27 | "in_filter": 0, 28 | "in_global_search": 0, 29 | "in_list_view": 1, 30 | "in_standard_filter": 0, 31 | "label": "Amount", 32 | "length": 0, 33 | "no_copy": 0, 34 | "permlevel": 0, 35 | "precision": "", 36 | "print_hide": 0, 37 | "print_hide_if_no_value": 0, 38 | "read_only": 0, 39 | "remember_last_selected_value": 0, 40 | "report_hide": 0, 41 | "reqd": 1, 42 | "search_index": 0, 43 | "set_only_once": 0, 44 | "unique": 0 45 | } 46 | ], 47 | "has_web_view": 0, 48 | "hide_heading": 0, 49 | "hide_toolbar": 0, 50 | "idx": 0, 51 | "image_view": 0, 52 | "in_create": 0, 53 | "is_submittable": 0, 54 | "issingle": 0, 55 | "istable": 0, 56 | "max_attachments": 0, 57 | "modified": "2017-12-06 11:52:08.800425", 58 | "modified_by": "Administrator", 59 | "module": "Non Profit", 60 | "name": "Volunteer Type", 61 | "name_case": "", 62 | "owner": "Administrator", 63 | "permissions": [ 64 | { 65 | "amend": 0, 66 | "apply_user_permissions": 0, 67 | "cancel": 0, 68 | "create": 1, 69 | "delete": 1, 70 | "email": 1, 71 | "export": 1, 72 | "if_owner": 0, 73 | "import": 0, 74 | "permlevel": 0, 75 | "print": 1, 76 | "read": 1, 77 | "report": 1, 78 | "role": "Non Profit Manager", 79 | "set_user_permissions": 0, 80 | "share": 1, 81 | "submit": 0, 82 | "write": 1 83 | } 84 | ], 85 | "quick_entry": 1, 86 | "read_only": 0, 87 | "read_only_onload": 0, 88 | "restrict_to_domain": "Non Profit", 89 | "show_name_in_global_search": 0, 90 | "sort_field": "modified", 91 | "sort_order": "DESC", 92 | "track_changes": 1, 93 | "track_seen": 0 94 | } -------------------------------------------------------------------------------- /non_profit/non_profit/doctype/volunteer_type/volunteer_type.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | from frappe.model.document import Document 6 | 7 | 8 | class VolunteerType(Document): 9 | pass 10 | -------------------------------------------------------------------------------- /non_profit/non_profit/print_format/80g_certificate_for_donation/80g_certificate_for_donation.json: -------------------------------------------------------------------------------- 1 | { 2 | "absolute_value": 0, 3 | "align_labels_right": 0, 4 | "creation": "2021-02-22 00:17:33.878581", 5 | "css": ".details {\n font-size: 15px;\n font-family: Tahoma, sans-serif;;\n line-height: 150%;\n}\n\n.certificate-footer {\n font-size: 15px;\n font-family: Tahoma, sans-serif;\n line-height: 140%;\n margin-top: 120px;\n}\n\n.company-address {\n color: #666666;\n font-size: 15px;\n font-family: Tahoma, sans-serif;;\n}", 6 | "custom_format": 1, 7 | "default_print_language": "en", 8 | "disabled": 0, 9 | "doc_type": "Tax Exemption 80G Certificate", 10 | "docstatus": 0, 11 | "doctype": "Print Format", 12 | "font": "Default", 13 | "html": "{% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n{%- endif %}\n\n
\n

{{ doc.company }} 80G Donor Certificate

\n
\n

\n\n
\n

{{ _(\"Certificate No. : \") }} {{ doc.name }}

\n

\n \t{{ _(\"Date\") }} : {{ doc.get_formatted(\"date\") }}
\n

\n

\n \n
\n\n This is to confirm that the {{ doc.company }} received an amount of {{doc.get_formatted(\"amount\")}}\n from {{ doc.donor_name }}\n {% if doc.pan_number -%}\n bearing PAN Number {{ doc.member_pan_number }}\n {%- endif %}\n\n via the Mode of Payment {{doc.mode_of_payment}}\n\n {% if doc.payment_id -%}\n bearing Payment ID {{ doc.payment_id }}\n {%- endif %}\n\n on {{ doc.get_formatted(\"date_of_donation\") }}\n

\n \n

\n We thank you for your contribution towards the corpus of the {{ doc.company }} and helping support our work.\n

\n\n
\n
\n\n

\n

{{doc.company_address_display }}

\n\n", 14 | "idx": 0, 15 | "line_breaks": 0, 16 | "modified": "2022-03-16 17:25:33.420509", 17 | "modified_by": "Administrator", 18 | "module": "Non Profit", 19 | "name": "80G Certificate for Donation", 20 | "owner": "Administrator", 21 | "print_format_builder": 0, 22 | "print_format_type": "Jinja", 23 | "raw_printing": 0, 24 | "show_section_headings": 0, 25 | "standard": "Yes" 26 | } -------------------------------------------------------------------------------- /non_profit/non_profit/print_format/80g_certificate_for_donation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/print_format/80g_certificate_for_donation/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/print_format/80g_certificate_for_membership/80g_certificate_for_membership.json: -------------------------------------------------------------------------------- 1 | { 2 | "absolute_value": 0, 3 | "align_labels_right": 0, 4 | "creation": "2021-02-15 16:53:55.026611", 5 | "css": ".details {\n font-size: 15px;\n font-family: Tahoma, sans-serif;;\n line-height: 150%;\n}\n\n.certificate-footer {\n font-size: 15px;\n font-family: Tahoma, sans-serif;\n line-height: 140%;\n margin-top: 120px;\n}\n\n.company-address {\n color: #666666;\n font-size: 15px;\n font-family: Tahoma, sans-serif;;\n}", 6 | "custom_format": 1, 7 | "default_print_language": "en", 8 | "disabled": 0, 9 | "doc_type": "Tax Exemption 80G Certificate", 10 | "docstatus": 0, 11 | "doctype": "Print Format", 12 | "font": "Default", 13 | "html": "{% if letter_head and not no_letterhead -%}\n
{{ letter_head }}
\n{%- endif %}\n\n
\n

{{ doc.company }} Members 80G Donor Certificate

\n

Financial Cycle {{ doc.fiscal_year }}

\n
\n

\n\n
\n

{{ _(\"Certificate No. : \") }} {{ doc.name }}

\n

\n \t{{ _(\"Date\") }} : {{ doc.get_formatted(\"date\") }}
\n

\n

\n \n
\n This is to confirm that the {{ doc.company }} received a total amount of {{doc.get_formatted(\"total\")}}\n from {{ doc.member_name }}\n {% if doc.pan_number -%}\n bearing PAN Number {{ doc.member_pan_number }}\n {%- endif %}\n as per the payment details given below:\n \n

\n \n \t\n \t\t\n \t\t\t\n \t\t\t\n \t\t\t\n \t\t\n \t\n \t\n \t\t{%- for payment in doc.payments -%}\n \t\t\n \t\t\t\n \t\t\t\n \t\t\t\n \t\t\n \t\t{%- endfor -%}\n \t\n
{{ _(\"Date\") }}{{ _(\"Amount\") }}{{ _(\"Invoice ID\") }}
{{ payment.date }} {{ payment.get_formatted(\"amount\") }}{{ payment.invoice_id }}
\n \n
\n \n

\n We thank you for your contribution towards the corpus of the {{ doc.company }} and helping support our work.\n

\n\n
\n
\n\n

\n

{{doc.company_address_display }}

\n\n", 14 | "idx": 0, 15 | "line_breaks": 0, 16 | "modified": "2021-02-21 23:29:00.778973", 17 | "modified_by": "Administrator", 18 | "module": "Non Profit", 19 | "name": "80G Certificate for Membership", 20 | "owner": "Administrator", 21 | "print_format_builder": 0, 22 | "print_format_type": "Jinja", 23 | "raw_printing": 0, 24 | "show_section_headings": 0, 25 | "standard": "Yes" 26 | } -------------------------------------------------------------------------------- /non_profit/non_profit/print_format/80g_certificate_for_membership/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/print_format/80g_certificate_for_membership/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/report/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/report/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/report/expiring_memberships/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/report/expiring_memberships/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/report/expiring_memberships/expiring_memberships.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors 2 | // For license information, please see license.txt 3 | /* eslint-disable */ 4 | 5 | frappe.query_reports["Expiring Memberships"] = { 6 | "filters": [ 7 | { 8 | "fieldname": "fiscal_year", 9 | "label": __("Fiscal Year"), 10 | "fieldtype": "Link", 11 | "options": "Fiscal Year", 12 | "default": frappe.defaults.get_user_default("fiscal_year"), 13 | "reqd": 1 14 | }, 15 | { 16 | "fieldname":"month", 17 | "label": __("Month"), 18 | "fieldtype": "Select", 19 | "options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", 20 | "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", 21 | "Dec"][frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth()], 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /non_profit/non_profit/report/expiring_memberships/expiring_memberships.json: -------------------------------------------------------------------------------- 1 | { 2 | "add_total_row": 0, 3 | "apply_user_permissions": 1, 4 | "creation": "2018-05-24 11:44:08.942809", 5 | "disabled": 0, 6 | "docstatus": 0, 7 | "doctype": "Report", 8 | "idx": 0, 9 | "is_standard": "Yes", 10 | "letter_head": "ERPNext Foundation", 11 | "modified": "2018-05-24 11:44:08.942809", 12 | "modified_by": "Administrator", 13 | "module": "Non Profit", 14 | "name": "Expiring Memberships", 15 | "owner": "Administrator", 16 | "ref_doctype": "Membership", 17 | "report_name": "Expiring Memberships", 18 | "report_type": "Script Report", 19 | "roles": [ 20 | { 21 | "role": "Non Profit Manager" 22 | }, 23 | { 24 | "role": "Non Profit Member" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /non_profit/non_profit/report/expiring_memberships/expiring_memberships.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors 2 | # For license information, please see license.txt 3 | 4 | 5 | import frappe 6 | from frappe import _ 7 | 8 | 9 | def execute(filters=None): 10 | columns = get_columns(filters) 11 | data = get_data(filters) 12 | return columns, data 13 | 14 | def get_columns(filters): 15 | return [ 16 | _("Membership Type") + ":Link/Membership Type:100", _("Membership ID") + ":Link/Membership:140", 17 | _("Member ID") + ":Link/Member:140", _("Member Name") + ":Data:140", _("Email") + ":Data:140", 18 | _("Expiring On") + ":Date:120" 19 | ] 20 | 21 | def get_data(filters): 22 | 23 | filters["month"] = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].index(filters.month) + 1 24 | 25 | return frappe.db.sql(""" 26 | select ms.membership_type,ms.name,m.name,m.member_name,m.email,ms.max_membership_date 27 | from `tabMember` m 28 | inner join (select name,membership_type,max(to_date) as max_membership_date,member 29 | from `tabMembership` 30 | where paid = 1 31 | group by member 32 | order by max_membership_date asc) ms 33 | on m.name = ms.member 34 | where month(max_membership_date) = %(month)s and year(max_membership_date) = %(year)s """,{'month': filters.get('month'),'year':filters.get('fiscal_year')}) 35 | -------------------------------------------------------------------------------- /non_profit/non_profit/utils.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | from non_profit.setup import setup_non_profit 4 | 5 | 6 | def get_company(): 7 | company = frappe.defaults.get_defaults().company 8 | if company: 9 | return company 10 | else: 11 | company = frappe.get_list("Company", limit=1) 12 | if company: 13 | return company[0].name 14 | return None 15 | 16 | 17 | def before_tests(): 18 | # complete setup if missing 19 | from frappe.desk.page.setup_wizard.setup_wizard import setup_complete 20 | if not frappe.get_list("Company"): 21 | setup_complete({ 22 | "currency" :"USD", 23 | "full_name" :"Test User", 24 | "company_name" :"Frappe Care LLC", 25 | "timezone" :"America/New_York", 26 | "company_abbr" :"WP", 27 | "industry" :"Healthcare", 28 | "country" :"United States", 29 | "fy_start_date" :"2021-01-01", 30 | "fy_end_date" :"2021-12-31", 31 | "language" :"english", 32 | "company_tagline" :"Testing", 33 | "email" :"test@erpnext.com", 34 | "password" :"test", 35 | "chart_of_accounts" : "Standard", 36 | "domains" : ["Non Profit"], 37 | }) 38 | setup_non_profit() 39 | -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/web_form/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/certification_application/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/web_form/certification_application/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/certification_application/certification_application.js: -------------------------------------------------------------------------------- 1 | frappe.ready(function() { 2 | // bind events here 3 | $(".page-header-actions-block .btn-primary, .page-header-actions-block .btn-default").addClass('hidden'); 4 | $(".text-right .btn-primary").addClass('hidden'); 5 | 6 | if (frappe.utils.get_url_arg('name')) { 7 | $('.page-content .btn-form-submit').addClass('hidden'); 8 | } else { 9 | user_name = frappe.full_name 10 | user_email_id = frappe.session.user 11 | $('[data-fieldname="currency"]').val("INR"); 12 | $('[data-fieldname="name_of_applicant"]').val(user_name); 13 | $('[data-fieldname="email"]').val(user_email_id); 14 | $('[data-fieldname="amount"]').val(20000); 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/certification_application/certification_application.json: -------------------------------------------------------------------------------- 1 | { 2 | "accept_payment": 1, 3 | "allow_comments": 0, 4 | "allow_delete": 0, 5 | "allow_edit": 0, 6 | "allow_incomplete": 0, 7 | "allow_multiple": 1, 8 | "allow_print": 0, 9 | "amount": 0.0, 10 | "amount_based_on_field": 1, 11 | "amount_field": "amount", 12 | "creation": "2018-06-08 16:24:05.805225", 13 | "doc_type": "Certification Application", 14 | "docstatus": 0, 15 | "doctype": "Web Form", 16 | "idx": 0, 17 | "introduction_text": "", 18 | "is_standard": 1, 19 | "login_required": 1, 20 | "max_attachment_size": 0, 21 | "modified": "2018-06-11 16:11:14.544987", 22 | "modified_by": "Administrator", 23 | "module": "Non Profit", 24 | "name": "certification-application", 25 | "owner": "Administrator", 26 | "payment_button_help": "Pay for your certification using RazorPay", 27 | "payment_button_label": "Pay Now", 28 | "payment_gateway": "Razorpay", 29 | "published": 1, 30 | "route": "certification-application", 31 | "show_sidebar": 1, 32 | "sidebar_items": [], 33 | "success_url": "/certification-application", 34 | "title": "Certification Application", 35 | "web_form_fields": [ 36 | { 37 | "fieldname": "name_of_applicant", 38 | "fieldtype": "Data", 39 | "hidden": 0, 40 | "label": "Name of Applicant", 41 | "max_length": 0, 42 | "max_value": 0, 43 | "read_only": 0, 44 | "reqd": 0 45 | }, 46 | { 47 | "fieldname": "email", 48 | "fieldtype": "Link", 49 | "hidden": 0, 50 | "label": "Email", 51 | "max_length": 0, 52 | "max_value": 0, 53 | "options": "User", 54 | "read_only": 1, 55 | "reqd": 1 56 | }, 57 | { 58 | "fieldname": "currency", 59 | "fieldtype": "Select", 60 | "hidden": 0, 61 | "label": "Currency", 62 | "max_length": 0, 63 | "max_value": 0, 64 | "options": "USD\nINR", 65 | "read_only": 1, 66 | "reqd": 0 67 | }, 68 | { 69 | "fieldname": "amount", 70 | "fieldtype": "Float", 71 | "hidden": 0, 72 | "label": "Amount", 73 | "max_length": 0, 74 | "max_value": 0, 75 | "read_only": 1, 76 | "reqd": 0 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/certification_application/certification_application.py: -------------------------------------------------------------------------------- 1 | def get_context(context): 2 | # do your magic here 3 | pass 4 | -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/certification_application_usd/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/web_form/certification_application_usd/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/certification_application_usd/certification_application_usd.js: -------------------------------------------------------------------------------- 1 | frappe.ready(function() { 2 | // bind events here 3 | $(".page-header-actions-block .btn-primary, .page-header-actions-block .btn-default").addClass('hidden'); 4 | $(".text-right .btn-primary").addClass('hidden'); 5 | 6 | if (frappe.utils.get_url_arg('name')) { 7 | $('.page-content .btn-form-submit').addClass('hidden'); 8 | } else { 9 | user_name = frappe.full_name 10 | user_email_id = frappe.session.user 11 | $('[data-fieldname="currency"]').val("USD"); 12 | $('[data-fieldname="name_of_applicant"]').val(user_name); 13 | $('[data-fieldname="email"]').val(user_email_id); 14 | $('[data-fieldname="amount"]').val(300); 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/certification_application_usd/certification_application_usd.json: -------------------------------------------------------------------------------- 1 | { 2 | "accept_payment": 1, 3 | "allow_comments": 0, 4 | "allow_delete": 0, 5 | "allow_edit": 0, 6 | "allow_incomplete": 0, 7 | "allow_multiple": 1, 8 | "allow_print": 0, 9 | "amount": 0.0, 10 | "amount_based_on_field": 1, 11 | "amount_field": "amount", 12 | "creation": "2018-06-13 09:22:48.262441", 13 | "currency": "USD", 14 | "doc_type": "Certification Application", 15 | "docstatus": 0, 16 | "doctype": "Web Form", 17 | "idx": 0, 18 | "introduction_text": "", 19 | "is_standard": 1, 20 | "login_required": 1, 21 | "max_attachment_size": 0, 22 | "modified": "2018-06-13 09:26:35.502064", 23 | "modified_by": "Administrator", 24 | "module": "Non Profit", 25 | "name": "certification-application-usd", 26 | "owner": "Administrator", 27 | "payment_button_help": "Pay for your certification using PayPal", 28 | "payment_button_label": "Pay Now", 29 | "payment_gateway": "PayPal", 30 | "published": 1, 31 | "route": "certification-application-usd", 32 | "show_sidebar": 1, 33 | "sidebar_items": [], 34 | "success_url": "/certification-application-usd", 35 | "title": "Certification Application USD", 36 | "web_form_fields": [ 37 | { 38 | "fieldname": "name_of_applicant", 39 | "fieldtype": "Data", 40 | "hidden": 0, 41 | "label": "Name of Applicant", 42 | "max_length": 0, 43 | "max_value": 0, 44 | "read_only": 0, 45 | "reqd": 0 46 | }, 47 | { 48 | "fieldname": "email", 49 | "fieldtype": "Link", 50 | "hidden": 0, 51 | "label": "Email", 52 | "max_length": 0, 53 | "max_value": 0, 54 | "options": "User", 55 | "read_only": 1, 56 | "reqd": 1 57 | }, 58 | { 59 | "fieldname": "currency", 60 | "fieldtype": "Select", 61 | "hidden": 0, 62 | "label": "Currency", 63 | "max_length": 0, 64 | "max_value": 0, 65 | "options": "USD\nINR", 66 | "read_only": 1, 67 | "reqd": 0 68 | }, 69 | { 70 | "fieldname": "amount", 71 | "fieldtype": "Float", 72 | "hidden": 0, 73 | "label": "Amount", 74 | "max_length": 0, 75 | "max_value": 0, 76 | "read_only": 1, 77 | "reqd": 0 78 | } 79 | ] 80 | } -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/certification_application_usd/certification_application_usd.py: -------------------------------------------------------------------------------- 1 | def get_context(context): 2 | # do your magic here 3 | pass 4 | -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/grant_application/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/non_profit/web_form/grant_application/__init__.py -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/grant_application/grant_application.js: -------------------------------------------------------------------------------- 1 | frappe.ready(function() { 2 | // bind events here 3 | }); 4 | -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/grant_application/grant_application.json: -------------------------------------------------------------------------------- 1 | { 2 | "accept_payment": 0, 3 | "allow_comments": 0, 4 | "allow_delete": 1, 5 | "allow_edit": 1, 6 | "allow_incomplete": 0, 7 | "allow_multiple": 1, 8 | "allow_print": 0, 9 | "amount": 0.0, 10 | "amount_based_on_field": 0, 11 | "creation": "2017-10-30 15:57:10.825188", 12 | "currency": "INR", 13 | "doc_type": "Grant Application", 14 | "docstatus": 0, 15 | "doctype": "Web Form", 16 | "idx": 0, 17 | "introduction_text": "Share as many details as you can to get quick response from organization", 18 | "is_standard": 1, 19 | "login_required": 1, 20 | "max_attachment_size": 0, 21 | "modified": "2017-12-06 12:32:16.893289", 22 | "modified_by": "Administrator", 23 | "module": "Non Profit", 24 | "name": "grant-application", 25 | "owner": "Administrator", 26 | "payment_button_label": "Buy Now", 27 | "published": 1, 28 | "route": "my-grant", 29 | "show_sidebar": 1, 30 | "sidebar_items": [], 31 | "success_url": "/grant-application", 32 | "title": "Grant Application", 33 | "web_form_fields": [ 34 | { 35 | "fieldname": "applicant_type", 36 | "fieldtype": "Select", 37 | "hidden": 0, 38 | "label": "Applicant Type", 39 | "max_length": 0, 40 | "max_value": 0, 41 | "options": "Individual\nOrganization", 42 | "read_only": 0, 43 | "reqd": 1 44 | }, 45 | { 46 | "fieldname": "applicant_name", 47 | "fieldtype": "Data", 48 | "hidden": 0, 49 | "label": "Name", 50 | "max_length": 0, 51 | "max_value": 0, 52 | "read_only": 0, 53 | "reqd": 1 54 | }, 55 | { 56 | "fieldname": "email", 57 | "fieldtype": "Data", 58 | "hidden": 0, 59 | "label": "Email Address", 60 | "max_length": 0, 61 | "max_value": 0, 62 | "read_only": 0, 63 | "reqd": 1 64 | }, 65 | { 66 | "description": "", 67 | "fieldname": "grant_description", 68 | "fieldtype": "Text", 69 | "hidden": 0, 70 | "label": "Please outline your current situation and why you are applying for a grant?", 71 | "max_length": 0, 72 | "max_value": 0, 73 | "read_only": 0, 74 | "reqd": 1 75 | }, 76 | { 77 | "fieldname": "amount", 78 | "fieldtype": "Float", 79 | "hidden": 0, 80 | "label": "Requested Amount", 81 | "max_length": 0, 82 | "max_value": 0, 83 | "read_only": 0, 84 | "reqd": 0 85 | }, 86 | { 87 | "fieldname": "has_any_past_grant_record", 88 | "fieldtype": "Check", 89 | "hidden": 0, 90 | "label": "Have you received any grant from us before?", 91 | "max_length": 0, 92 | "max_value": 0, 93 | "options": "", 94 | "read_only": 0, 95 | "reqd": 0 96 | }, 97 | { 98 | "fieldname": "published", 99 | "fieldtype": "Check", 100 | "hidden": 0, 101 | "label": "Show on Website", 102 | "max_length": 0, 103 | "max_value": 0, 104 | "read_only": 0, 105 | "reqd": 0 106 | } 107 | ] 108 | } -------------------------------------------------------------------------------- /non_profit/non_profit/web_form/grant_application/grant_application.py: -------------------------------------------------------------------------------- 1 | def get_context(context): 2 | context.no_cache = True 3 | context.parents = [dict(label='View All ', 4 | route='grant-application', title='View All')] 5 | -------------------------------------------------------------------------------- /non_profit/non_profit/workspace/non_profit/non_profit.json: -------------------------------------------------------------------------------- 1 | { 2 | "charts": [], 3 | "content": "[{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Member\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Non Profit Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Membership\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chapter\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chapter Member\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Loan Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Grant Application\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Membership\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Volunteer\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Chapter\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Donation\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tax Exemption Certification (India)\",\"col\":4}}]", 4 | "creation": "2020-03-02 17:23:47.811421", 5 | "docstatus": 0, 6 | "doctype": "Workspace", 7 | "for_user": "", 8 | "hide_custom": 0, 9 | "icon": "non-profit", 10 | "idx": 0, 11 | "label": "Non Profit", 12 | "links": [ 13 | { 14 | "hidden": 0, 15 | "is_query_report": 0, 16 | "label": "Loan Management", 17 | "link_count": 0, 18 | "onboard": 0, 19 | "type": "Card Break" 20 | }, 21 | { 22 | "dependencies": "", 23 | "hidden": 0, 24 | "is_query_report": 0, 25 | "label": "Loan Type", 26 | "link_count": 0, 27 | "link_to": "Loan Type", 28 | "link_type": "DocType", 29 | "onboard": 0, 30 | "type": "Link" 31 | }, 32 | { 33 | "dependencies": "", 34 | "hidden": 0, 35 | "is_query_report": 0, 36 | "label": "Loan Application", 37 | "link_count": 0, 38 | "link_to": "Loan Application", 39 | "link_type": "DocType", 40 | "onboard": 0, 41 | "type": "Link" 42 | }, 43 | { 44 | "dependencies": "", 45 | "hidden": 0, 46 | "is_query_report": 0, 47 | "label": "Loan", 48 | "link_count": 0, 49 | "link_to": "Loan", 50 | "link_type": "DocType", 51 | "onboard": 0, 52 | "type": "Link" 53 | }, 54 | { 55 | "hidden": 0, 56 | "is_query_report": 0, 57 | "label": "Grant Application", 58 | "link_count": 0, 59 | "onboard": 0, 60 | "type": "Card Break" 61 | }, 62 | { 63 | "dependencies": "", 64 | "hidden": 0, 65 | "is_query_report": 0, 66 | "label": "Grant Application", 67 | "link_count": 0, 68 | "link_to": "Grant Application", 69 | "link_type": "DocType", 70 | "onboard": 0, 71 | "type": "Link" 72 | }, 73 | { 74 | "hidden": 0, 75 | "is_query_report": 0, 76 | "label": "Membership", 77 | "link_count": 0, 78 | "onboard": 0, 79 | "type": "Card Break" 80 | }, 81 | { 82 | "dependencies": "", 83 | "hidden": 0, 84 | "is_query_report": 0, 85 | "label": "Member", 86 | "link_count": 0, 87 | "link_to": "Member", 88 | "link_type": "DocType", 89 | "onboard": 1, 90 | "type": "Link" 91 | }, 92 | { 93 | "dependencies": "", 94 | "hidden": 0, 95 | "is_query_report": 0, 96 | "label": "Membership", 97 | "link_count": 0, 98 | "link_to": "Membership", 99 | "link_type": "DocType", 100 | "onboard": 1, 101 | "type": "Link" 102 | }, 103 | { 104 | "dependencies": "", 105 | "hidden": 0, 106 | "is_query_report": 0, 107 | "label": "Membership Type", 108 | "link_count": 0, 109 | "link_to": "Membership Type", 110 | "link_type": "DocType", 111 | "onboard": 0, 112 | "type": "Link" 113 | }, 114 | { 115 | "dependencies": "", 116 | "hidden": 0, 117 | "is_query_report": 0, 118 | "label": "Non Profit Settings", 119 | "link_count": 0, 120 | "link_to": "Non Profit Settings", 121 | "link_type": "DocType", 122 | "onboard": 0, 123 | "type": "Link" 124 | }, 125 | { 126 | "hidden": 0, 127 | "is_query_report": 0, 128 | "label": "Volunteer", 129 | "link_count": 0, 130 | "onboard": 0, 131 | "type": "Card Break" 132 | }, 133 | { 134 | "dependencies": "", 135 | "hidden": 0, 136 | "is_query_report": 0, 137 | "label": "Volunteer", 138 | "link_count": 0, 139 | "link_to": "Volunteer", 140 | "link_type": "DocType", 141 | "onboard": 1, 142 | "type": "Link" 143 | }, 144 | { 145 | "dependencies": "", 146 | "hidden": 0, 147 | "is_query_report": 0, 148 | "label": "Volunteer Type", 149 | "link_count": 0, 150 | "link_to": "Volunteer Type", 151 | "link_type": "DocType", 152 | "onboard": 0, 153 | "type": "Link" 154 | }, 155 | { 156 | "hidden": 0, 157 | "is_query_report": 0, 158 | "label": "Chapter", 159 | "link_count": 0, 160 | "onboard": 0, 161 | "type": "Card Break" 162 | }, 163 | { 164 | "dependencies": "", 165 | "hidden": 0, 166 | "is_query_report": 0, 167 | "label": "Chapter", 168 | "link_count": 0, 169 | "link_to": "Chapter", 170 | "link_type": "DocType", 171 | "onboard": 1, 172 | "type": "Link" 173 | }, 174 | { 175 | "hidden": 0, 176 | "is_query_report": 0, 177 | "label": "Donation", 178 | "link_count": 0, 179 | "onboard": 0, 180 | "type": "Card Break" 181 | }, 182 | { 183 | "dependencies": "", 184 | "hidden": 0, 185 | "is_query_report": 0, 186 | "label": "Donor", 187 | "link_count": 0, 188 | "link_to": "Donor", 189 | "link_type": "DocType", 190 | "onboard": 0, 191 | "type": "Link" 192 | }, 193 | { 194 | "dependencies": "", 195 | "hidden": 0, 196 | "is_query_report": 0, 197 | "label": "Donor Type", 198 | "link_count": 0, 199 | "link_to": "Donor Type", 200 | "link_type": "DocType", 201 | "onboard": 0, 202 | "type": "Link" 203 | }, 204 | { 205 | "hidden": 0, 206 | "is_query_report": 0, 207 | "label": "Donation", 208 | "link_count": 0, 209 | "link_to": "Donation", 210 | "link_type": "DocType", 211 | "onboard": 0, 212 | "type": "Link" 213 | }, 214 | { 215 | "hidden": 0, 216 | "is_query_report": 0, 217 | "label": "Tax Exemption Certification (India)", 218 | "link_count": 0, 219 | "link_type": "DocType", 220 | "onboard": 0, 221 | "type": "Card Break" 222 | }, 223 | { 224 | "hidden": 0, 225 | "is_query_report": 0, 226 | "label": "Tax Exemption 80G Certificate", 227 | "link_count": 0, 228 | "link_to": "Tax Exemption 80G Certificate", 229 | "link_type": "DocType", 230 | "onboard": 0, 231 | "type": "Link" 232 | } 233 | ], 234 | "modified": "2022-05-09 17:40:50.220877", 235 | "modified_by": "Administrator", 236 | "module": "Non Profit", 237 | "name": "Non Profit", 238 | "owner": "Administrator", 239 | "parent_page": "", 240 | "public": 1, 241 | "restrict_to_domain": "Non Profit", 242 | "roles": [], 243 | "sequence_id": 18.0, 244 | "shortcuts": [ 245 | { 246 | "label": "Member", 247 | "link_to": "Member", 248 | "type": "DocType" 249 | }, 250 | { 251 | "label": "Non Profit Settings", 252 | "link_to": "Non Profit Settings", 253 | "type": "DocType" 254 | }, 255 | { 256 | "label": "Membership", 257 | "link_to": "Membership", 258 | "type": "DocType" 259 | }, 260 | { 261 | "label": "Chapter", 262 | "link_to": "Chapter", 263 | "type": "DocType" 264 | }, 265 | { 266 | "label": "Chapter Member", 267 | "link_to": "Chapter Member", 268 | "type": "DocType" 269 | } 270 | ], 271 | "title": "Non Profit" 272 | } -------------------------------------------------------------------------------- /non_profit/patches.txt: -------------------------------------------------------------------------------- 1 | non_profit.patches.rename_non_profit_fields 2 | -------------------------------------------------------------------------------- /non_profit/patches/rename_non_profit_fields.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.model.utils.rename_field import rename_field 3 | 4 | def execute(): 5 | if frappe.db.table_exists("Donation"): 6 | frappe.reload_doc("non_profit", "doctype", "Donation") 7 | 8 | rename_field("Donation", "razorpay_payment_id", "payment_id") 9 | 10 | if frappe.db.table_exists("Tax Exemption 80G Certificate"): 11 | frappe.reload_doc("non_profit", "doctype", "Tax Exemption 80G Certificate") 12 | frappe.reload_doc("non_profit", "doctype", "Tax Exemption 80G Certificate Detail") 13 | 14 | rename_field("Tax Exemption 80G Certificate", "razorpay_payment_id", "payment_id") 15 | rename_field("Tax Exemption 80G Certificate Detail", "razorpay_payment_id", "payment_id") -------------------------------------------------------------------------------- /non_profit/setup.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | from frappe.desk.page.setup_wizard.setup_wizard import make_records 3 | from frappe.custom.doctype.custom_field.custom_field import create_custom_fields 4 | 5 | 6 | def make_custom_records(): 7 | records = [ 8 | {'doctype': "Party Type", "party_type": "Member", "account_type": "Receivable"}, 9 | {'doctype': "Party Type", "party_type": "Donor", "account_type": "Receivable"}, 10 | ] 11 | make_records(records) 12 | 13 | 14 | def setup_non_profit(): 15 | make_custom_records() 16 | make_custom_fields() 17 | 18 | has_domain = frappe.get_doc({ 19 | 'doctype': 'Has Domain', 20 | 'parent': 'Domain Settings', 21 | 'parentfield': 'active_domains', 22 | 'parenttype': 'Domain Settings', 23 | 'domain': 'Non Profit', 24 | }) 25 | has_domain.save() 26 | 27 | domain = frappe.get_doc('Domain', 'Non Profit') 28 | domain.setup_domain() 29 | 30 | domain_settings = frappe.get_single('Domain Settings') 31 | domain_settings.append('active_domains', dict(domain=domain)) 32 | frappe.clear_cache() 33 | 34 | 35 | data = { 36 | 'on_setup': 'non_profit.setup.setup_non_profit' 37 | } 38 | 39 | 40 | def make_custom_fields(update=True): 41 | custom_fields = get_custom_fields() 42 | create_custom_fields(custom_fields, update=update) 43 | 44 | 45 | def get_custom_fields(): 46 | custom_fields = { 47 | 'Company': [ 48 | dict(fieldname='non_profit_section', label='Non Profit Settings', 49 | fieldtype='Section Break', insert_after='asset_received_but_not_billed', collapsible=1), 50 | dict(fieldname='company_80g_number', label='80G Number', 51 | fieldtype='Data', insert_after='non_profit_section'), 52 | dict(fieldname='with_effect_from', label='80G With Effect From', 53 | fieldtype='Date', insert_after='company_80g_number'), 54 | dict(fieldname='non_profit_column_break', fieldtype='Column Break', 55 | insert_after='with_effect_from'), 56 | dict(fieldname='pan_details', label='PAN Number', 57 | fieldtype='Data', insert_after='with_effect_from') 58 | ], 59 | 'Member': [ 60 | { 61 | 'fieldname': 'pan_number', 62 | 'label': 'PAN Details', 63 | 'fieldtype': 'Data', 64 | 'insert_after': 'email_id' 65 | } 66 | ], 67 | 'Donor': [ 68 | { 69 | 'fieldname': 'pan_number', 70 | 'label': 'PAN Details', 71 | 'fieldtype': 'Data', 72 | 'insert_after': 'email' 73 | } 74 | ], 75 | } 76 | return custom_fields 77 | -------------------------------------------------------------------------------- /non_profit/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/templates/__init__.py -------------------------------------------------------------------------------- /non_profit/templates/pages/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/templates/pages/__init__.py -------------------------------------------------------------------------------- /non_profit/templates/pages/non_profit/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frappe/non_profit/ea2c88ddb04d60e7a81bfae58a1307d5f6496487/non_profit/templates/pages/non_profit/__init__.py -------------------------------------------------------------------------------- /non_profit/templates/pages/non_profit/join-chapter.html: -------------------------------------------------------------------------------- 1 | {% extends "templates/web.html" %} 2 | 3 | {% block page_content %} 4 | 5 | {% macro chapter_button() %} 6 |

7 | Go to Chapter Page

8 | {% endmacro %} 9 | {% if frappe.session.user=='Guest' %} 10 |

Please signup and login to join this chapter

11 |

Login

12 | {% else %} 13 | {% if already_member %} 14 |

You are already a member of {{ chapter.name }}!

15 | {{ chapter_button() }} 16 |

Leave Chapter

17 | {% else %} 18 | {% if request and request.method=='POST' %} 19 |

Welcome to chapter {{ chapter.name }}!

20 | {{ chapter_button() }} 21 | {% else %} 22 |
23 |
24 |
25 |
26 |
27 | 29 | 31 |
32 |
33 | 34 | 35 |
36 |
37 | 38 | 40 |
41 |
42 | 43 | 45 |
46 |
47 | 49 |
50 |
51 |
52 |
53 |
54 | {% endif %} 55 | {% endif %} 56 | 57 | {% endif %} 58 | 59 | {% endblock %} 60 | -------------------------------------------------------------------------------- /non_profit/templates/pages/non_profit/join_chapter.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, EOSSF and contributors 2 | // For license information, please see license.txt 3 | 4 | frappe.ui.form.on('Chapter Member', { 5 | onsubmit: function (frm) { 6 | console.log("here" + frappe.session.user) 7 | // body... 8 | } 9 | refresh: function(frm) { 10 | 11 | } 12 | }); 13 | -------------------------------------------------------------------------------- /non_profit/templates/pages/non_profit/join_chapter.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def get_context(context): 5 | context.no_cache = True 6 | chapter = frappe.get_doc('Chapter', frappe.form_dict.name) 7 | if frappe.session.user!='Guest': 8 | if frappe.session.user in [d.user for d in chapter.members if d.enabled == 1]: 9 | context.already_member = True 10 | else: 11 | if frappe.request.method=='GET': 12 | pass 13 | elif frappe.request.method=='POST': 14 | chapter.append('members', dict( 15 | user=frappe.session.user, 16 | introduction=frappe.form_dict.introduction, 17 | website_url=frappe.form_dict.website_url, 18 | enabled=1 19 | )) 20 | chapter.save(ignore_permissions=1) 21 | frappe.db.commit() 22 | 23 | context.chapter = chapter 24 | -------------------------------------------------------------------------------- /non_profit/templates/pages/non_profit/leave-chapter.html: -------------------------------------------------------------------------------- 1 | {% extends "templates/web.html" %} 2 | {% block page_content %} 3 | 4 | {% if member_deleted %} 5 |

You are not a member of {{ chapter.name }}!

6 |
7 |
8 |
9 | 10 | 11 |
12 | 14 |
15 |
16 |

Please signup and login to join this chapter

17 | 18 |

Become Member Again

19 | {% endif %} 20 | 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /non_profit/templates/pages/non_profit/leave_chapter.py: -------------------------------------------------------------------------------- 1 | import frappe 2 | 3 | 4 | def get_context(context): 5 | context.no_cache = True 6 | chapter = frappe.get_doc('Chapter', frappe.form_dict.name) 7 | context.member_deleted = True 8 | context.chapter = chapter 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # frappe -- https://github.com/frappe/frappe is installed via 'bench init' -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | with open("requirements.txt") as f: 4 | install_requires = f.read().strip().split("\n") 5 | 6 | # get version from __version__ variable in non_profit/__init__.py 7 | from non_profit import __version__ as version 8 | 9 | setup( 10 | name="non_profit", 11 | version=version, 12 | description="Non Profit", 13 | author="Frappe", 14 | author_email="pandikunta@frappe.io", 15 | packages=find_packages(), 16 | zip_safe=False, 17 | include_package_data=True, 18 | install_requires=install_requires 19 | ) 20 | --------------------------------------------------------------------------------