diff --git a/.deepsource.toml b/.deepsource.toml
index c2162e34..25bc3d76 100644
--- a/.deepsource.toml
+++ b/.deepsource.toml
@@ -6,4 +6,3 @@ enabled = true
[analyzers.meta]
runtime_version = "3.x.x"
-
diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
deleted file mode 100644
index 0a4c322e..00000000
--- a/.github/workflows/ci-tests.yml
+++ /dev/null
@@ -1,111 +0,0 @@
-# CI: sets up a Frappe bench and runs the PropMS test suite.
-#
-# Confirmed against the dev bench: frappe 15.100.1, erpnext 15.98.1
-# (both on version-15). ERPNext is required — PropMS's doc_events hook
-# directly into ERPNext doctypes (Sales Invoice, Journal Entry Account,
-# Material Request, Sales Order) even though it's not declared in
-# hooks.py required_apps.
-#
-# TODO before this will run green:
-# - Confirm the default/target branches under `on:` match your repo
-# (develop vs main).
-# - Confirm Python version (`python --version` in the bench).
-
-name: CI
-
-on:
- pull_request:
- branches: [develop, main]
- push:
- branches: [develop, main]
-
-env:
- FRAPPE_BRANCH: version-15
- ERPNEXT_BRANCH: version-15 # only used if the get-app step below is uncommented
-
-jobs:
- tests:
- runs-on: ubuntu-latest
-
- services:
- mysql:
- image: mariadb:10.6
- env:
- MYSQL_ROOT_PASSWORD: root
- ports:
- - 3306:3306
- options: >-
- --health-cmd="mysqladmin ping"
- --health-interval=10s
- --health-timeout=5s
- --health-retries=5
- redis-cache:
- image: redis:alpine
- ports:
- - 13000:6379
- redis-queue:
- image: redis:alpine
- ports:
- - 11000:6379
-
- steps:
- - name: Checkout PropMS
- uses: actions/checkout@v4
- with:
- path: apps/propms
-
- - name: Setup Python
- uses: actions/setup-python@v5
- with:
- python-version: "3.11" # TODO: confirm
-
- - name: Setup Node
- uses: actions/setup-node@v4
- with:
- node-version: "18"
-
- - name: Install system dependencies
- run: |
- sudo apt-get update
- sudo apt-get install -y wkhtmltopdf mariadb-client
-
- - name: Install bench
- run: pip install frappe-bench
-
- - name: Init bench
- run: |
- bench init frappe-bench \
- --frappe-branch ${{ env.FRAPPE_BRANCH }} \
- --skip-redis-config-generation \
- --skip-assets \
- --python "$(which python)"
-
- - name: Get ERPNext
- working-directory: frappe-bench
- run: bench get-app erpnext --branch ${{ env.ERPNEXT_BRANCH }}
-
- - name: Get PropMS
- working-directory: frappe-bench
- run: bench get-app propms ../apps/propms
-
- - name: Configure bench for CI services
- working-directory: frappe-bench
- run: |
- bench set-mariadb-host 127.0.0.1
- bench set-redis-cache-host redis://localhost:13000
- bench set-redis-queue-host redis://localhost:11000
- bench set-redis-socketio-host redis://localhost:11000
- sed -i 's/redis_socketio/redis_queue/g' sites/common_site_config.json || true
-
- - name: Create test site
- working-directory: frappe-bench
- run: |
- bench new-site test_site \
- --mariadb-root-password root \
- --admin-password admin \
- --no-mariadb-socket
- bench --site test_site install-app propms
-
- - name: Run PropMS tests
- working-directory: frappe-bench
- run: bench --site test_site run-tests --app propms
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index f2ee62d5..00000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,89 +0,0 @@
-name: CI
-
-on:
- push:
- branches:
- - develop
- pull_request:
-
-concurrency:
- group: develop-propms-${{ github.event.number }}
- cancel-in-progress: true
-
-jobs:
- tests:
- runs-on: ubuntu-latest
- name: Server
-
- services:
- redis-cache:
- image: redis:alpine
- ports:
- - 13000:6379
- redis-queue:
- image: redis:alpine
- ports:
- - 11000:6379
- mariadb:
- image: mariadb:10.6
- env:
- MYSQL_ROOT_PASSWORD: root
- ports:
- - 3306:3306
- options: --health-cmd="mariadb-admin ping" --health-interval=5s --health-timeout=2s --health-retries=3
-
- steps:
- - name: Clone
- uses: actions/checkout@v4
-
- - name: Setup Python
- uses: actions/setup-python@v5
- with:
- python-version: '3.10'
-
- - name: Setup Node
- uses: actions/setup-node@v4
- with:
- node-version: 18
- check-latest: true
-
- - name: Cache pip
- uses: actions/cache@v4
- with:
- path: ~/.cache/pip
- key: ${{ runner.os }}-pip-${{ hashFiles('**/*requirements.txt', '**/pyproject.toml', '**/setup.py', '**/setup.cfg') }}
- restore-keys: |
- ${{ runner.os }}-pip-
- ${{ runner.os }}-
-
- - name: Install MariaDB Client
- run: sudo apt-get install -y mariadb-client
-
- - name: Setup
- run: |
- pip install frappe-bench
- bench init --skip-redis-config-generation --skip-assets --frappe-branch version-15 --python "$(which python)" ~/frappe-bench
- mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
- mariadb --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
-
- - name: Install
- working-directory: /home/runner/frappe-bench
- run: |
- bench get-app --skip-assets erpnext --branch version-15 --resolve-deps
- bench get-app --skip-assets propms $GITHUB_WORKSPACE --resolve-deps
- bench setup requirements --dev
- bench new-site --db-root-password root --admin-password admin test_site
- bench --site test_site install-app erpnext
- bench --site test_site install-app propms
- env:
- CI: 'Yes'
-
- - name: Smoke Test
- working-directory: /home/runner/frappe-bench
- run: |
- bench --site test_site set-config allow_tests true
- bench --site test_site execute erpnext.setup.utils.before_tests
- bench --site test_site migrate
- bench --site test_site list-apps
- env:
- TYPE: server
diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml
index 7a897352..57d5a5ef 100644
--- a/.github/workflows/linter.yml
+++ b/.github/workflows/linter.yml
@@ -8,51 +8,55 @@ permissions:
contents: read
concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
+ group: linters-propms-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
- linter:
- name: 'Frappe Linter'
+ semgrep:
+ name: Frappe Linter
runs-on: ubuntu-latest
- if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
+
- uses: actions/setup-python@v5
with:
- python-version: '3.10'
+ python-version: "3.11"
cache: pip
- - name: Install pre-commit
- run: pip install pre-commit
- - name: Run pre-commit on changed files
- run: |
- pre-commit run \
- --show-diff-on-failure \
- --color=always \
- --from-ref origin/${{ github.base_ref }} \
- --to-ref HEAD
- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
+ - name: Install Semgrep
+ run: pip install semgrep
+
+ # Blocking: real bugs and security issues only
- name: Run Semgrep rules
run: |
- pip install semgrep
- semgrep ci --config ./frappe-semgrep-rules/rules --config r/python.lang.correctness
+ semgrep scan --config ./frappe-semgrep-rules/rules \
+ --config r/python.lang.security \
+ --severity=ERROR --error propms
+
+ # Informational: style and i18n warnings, never fails the build
+ - name: Semgrep warnings (non-blocking)
+ if: always()
+ run: |
+ semgrep scan --config ./frappe-semgrep-rules/rules \
+ --config r/python.lang.security \
+ --severity=WARNING propms || true
deps-vulnerable-check:
- name: 'Vulnerable Dependency Check'
+ name: Vulnerable Dependency Check
runs-on: ubuntu-latest
steps:
+ - uses: actions/checkout@v4
+
- uses: actions/setup-python@v5
with:
- python-version: '3.10'
-
- - uses: actions/checkout@v4
+ python-version: "3.11"
- name: Cache pip
uses: actions/cache@v4
@@ -66,5 +70,4 @@ jobs:
- name: Install and run pip-audit
run: |
pip install pip-audit
- cd ${GITHUB_WORKSPACE}
pip-audit --desc on .
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
new file mode 100644
index 00000000..a1834900
--- /dev/null
+++ b/.github/workflows/pre-commit.yml
@@ -0,0 +1,31 @@
+name: Pre-commit
+
+on:
+ pull_request:
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+concurrency:
+ group: precommit-propms-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ pre-commit:
+ name: pre-commit
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.11"
+ cache: pip
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - uses: pre-commit/action@v3.0.1
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
deleted file mode 100644
index e2383439..00000000
--- a/.github/workflows/release.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: Generate Semantic Release
-
-on:
- workflow_dispatch:
-
-permissions:
- contents: write
- issues: write
- pull-requests: write
-
-concurrency:
- group: release-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- release:
- name: Release
- runs-on: ubuntu-latest
- steps:
- - name: Checkout Entire Repository
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
- persist-credentials: false
-
- - name: Setup Node.js
- uses: actions/setup-node@v4
- with:
- node-version: 20
-
- - name: Setup dependencies
- run: |
- npm install @semantic-release/git @semantic-release/exec --no-save
-
- - name: Create Release
- env:
- GH_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
- GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN || secrets.GITHUB_TOKEN }}
- GIT_AUTHOR_NAME: "Aakvatech Release Bot"
- GIT_AUTHOR_EMAIL: "info@aakvatech.com"
- GIT_COMMITTER_NAME: "Aakvatech Release Bot"
- GIT_COMMITTER_EMAIL: "info@aakvatech.com"
- run: npx semantic-release
diff --git a/.github/workflows/semantic-commits.yml b/.github/workflows/semantic-commits.yml
new file mode 100644
index 00000000..a3024790
--- /dev/null
+++ b/.github/workflows/semantic-commits.yml
@@ -0,0 +1,31 @@
+name: Semantic Commits
+
+on:
+ pull_request: {}
+
+permissions:
+ contents: read
+
+concurrency:
+ group: commitcheck-propms-${{ github.event.number }}
+ cancel-in-progress: true
+
+jobs:
+ commitlint:
+ name: Check Commit Messages
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 200
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ check-latest: true
+
+ - name: Check commit messages
+ run: |
+ npm install @commitlint/cli @commitlint/config-conventional conventional-changelog-conventionalcommits
+ npx commitlint --verbose --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }}
diff --git a/.gitignore b/.gitignore
index 70899f1a..115e87b6 100755
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,6 @@
*.egg-info
*.swp
tags
-propms/docs/current
\ No newline at end of file
+propms/docs/current
+# Cloned by the frappe-semgrep-rules pre-commit hook
+frappe-semgrep-rules/
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 6f522dfe..6f82128f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -1,5 +1,6 @@
-exclude: 'node_modules|.git'
+exclude: "^(node_modules/|frappe-semgrep-rules/|[.]vscode/|.*/node_modules/)"
default_stages: [pre-commit]
+default_install_hook_types: [pre-commit, commit-msg, pre-push]
fail_fast: false
repos:
@@ -7,20 +8,74 @@ repos:
rev: v5.0.0
hooks:
- id: trailing-whitespace
- exclude: ".*json$|.*txt$|.*csv|.*md|.*svg"
+ exclude: '\.(json|txt|csv|md|svg)$'
- id: end-of-file-fixer
- exclude: ".*json$|.*csv"
+ exclude: '\.(json|csv|svg)$'
- id: check-merge-conflict
- id: check-ast
- id: check-json
- id: check-toml
- id: check-yaml
+ - id: debug-statements
+ - id: no-commit-to-branch
+ args:
+ - --branch
+ - main
+ - --branch
+ - master
+ - --branch
+ - production
+ - --branch
+ - version-14
+ - --branch
+ - version-15
+ - --branch
+ - version-16
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.8.1
+ rev: v0.13.2
hooks:
- id: ruff
- name: "ruff lint"
+ name: "Run ruff import sorter"
+ args: ["--select=I", "--fix"]
+ files: '^propms/.*\.py$'
+ - id: ruff
+ name: "Run ruff linter"
args: ["--fix"]
+ files: '^propms/.*\.py$'
- id: ruff-format
- name: "ruff format"
+ name: "Run ruff formatter"
+ files: '^propms/.*\.py$'
+
+ - repo: local
+ hooks:
+ - id: frappe-semgrep-rules
+ name: "Frappe Semgrep Security Rules"
+ entry: bash -c 'if [ ! -d frappe-semgrep-rules/.git ]; then rm -rf frappe-semgrep-rules && GIT_TEMPLATE_DIR="" git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules; fi && semgrep scan --config ./frappe-semgrep-rules/rules --config r/python.lang.security --severity=ERROR --error --quiet "$@"' --
+ language: python
+ additional_dependencies: ["semgrep"]
+ types: [python]
+ files: '^propms/.*\.py$'
+ pass_filenames: true
+ require_serial: true
+
+ - id: full-repository-check
+ name: "Full repository check before push"
+ entry: bash -c 'if command -v pre-commit >/dev/null 2>&1; then exec pre-commit run --all-files --hook-stage pre-commit --show-diff-on-failure --color=always; else exec python3 -m pre_commit run --all-files --hook-stage pre-commit --show-diff-on-failure --color=always; fi'
+ language: system
+ stages: [pre-push]
+ pass_filenames: false
+ always_run: true
+ verbose: true
+
+ - repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
+ rev: v9.22.0
+ hooks:
+ - id: commitlint
+ stages: [commit-msg]
+ additional_dependencies: ["@commitlint/config-conventional"]
+
+ci:
+ autoupdate_schedule: weekly
+ skip: [frappe-semgrep-rules, full-repository-check]
+ submodules: false
diff --git a/.releaserc.json b/.releaserc.json
deleted file mode 100644
index 5b37bb96..00000000
--- a/.releaserc.json
+++ /dev/null
@@ -1,29 +0,0 @@
-{
- "branches": ["version-15"],
- "plugins": [
- [
- "@semantic-release/commit-analyzer",
- {
- "preset": "angular",
- "releaseRules": [
- { "breaking": true, "release": false }
- ]
- }
- ],
- "@semantic-release/release-notes-generator",
- [
- "@semantic-release/exec",
- {
- "prepareCmd": "sed -ir -E \"s/\\\"[0-9]+\\.[0-9]+\\.[0-9]+\\\"/\\\"${nextRelease.version}\\\"/\" propms/__init__.py"
- }
- ],
- [
- "@semantic-release/git",
- {
- "assets": ["propms/__init__.py"],
- "message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
- }
- ],
- "@semantic-release/github"
- ]
-}
diff --git a/LICENSE b/LICENSE
index 1a5b374d..5852795b 100755
--- a/LICENSE
+++ b/LICENSE
@@ -1 +1 @@
-License: GPL
\ No newline at end of file
+License: GPL
diff --git a/MANIFEST.in b/MANIFEST.in
index 782f0182..b4b3f3fa 100755
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -15,4 +15,4 @@ recursive-include propms *.png
recursive-include propms *.py
recursive-include propms *.svg
recursive-include propms *.txt
-recursive-exclude propms *.pyc
\ No newline at end of file
+recursive-exclude propms *.pyc
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 00000000..300da21e
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1,13 @@
+module.exports = {
+ extends: ["@commitlint/config-conventional"],
+ rules: {
+ "subject-empty": [2, "never"],
+ "type-case": [2, "always", "lower-case"],
+ "type-empty": [2, "never"],
+ "type-enum": [
+ 2,
+ "always",
+ ["build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"],
+ ],
+ },
+};
diff --git a/propms/__init__.py b/propms/__init__.py
index 0eaffdf5..1609d49b 100755
--- a/propms/__init__.py
+++ b/propms/__init__.py
@@ -1,4 +1 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
__version__ = "15.3.0"
diff --git a/propms/auto_custom.py b/propms/auto_custom.py
index 780886d9..045aa198 100755
--- a/propms/auto_custom.py
+++ b/propms/auto_custom.py
@@ -1,239 +1,226 @@
-from __future__ import unicode_literals
-from datetime import datetime
-from erpnext.controllers.accounts_controller import get_taxes_and_charges
-from frappe.model.mapper import get_mapped_doc
-from frappe.utils import add_days, today, date_diff, getdate, add_months
-from propms.lease_invoice import getDueDate
import calendar
+import traceback
+from datetime import datetime
+
import frappe
import frappe.permissions
import frappe.share
-import traceback
+from erpnext.controllers.accounts_controller import get_taxes_and_charges
from frappe import _
+from frappe.model.mapper import get_mapped_doc
+from frappe.utils import add_days, add_months, date_diff, getdate, today
+
+from propms.lease_invoice import getDueDate
@frappe.whitelist()
def app_error_log(title, error):
- frappe.throw(
- msg=error,
- exc=traceback.format_exc(),
- title=str("User:") + str(title),
- is_minimizable=None,
- )
+ frappe.throw(
+ msg=error,
+ exc=traceback.format_exc(),
+ title="User:" + str(title),
+ is_minimizable=None,
+ )
@frappe.whitelist()
def makeSalesInvoice(self, method):
- try:
- if self.doctype == "Stock Entry":
- return
- if (
- self.doctype == "Material Request"
- and self.material_request_type == "Material Issue"
- ):
-
- if self.status == "Issued":
- result = checkIssue(self.name)
- if result:
- items = []
- issue_details = frappe.get_doc("Issue", result)
- if issue_details.customer:
- material_request_details = frappe.get_doc(
- "Material Request", self.name
- )
- if not material_request_details.sales_invoice:
- if not len(material_request_details.items) == 0:
- for item in material_request_details.items:
- item_json = {}
- item_json["item_code"] = item.item_code
- item_json["qty"] = item.qty
- items.append(item_json)
- sales_invoice = frappe.get_doc(
- dict(
- doctype="Sales Invoice",
- company=self.company,
- fiscal_year=frappe.db.get_single_value(
- "Global Defaults", "current_fiscal_year"
- ),
- posting_date=today(),
- items=items,
- taxes_and_charges=frappe.get_value(
- "Company",
- self.company,
- "default_tax_template",
- ),
- customer=str(issue_details.customer),
- due_date=add_days(today(), 2),
- update_stock=1,
- )
- ).insert()
- if sales_invoice.name:
- assignInvoiceNameInMR(
- sales_invoice.name,
- material_request_details.name,
- )
- getTax(sales_invoice)
- sales_invoice.calculate_taxes_and_totals()
- changeStatusIssue(self.name, self.status)
- else:
- if self.customer:
- if not len(self.materials_required) == 0:
- items = []
- for row in self.materials_required:
- material_request_details = frappe.get_doc(
- "Material Request", row.material_request
- )
- if (
- material_request_details.status == "Issued"
- and not material_request_details.sales_invoice
- ):
-
- if not len(material_request_details.items) == 0:
- for item in material_request_details.items:
- item_json = {}
- item_json["item_code"] = item.item_code
- item_json["qty"] = item.qty
- items.append(item_json)
- sales_invoice = frappe.get_doc(
- dict(
- doctype="Sales Invoice",
- company=self.company,
- fiscal_year=frappe.db.get_single_value(
- "Global Defaults", "current_fiscal_year"
- ),
- posting_date=today(),
- items=items,
- taxes_and_charges=frappe.get_value(
- "Company",
- self.company,
- "default_tax_template",
- ),
- customer=str(self.customer),
- due_date=add_days(today(), 2),
- update_stock=1,
- )
- ).insert()
- if sales_invoice.name:
- assignInvoiceNameInMR(
- sales_invoice.name,
- material_request_details.name,
- )
- if sales_invoice.taxes_and_charges:
- getTax(sales_invoice)
- sales_invoice.calculate_taxes_and_totals()
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
+ try:
+ if self.doctype == "Stock Entry":
+ return
+ if self.doctype == "Material Request" and self.material_request_type == "Material Issue":
+ if self.status == "Issued":
+ result = checkIssue(self.name)
+ if result:
+ items = []
+ issue_details = frappe.get_doc("Issue", result)
+ if issue_details.customer:
+ material_request_details = frappe.get_doc("Material Request", self.name)
+ if not material_request_details.sales_invoice:
+ if not len(material_request_details.items) == 0:
+ for item in material_request_details.items:
+ item_json = {}
+ item_json["item_code"] = item.item_code
+ item_json["qty"] = item.qty
+ items.append(item_json)
+ sales_invoice = frappe.get_doc(
+ dict(
+ doctype="Sales Invoice",
+ company=self.company,
+ fiscal_year=frappe.db.get_single_value(
+ "Global Defaults", "current_fiscal_year"
+ ),
+ posting_date=today(),
+ items=items,
+ taxes_and_charges=frappe.get_value(
+ "Company",
+ self.company,
+ "default_tax_template",
+ ),
+ customer=str(issue_details.customer),
+ due_date=add_days(today(), 2),
+ update_stock=1,
+ )
+ ).insert()
+ if sales_invoice.name:
+ assignInvoiceNameInMR(
+ sales_invoice.name,
+ material_request_details.name,
+ )
+ getTax(sales_invoice)
+ sales_invoice.calculate_taxes_and_totals()
+ changeStatusIssue(self.name, self.status)
+ else:
+ if self.customer:
+ if not len(self.materials_required) == 0:
+ items = []
+ for row in self.materials_required:
+ material_request_details = frappe.get_doc("Material Request", row.material_request)
+ if (
+ material_request_details.status == "Issued"
+ and not material_request_details.sales_invoice
+ ):
+ if not len(material_request_details.items) == 0:
+ for item in material_request_details.items:
+ item_json = {}
+ item_json["item_code"] = item.item_code
+ item_json["qty"] = item.qty
+ items.append(item_json)
+ sales_invoice = frappe.get_doc(
+ dict(
+ doctype="Sales Invoice",
+ company=self.company,
+ fiscal_year=frappe.db.get_single_value(
+ "Global Defaults", "current_fiscal_year"
+ ),
+ posting_date=today(),
+ items=items,
+ taxes_and_charges=frappe.get_value(
+ "Company",
+ self.company,
+ "default_tax_template",
+ ),
+ customer=str(self.customer),
+ due_date=add_days(today(), 2),
+ update_stock=1,
+ )
+ ).insert()
+ if sales_invoice.name:
+ assignInvoiceNameInMR(
+ sales_invoice.name,
+ material_request_details.name,
+ )
+ if sales_invoice.taxes_and_charges:
+ getTax(sales_invoice)
+ sales_invoice.calculate_taxes_and_totals()
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
def getTax(sales_invoice):
- taxes = get_taxes_and_charges(
- "Sales Taxes and Charges Template", sales_invoice.taxes_and_charges
- )
- for tax in taxes:
- sales_invoice.append("taxes", tax)
+ taxes = get_taxes_and_charges("Sales Taxes and Charges Template", sales_invoice.taxes_and_charges)
+ for tax in taxes:
+ sales_invoice.append("taxes", tax)
def checkIssue(name):
- data = frappe.db.sql(
- """select parent from `tabIssue Materials Detail` where material_request=%s""",
- name,
- )
- if data:
- if not data[0][0] is None:
- return data[0][0]
- else:
- return False
- else:
- return False
+ data = frappe.db.sql(
+ """select parent from `tabIssue Materials Detail` where material_request=%s""",
+ name,
+ )
+ if data:
+ if data[0][0] is not None:
+ return data[0][0]
+ else:
+ return False
+ else:
+ return False
def assignInvoiceNameInMR(invoice, pr):
- frappe.db.sql(
- """update `tabMaterial Request` set sales_invoice=%s where name=%s""",
- (invoice, pr),
- )
+ frappe.db.sql(
+ """update `tabMaterial Request` set sales_invoice=%s where name=%s""",
+ (invoice, pr),
+ )
@frappe.whitelist()
def changeStatusKeyset(self, method):
- try:
- keyset_name = getKeysetName(self.key_set)
- if keyset_name:
- doc = frappe.get_doc("Key Set", keyset_name)
- if self.returned:
- doc.status = "In"
- else:
- doc.status = "Out"
- doc.save()
- else:
- frappe.throw(_("Key set not found - {0}.").format(self.key_set))
-
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
+ try:
+ keyset_name = getKeysetName(self.key_set)
+ if keyset_name:
+ doc = frappe.get_doc("Key Set", keyset_name)
+ if self.returned:
+ doc.status = "In"
+ else:
+ doc.status = "Out"
+ doc.save()
+ else:
+ frappe.throw(_("Key set not found - {0}.").format(self.key_set))
+
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
def getKeysetName(name):
- data = frappe.db.sql("""select name from `tabKey Set` where name=%s""", name)
- if data:
- if not data[0][0] is None:
- return data[0][0]
- else:
- return False
- else:
- return False
+ data = frappe.db.sql("""select name from `tabKey Set` where name=%s""", name)
+ if data:
+ if data[0][0] is not None:
+ return data[0][0]
+ else:
+ return False
+ else:
+ return False
@frappe.whitelist()
def changeStatusIssue(name, status):
- try:
- issue_name = getIssueName(name)
- if issue_name:
- doc = frappe.get_doc("Issue Materials Detail", issue_name)
- doc.material_status = status
- doc.save()
+ try:
+ issue_name = getIssueName(name)
+ if issue_name:
+ doc = frappe.get_doc("Issue Materials Detail", issue_name)
+ doc.material_status = status
+ doc.save()
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
def getIssueName(name):
- data = frappe.db.sql(
- """select name from `tabIssue Materials Detail` where material_request=%s""",
- name,
- )
- if data:
- if not data[0][0] is None:
- return data[0][0]
- else:
- return False
- else:
- return False
+ data = frappe.db.sql(
+ """select name from `tabIssue Materials Detail` where material_request=%s""",
+ name,
+ )
+ if data:
+ if data[0][0] is not None:
+ return data[0][0]
+ else:
+ return False
+ else:
+ return False
@frappe.whitelist()
def validateSalesInvoiceItemDuplication(self, method):
- for item in self.items:
- for item_child in self.items:
- if not item.name == item_child.name:
- if item.item_code == item_child.item_code:
- frappe.throw(
- _("Duplicate Item Exists - {0}. Duplications are not allowed.").format(
- item.item_code
- )
- )
+ for item in self.items:
+ for item_child in self.items:
+ if not item.name == item_child.name:
+ if item.item_code == item_child.item_code:
+ frappe.throw(
+ _("Duplicate Item Exists - {0}. Duplications are not allowed.").format(item.item_code)
+ )
@frappe.whitelist()
def statusChangeBeforeLeaseExpire():
- try:
-
- # ---------------------------------------------
- # Mark properties as "Off Lease in 3 Months"
- # Only when lease *has* an end_date
- # and ends within next 3 months.
- # ---------------------------------------------
- upcoming_expiry = frappe.db.sql(
- """
+ try:
+ # ---------------------------------------------
+ # Mark properties as "Off Lease in 3 Months"
+ # Only when lease *has* an end_date
+ # and ends within next 3 months.
+ # ---------------------------------------------
+ upcoming_expiry = frappe.db.sql(
+ """
SELECT l.name, l.property, l.end_date
FROM `tabLease` l
INNER JOIN `tabProperty` p ON l.property = p.name
@@ -248,65 +235,62 @@ def statusChangeBeforeLeaseExpire():
AND l.end_date BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 3 MONTH)
AND p.status = 'On Lease'
""",
- as_dict=1,
- )
+ as_dict=1,
+ )
- for lease in upcoming_expiry:
- frappe.db.set_value(
- "Property", lease.property, "status", "Off Lease in 3 Months"
- )
+ for lease in upcoming_expiry:
+ frappe.db.set_value("Property", lease.property, "status", "Off Lease in 3 Months")
+
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def statusChangeAfterLeaseExpire():
- try:
- from frappe.query_builder import DocType
-
- Property = DocType('Property')
- Lease = DocType('Lease')
-
- # Get properties that might need status change
- properties = (
- frappe.qb.from_(Property)
- .select(Property.name)
- .where(Property.status.isin(['On Lease', 'Off Lease in 3 Months']))
- ).run(as_dict=True)
-
- properties_to_update = []
-
- for prop in properties:
- # Check if property has any active leases
- active_leases = (
- frappe.qb.from_(Lease)
- .select(Lease.name)
- .where(Lease.property == prop.name)
- .where(Lease.start_date <= frappe.utils.now())
- .where(
- (Lease.end_date >= frappe.utils.now()) |
- (Lease.end_date.isnull())
- )
- ).run()
+ try:
+ from frappe.query_builder import DocType
+
+ Property = DocType("Property")
+ Lease = DocType("Lease")
+
+ # Get properties that might need status change
+ properties = (
+ frappe.qb.from_(Property)
+ .select(Property.name)
+ .where(Property.status.isin(["On Lease", "Off Lease in 3 Months"]))
+ ).run(as_dict=True)
+
+ properties_to_update = []
+
+ for prop in properties:
+ # Check if property has any active leases
+ active_leases = (
+ frappe.qb.from_(Lease)
+ .select(Lease.name)
+ .where(Lease.property == prop.name)
+ .where(Lease.start_date <= frappe.utils.now())
+ .where((Lease.end_date >= frappe.utils.now()) | (Lease.end_date.isnull()))
+ ).run()
- # If no active leases, add to update list
- if not active_leases:
- properties_to_update.append(prop.name)
+ # If no active leases, add to update list
+ if not active_leases:
+ properties_to_update.append(prop.name)
- # Bulk update properties to Available
- if properties_to_update:
- frappe.qb.update(Property).set(Property.status, 'Available').where(
- Property.name.isin(properties_to_update)
- ).run()
+ # Bulk update properties to Available
+ if properties_to_update:
+ frappe.qb.update(Property).set(Property.status, "Available").where(
+ Property.name.isin(properties_to_update)
+ ).run()
+
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def update_property_status():
- try:
- active_lease_properties = frappe.db.sql(
- """
+ try:
+ active_lease_properties = frappe.db.sql(
+ """
SELECT DISTINCT p.name
FROM `tabProperty` p
INNER JOIN `tabLease` l ON l.property = p.name
@@ -314,374 +298,358 @@ def update_property_status():
AND l.start_date <= NOW()
AND l.lease_status = 'Active'
""",
- as_dict=1,
- )
- # frappe.throw(str(active_lease_properties))
+ as_dict=1,
+ )
+ # frappe.throw(str(active_lease_properties))
+
+ for row in active_lease_properties:
+ frappe.db.set_value("Property", row.name, "status", "On Lease")
+
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
- for row in active_lease_properties:
- frappe.db.set_value("Property", row.name, "status", "On Lease")
-
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def getCheckList():
- checklist_doc = frappe.get_doc("Checklist Checkup Area", "Takeover")
- if checklist_doc:
- check_list = []
- for task in checklist_doc.task:
- check = {}
- check["checklist_task"] = task.task_name
- check_list.append(check)
- return check_list
+ checklist_doc = frappe.get_doc("Checklist Checkup Area", "Takeover")
+ if checklist_doc:
+ check_list = []
+ for task in checklist_doc.task:
+ check = {}
+ check["checklist_task"] = task.task_name
+ check_list.append(check)
+ return check_list
@frappe.whitelist()
-def makeDailyCheckListForTakeover(
- source_name, target_doc=None, ignore_permissions=True
-):
- try:
-
- def set_missing_values(source, target):
- target.checkup_date = today()
- target.area = "Takeover"
-
- doclist = get_mapped_doc(
- "Lease",
- source_name,
- {
- "Lease": {
- "doctype": "Daily Checklist",
- "field_map": {"property": "property"},
- }
- },
- target_doc,
- set_missing_values,
- ignore_permissions=ignore_permissions,
- )
- return doclist
-
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
+def makeDailyCheckListForTakeover(source_name, target_doc=None, ignore_permissions=True):
+ try:
+
+ def set_missing_values(source, target):
+ target.checkup_date = today()
+ target.area = "Takeover"
+
+ doclist = get_mapped_doc(
+ "Lease",
+ source_name,
+ {
+ "Lease": {
+ "doctype": "Daily Checklist",
+ "field_map": {"property": "property"},
+ }
+ },
+ target_doc,
+ set_missing_values,
+ ignore_permissions=ignore_permissions,
+ )
+ return doclist
+
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def makeJournalEntry(customer, date, amount):
- try:
- propm_setting = frappe.get_doc(
- "Property Management Settings", "Property Management Settings"
- )
- company = frappe.db.get_single_value("Global Defaults", "default_company")
- company_doc = frappe.get_doc("Company", company)
- j_entry = []
- j_entry_debit = {}
- j_entry_debit["account"] = company_doc.default_receivable_account
- j_entry_debit["party_type"] = "Customer"
- j_entry_debit["party"] = customer
- j_entry_debit["debit_in_account_currency"] = amount
- j_entry.append(j_entry_debit)
- j_entry_credit = {}
- j_entry_credit["account"] = company_doc.default_cash_account
- j_entry_credit["credit_in_account_currency"] = amount
- j_entry.append(j_entry_credit)
- j_entry = frappe.get_doc(
- dict(
- doctype="Journal Entry",
- posting_date=date,
- company=propm_setting.company,
- accounts=j_entry,
- mode_of_payment=propm_setting.security_deposit_payment_type,
- )
- ).insert()
- return j_entry.name
-
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
+ try:
+ propm_setting = frappe.get_doc("Property Management Settings", "Property Management Settings")
+ company = frappe.db.get_single_value("Global Defaults", "default_company")
+ company_doc = frappe.get_doc("Company", company)
+ j_entry = []
+ j_entry_debit = {}
+ j_entry_debit["account"] = company_doc.default_receivable_account
+ j_entry_debit["party_type"] = "Customer"
+ j_entry_debit["party"] = customer
+ j_entry_debit["debit_in_account_currency"] = amount
+ j_entry.append(j_entry_debit)
+ j_entry_credit = {}
+ j_entry_credit["account"] = company_doc.default_cash_account
+ j_entry_credit["credit_in_account_currency"] = amount
+ j_entry.append(j_entry_credit)
+ j_entry = frappe.get_doc(
+ dict(
+ doctype="Journal Entry",
+ posting_date=date,
+ company=propm_setting.company,
+ accounts=j_entry,
+ mode_of_payment=propm_setting.security_deposit_payment_type,
+ )
+ ).insert()
+ return j_entry.name
+
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def getMonthADD(date, month):
- return add_months(getdate(date), int(month))
+ return add_months(getdate(date), int(month))
@frappe.whitelist()
def getDateDiff(date1, date2):
- return date_diff(getdate(date1), getdate(date2))
+ return date_diff(getdate(date1), getdate(date2))
@frappe.whitelist()
def getNumberOfDays(date):
- return calendar.monthrange(getdate(date).year, getdate(date).month)[1]
+ return calendar.monthrange(getdate(date).year, getdate(date).month)[1]
@frappe.whitelist()
def getMonthNo(date1, date2):
- d1 = getdate(date1)
- d2 = getdate(date2)
- return diff_month(
- datetime(d1.year, d1.month, d1.day), datetime(d2.year, d2.month, d2.day)
- )
+ d1 = getdate(date1)
+ d2 = getdate(date2)
+ return diff_month(datetime(d1.year, d1.month, d1.day), datetime(d2.year, d2.month, d2.day))
@frappe.whitelist()
def makeInvoiceSchedule(
- date,
- item,
- paid_by,
- item_name,
- name,
- qty,
- rate,
- idx,
- currency=None,
- tax=None,
- days_to_invoice_in_advance=None,
- invoice_item_group=None,
- document_type="Sales Invoice",
+ date,
+ item,
+ paid_by,
+ item_name,
+ name,
+ qty,
+ rate,
+ idx,
+ currency=None,
+ tax=None,
+ days_to_invoice_in_advance=None,
+ invoice_item_group=None,
+ document_type="Sales Invoice",
):
- if not document_type:
- document_type = "Sales Invoice"
- try:
- date_to_invoice = add_days(date, -1 * (days_to_invoice_in_advance or 0))
- frappe.get_doc(
- dict(
- idx=idx,
- doctype="Lease Invoice Schedule",
- parent=name,
- parentfield="lease_invoice_schedule",
- parenttype="lease",
- date_to_invoice=date_to_invoice,
- schedule_start_date=date,
- lease_item=item,
- paid_by=paid_by,
- lease_item_name=item_name,
- qty=qty,
- rate=rate,
- currency=currency,
- tax=tax,
- invoice_item_group=invoice_item_group,
- document_type=document_type,
- )
- ).insert()
- # frappe.msgprint(str(doc.name))
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
+ if not document_type:
+ document_type = "Sales Invoice"
+ try:
+ date_to_invoice = add_days(date, -1 * (days_to_invoice_in_advance or 0))
+ frappe.get_doc(
+ dict(
+ idx=idx,
+ doctype="Lease Invoice Schedule",
+ parent=name,
+ parentfield="lease_invoice_schedule",
+ parenttype="lease",
+ date_to_invoice=date_to_invoice,
+ schedule_start_date=date,
+ lease_item=item,
+ paid_by=paid_by,
+ lease_item_name=item_name,
+ qty=qty,
+ rate=rate,
+ currency=currency,
+ tax=tax,
+ invoice_item_group=invoice_item_group,
+ document_type=document_type,
+ )
+ ).insert()
+ # frappe.msgprint(str(doc.name))
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
def diff_month(d1, d2):
- if d1.day >= d2.day - 1:
- return (d1.year - d2.year) * 12 + d1.month - d2.month
- else:
- return (d1.year - d2.year) * 12 + d1.month - d2.month - 1
+ if d1.day >= d2.day - 1:
+ return (d1.year - d2.year) * 12 + d1.month - d2.month
+ else:
+ return (d1.year - d2.year) * 12 + d1.month - d2.month - 1
@frappe.whitelist()
def getDateMonthDiff(start_date, end_date, month_factor):
- month_count = 0
- no_month = 0
- month_float = 0
- # frappe.msgprint("start_date: " + str(start_date) + " --- end_date: " + str(end_date))
- while start_date <= end_date:
- period_end_date = add_days(add_months(start_date, month_factor), -1)
- # frappe.msgprint("start_date: " + str(start_date) + " --- period_end_date: " + str(period_end_date))
- if period_end_date <= end_date:
- # add month and set new start date to calculate next month_count
- month_count = month_count + month_factor
- start_date = add_months(start_date, month_factor)
- else:
- # find last number of days
- days = float(
- date_diff(getdate(end_date), getdate(add_months(start_date, no_month)))
- + 1
- )
- # msg = "no_month = 0 so Days calculated: " + str(days) + " between " + str(start_date) + " and " + str(end_date)
- # frappe.msgprint(msg)
- # start_date to cater for correct number of days in month in case the start date is feb
- no_days_in_month = float(
- calendar.monthrange(
- getdate(start_date).year, getdate(start_date).month
- )[1]
- )
- # msg = "no_month = 0 so No of Days calculated: " + str(no_days_in_month) + " between " + str(start_date) + " and " + str(end_date)
- # frappe.msgprint(msg)
- month_float = days / no_days_in_month
- # frappe.msgprint("month_float = " + str(month_float) + " for days = " + str(days) + " and total number of days = " + str(no_days_in_month))
- start_date = add_months(start_date, month_factor)
- month_count = month_count + no_month + month_float
- return month_count
+ month_count = 0
+ no_month = 0
+ month_float = 0
+ # frappe.msgprint("start_date: " + str(start_date) + " --- end_date: " + str(end_date))
+ while start_date <= end_date:
+ period_end_date = add_days(add_months(start_date, month_factor), -1)
+ # frappe.msgprint("start_date: " + str(start_date) + " --- period_end_date: " + str(period_end_date))
+ if period_end_date <= end_date:
+ # add month and set new start date to calculate next month_count
+ month_count = month_count + month_factor
+ start_date = add_months(start_date, month_factor)
+ else:
+ # find last number of days
+ days = float(date_diff(getdate(end_date), getdate(add_months(start_date, no_month))) + 1)
+ # msg = "no_month = 0 so Days calculated: " + str(days) + " between " + str(start_date) + " and " + str(end_date)
+ # frappe.msgprint(msg)
+ # start_date to cater for correct number of days in month in case the start date is feb
+ no_days_in_month = float(
+ calendar.monthrange(getdate(start_date).year, getdate(start_date).month)[1]
+ )
+ # msg = "no_month = 0 so No of Days calculated: " + str(no_days_in_month) + " between " + str(start_date) + " and " + str(end_date)
+ # frappe.msgprint(msg)
+ month_float = days / no_days_in_month
+ # frappe.msgprint("month_float = " + str(month_float) + " for days = " + str(days) + " and total number of days = " + str(no_days_in_month))
+ start_date = add_months(start_date, month_factor)
+ month_count = month_count + no_month + month_float
+ return month_count
@frappe.whitelist()
def get_active_meter_from_property(property_id, meter_type):
- """Get Active Meter Number"""
- meter_data = frappe.db.sql(
- """SELECT meter_number
+ """Get Active Meter Number"""
+ meter_data = frappe.db.sql(
+ """SELECT meter_number
FROM `tabProperty Meter Reading`
WHERE parent=%s
AND meter_type=%s
AND status='Active'""",
- (property_id, meter_type),
- as_dict=True,
- )
- if meter_data:
- return meter_data[0].meter_number
- else:
- return ""
+ (property_id, meter_type),
+ as_dict=True,
+ )
+ if meter_data:
+ return meter_data[0].meter_number
+ else:
+ return ""
@frappe.whitelist()
def get_active_meter_customer_from_property(property_id, meter_type):
- # Unused as per conversation with Vimal on 2019-08-11
- """Get Active Meter Customer Name"""
- meter_data = frappe.db.sql(
- """SELECT invoice_customer
+ # Unused as per conversation with Vimal on 2019-08-11
+ """Get Active Meter Customer Name"""
+ meter_data = frappe.db.sql(
+ """SELECT invoice_customer
FROM `tabProperty Meter Reading`
WHERE parent=%s
AND meter_type=%s
AND status='Active'""",
- (property_id, meter_type),
- as_dict=True,
- )
- if meter_data:
- return meter_data[0].invoice_customer
- else:
- return ""
+ (property_id, meter_type),
+ as_dict=True,
+ )
+ if meter_data:
+ return meter_data[0].invoice_customer
+ else:
+ return ""
@frappe.whitelist()
def get_previous_meter_reading(meter_number, property_id, meter_type):
- """Get Previous Meter Reading"""
- previous_reading_details = frappe.db.sql(
- """SELECT md.current_meter_reading as 'previous_reading',
+ """Get Previous Meter Reading"""
+ previous_reading_details = frappe.db.sql(
+ """SELECT md.current_meter_reading as 'previous_reading',
m.reading_date as 'reading_date'
FROM `tabMeter Reading Detail` AS md
INNER JOIN `tabMeter Reading` AS m ON md.parent=m.name
WHERE md.meter_number=%s
AND m.docstatus=1
ORDER BY m.reading_date DESC limit 1""",
- meter_number,
- as_dict=True,
- )
- if len(previous_reading_details) >= 1:
- # print previous_reading_details[0].previous_reading
- return previous_reading_details[0]
- else:
- initial_reading_details = frappe.db.sql(
- """SELECT initial_meter_reading as 'previous_reading',
+ meter_number,
+ as_dict=True,
+ )
+ if len(previous_reading_details) >= 1:
+ # print previous_reading_details[0].previous_reading
+ return previous_reading_details[0]
+ else:
+ initial_reading_details = frappe.db.sql(
+ """SELECT initial_meter_reading as 'previous_reading',
installation_date as 'reading_date'
FROM `tabProperty Meter Reading`
WHERE parent=%s
AND meter_type=%s
AND meter_number=%s
AND status='Active'""",
- (property_id, meter_type, meter_number),
- as_dict=True,
- )
- if len(initial_reading_details) >= 1:
- return initial_reading_details[0]
- else:
- return 0
+ (property_id, meter_type, meter_number),
+ as_dict=True,
+ )
+ if len(initial_reading_details) >= 1:
+ return initial_reading_details[0]
+ else:
+ return 0
@frappe.whitelist()
def make_invoice_meter_reading(self, method):
- for meter_row in self.meter_reading_detail:
- if int(meter_row.do_not_create_invoice) != 1:
- item_detail = get_item_details(
- self.meter_type,
- meter_row.reading_difference,
- meter_row.previous_reading_date,
- add_days(self.reading_date, -1),
- )
- # Changed from propert/meter customer lookup to pos cusotmer lookup as per conversation with Vimal on 2019-11-08
- leasename = get_latest_active_lease(meter_row.property)
- lease = frappe.get_doc("Lease", leasename)
- # customer = get_active_meter_customer_from_property(meter_row.property,self.meter_type)
- customer = lease.customer
- if customer:
- meter_row.invoice_number = make_invoice(
- self.reading_date,
- customer,
- meter_row.property,
- item_detail,
- self.meter_type,
- meter_row.previous_reading_date,
- add_days(self.reading_date, -1),
- )
- # meter_row.invoice_number = si_no
- # frappe.db.set_value("Meter Reading Detail",meter_row.name,"invoice_number",si_no)
- self.db_update()
+ for meter_row in self.meter_reading_detail:
+ if int(meter_row.do_not_create_invoice) != 1:
+ item_detail = get_item_details(
+ self.meter_type,
+ meter_row.reading_difference,
+ meter_row.previous_reading_date,
+ add_days(self.reading_date, -1),
+ )
+ # Changed from propert/meter customer lookup to pos cusotmer lookup as per conversation with Vimal on 2019-11-08
+ leasename = get_latest_active_lease(meter_row.property)
+ lease = frappe.get_doc("Lease", leasename)
+ # customer = get_active_meter_customer_from_property(meter_row.property,self.meter_type)
+ customer = lease.customer
+ if customer:
+ meter_row.invoice_number = make_invoice(
+ self.reading_date,
+ customer,
+ meter_row.property,
+ item_detail,
+ self.meter_type,
+ meter_row.previous_reading_date,
+ add_days(self.reading_date, -1),
+ )
+ # meter_row.invoice_number = si_no
+ # frappe.db.set_value("Meter Reading Detail",meter_row.name,"invoice_number",si_no)
+ self.db_update()
@frappe.whitelist()
-def make_invoice(
- meter_date, customer, property_id, items, lease_item, from_date=None, to_date=None
-):
- company = frappe.db.get_value("Property", property_id, "company")
- try:
- sales_invoice = frappe.get_doc(
- dict(
- doctype="Sales Invoice",
- company=company,
- posting_date=meter_date,
- items=items,
- lease=get_latest_active_lease(property_id),
- lease_item=lease_item,
- customer=str(customer),
- due_date=getDueDate(meter_date, str(customer)),
- taxes_and_charges=frappe.get_value(
- "Company", company, "default_tax_template"
- ),
- cost_center=get_cost_center(property_id),
- from_date=from_date,
- to_date=to_date,
- )
- ).insert()
- if sales_invoice.taxes_and_charges:
- get_tax(sales_invoice)
- sales_invoice.calculate_taxes_and_totals()
- sales_invoice.save()
- return sales_invoice.name
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
+def make_invoice(meter_date, customer, property_id, items, lease_item, from_date=None, to_date=None):
+ company = frappe.db.get_value("Property", property_id, "company")
+ try:
+ sales_invoice = frappe.get_doc(
+ dict(
+ doctype="Sales Invoice",
+ company=company,
+ posting_date=meter_date,
+ items=items,
+ lease=get_latest_active_lease(property_id),
+ lease_item=lease_item,
+ customer=str(customer),
+ due_date=getDueDate(meter_date, str(customer)),
+ taxes_and_charges=frappe.get_value("Company", company, "default_tax_template"),
+ cost_center=get_cost_center(property_id),
+ from_date=from_date,
+ to_date=to_date,
+ )
+ ).insert()
+ if sales_invoice.taxes_and_charges:
+ get_tax(sales_invoice)
+ sales_invoice.calculate_taxes_and_totals()
+ sales_invoice.save()
+ return sales_invoice.name
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def get_tax(sales_invoice):
- taxes = get_taxes_and_charges(
- "Sales Taxes and Charges Template", sales_invoice.taxes_and_charges
- )
- for tax in taxes:
- sales_invoice.append("taxes", tax)
+ taxes = get_taxes_and_charges("Sales Taxes and Charges Template", sales_invoice.taxes_and_charges)
+ for tax in taxes:
+ sales_invoice.append("taxes", tax)
@frappe.whitelist()
def get_cost_center(property_id):
- return frappe.db.get_value("Property", property_id, "cost_center")
+ return frappe.db.get_value("Property", property_id, "cost_center")
@frappe.whitelist()
def get_item_details(item, qty, service_start_date, service_end_date):
- item_dict = []
- item_json = {}
- item_json["item_code"] = item
- item_json["qty"] = qty
- item_json["service_start_date"] = service_start_date
- item_json["service_end_date"] = service_end_date
- item_dict.append(item_json)
- return item_dict
+ item_dict = []
+ item_json = {}
+ item_json["item_code"] = item
+ item_json["qty"] = qty
+ item_json["service_start_date"] = service_start_date
+ item_json["service_end_date"] = service_end_date
+ item_dict.append(item_json)
+ return item_dict
@frappe.whitelist()
def get_latest_active_lease(property_id):
- lease_details = frappe.get_all(
- "Lease",
- filters={"property": property_id},
- fields=["name"],
- order_by="lease_date desc",
- limit=1,
- )
- if len(lease_details) >= 1:
- return lease_details[0].name
- else:
- return ""
+ lease_details = frappe.get_all(
+ "Lease",
+ filters={"property": property_id},
+ fields=["name"],
+ order_by="lease_date desc",
+ limit=1,
+ )
+ if len(lease_details) >= 1:
+ return lease_details[0].name
+ else:
+ return ""
diff --git a/propms/config/desktop.py b/propms/config/desktop.py
index d217fbc6..c95333aa 100755
--- a/propms/config/desktop.py
+++ b/propms/config/desktop.py
@@ -1,17 +1,15 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
from frappe import _
def get_data():
- return [
- {
- "module_name": "Property Management Solution",
- "category": "Domains",
- "color": "grey",
- "icon": "octicon octicon-home",
- "type": "module",
- "label": _("Property Management"),
- "description": "Property, lease, maintenance jobs, keys and analytics",
- },
- ]
+ return [
+ {
+ "module_name": "Property Management Solution",
+ "category": "Domains",
+ "color": "grey",
+ "icon": "octicon octicon-home",
+ "type": "module",
+ "label": _("Property Management"),
+ "description": "Property, lease, maintenance jobs, keys and analytics",
+ },
+ ]
diff --git a/propms/config/docs.py b/propms/config/docs.py
index 846fb030..9c090a58 100755
--- a/propms/config/docs.py
+++ b/propms/config/docs.py
@@ -9,4 +9,4 @@
def get_context(context):
- context.brand_html = "Property Management Solution"
+ context.brand_html = "Property Management Solution"
diff --git a/propms/config/property_management_solution.py b/propms/config/property_management_solution.py
index f7da7e82..b7a4687f 100644
--- a/propms/config/property_management_solution.py
+++ b/propms/config/property_management_solution.py
@@ -1,200 +1,197 @@
-from __future__ import unicode_literals
from frappe import _
def get_data():
- config = [
- {
- "label": _("Property Documents"),
- "items": [
- {
- "type": "doctype",
- "name": "Property",
- "description": _("Property that needs to be managed."),
- },
- {
- "type": "doctype",
- "name": "Lease",
- "description": _("Lease pertaining to the properties."),
- },
- {
- "type": "doctype",
- "name": "Key Set Detail",
- "description": _("Key Set Detail"),
- },
- {
- "type": "doctype",
- "name": "Tool Item Record",
- "description": _("Tool Item Record"),
- },
- {
- "type": "doctype",
- "name": "Daily Checklist",
- "description": _("Daily Checklist"),
- },
- {
- "type": "doctype",
- "name": "Exit",
- "description": _("Exit"),
- },
- {
- "type": "doctype",
- "name": "Meter Reading",
- "description": _("Meter Reading"),
- },
- {
- "type": "doctype",
- "name": "Outsourcing Attendance",
- "description": _("Outsourcing Attendance"),
- },
- {
- "type": "doctype",
- "name": "Insurance",
- "description": _("Insurance"),
- },
- {
- "type": "doctype",
- "name": "Security Attendance",
- "description": _("Security Attendance"),
- },
- {
- "type": "doctype",
- "name": "Withholding Tax Summary",
- "description": _("Withholding Tax Summary"),
- },
- ],
- },
- {
- "label": _("Property Masters"),
- "icon": "fa fa-cog",
- "items": [
- {
- "type": "doctype",
- "name": "Unit Type",
- "label": _("Unit Type"),
- "description": _("Unit Type definition."),
- },
- {
- "type": "doctype",
- "name": "Property",
- "description": _("Property database."),
- },
- {
- "type": "doctype",
- "name": "Checklist Checkup Area",
- "icon": "fa fa-sitemap",
- "label": _("Checklist Checkup Area"),
- "description": _("Areas for Checklist Checkup."),
- },
- {
- "type": "doctype",
- "name": "Guard Shift",
- "label": _("Guard Shift"),
- "description": _("Shif for security guards."),
- },
- {
- "type": "doctype",
- "name": "Key Set",
- "icon": "fa fa-sitemap",
- "label": _("Key Set"),
- "description": _("Key sets in custody."),
- },
- {
- "type": "doctype",
- "name": "Tool Item Set",
- "icon": "fa fa-sitemap",
- "label": _("Tool Item Set"),
- "description": _("Tool Item Sets in custody."),
- },
- {
- "type": "doctype",
- "name": "Outsourcing Category",
- "label": _("Outsourcing Category"),
- "description": _("Outsourcing Category definition."),
- },
- {
- "type": "doctype",
- "name": "Property Amenity",
- "label": _("Property Amenity"),
- "description": _("Property Amenity definition."),
- },
- {
- "type": "doctype",
- "name": "Security Deposit Details",
- "label": _("Security Deposit Details"),
- "description": _("Security Deposit Details definition."),
- },
- {
- "type": "doctype",
- "name": "Meter",
- "label": _("Meter database"),
- "description": _("Register all meters here."),
- },
- ],
- },
- {
- "label": _("Property Settings"),
- "items": [
- {
- "type": "doctype",
- "name": "Property Management Settings",
- "label": _("Property Management Settings"),
- "description": _("Property Management Settings"),
- },
- ],
- },
- {
- "label": _("Property Analytics"),
- "items": [
- {
- "type": "report",
- "name": "Outsourcing Attendance",
- "is_query_report": True,
- "doctype": "Outsourcing Attendance",
- },
- {
- "type": "report",
- "name": "Security Attendance Report",
- "is_query_report": True,
- "doctype": "Security Attendance",
- },
- {
- "type": "report",
- "name": "Security Deposit",
- "is_query_report": True,
- "doctype": "Journal Entry",
- },
- {
- "type": "report",
- "name": "Debtors Report",
- "is_query_report": True,
- "doctype": "Sales Invoice",
- },
- {
- "type": "report",
- "name": "Creditors Report",
- "is_query_report": True,
- "doctype": "Purchase Invoice",
- },
- {
- "type": "report",
- "name": "Lease Information",
- "is_query_report": True,
- "doctype": "Lease",
- "label": _("Lease Report"),
- "description": _(
- "This is to show status of every lease by type of property"
- ),
- },
- {
- "type": "report",
- "name": "Property Status",
- "is_query_report": True,
- "doctype": "Property",
- "label": _("Property Status"),
- "description": _("Information about all properties in the system"),
- },
- ],
- },
- ]
- return config
+ config = [
+ {
+ "label": _("Property Documents"),
+ "items": [
+ {
+ "type": "doctype",
+ "name": "Property",
+ "description": _("Property that needs to be managed."),
+ },
+ {
+ "type": "doctype",
+ "name": "Lease",
+ "description": _("Lease pertaining to the properties."),
+ },
+ {
+ "type": "doctype",
+ "name": "Key Set Detail",
+ "description": _("Key Set Detail"),
+ },
+ {
+ "type": "doctype",
+ "name": "Tool Item Record",
+ "description": _("Tool Item Record"),
+ },
+ {
+ "type": "doctype",
+ "name": "Daily Checklist",
+ "description": _("Daily Checklist"),
+ },
+ {
+ "type": "doctype",
+ "name": "Exit",
+ "description": _("Exit"),
+ },
+ {
+ "type": "doctype",
+ "name": "Meter Reading",
+ "description": _("Meter Reading"),
+ },
+ {
+ "type": "doctype",
+ "name": "Outsourcing Attendance",
+ "description": _("Outsourcing Attendance"),
+ },
+ {
+ "type": "doctype",
+ "name": "Insurance",
+ "description": _("Insurance"),
+ },
+ {
+ "type": "doctype",
+ "name": "Security Attendance",
+ "description": _("Security Attendance"),
+ },
+ {
+ "type": "doctype",
+ "name": "Withholding Tax Summary",
+ "description": _("Withholding Tax Summary"),
+ },
+ ],
+ },
+ {
+ "label": _("Property Masters"),
+ "icon": "fa fa-cog",
+ "items": [
+ {
+ "type": "doctype",
+ "name": "Unit Type",
+ "label": _("Unit Type"),
+ "description": _("Unit Type definition."),
+ },
+ {
+ "type": "doctype",
+ "name": "Property",
+ "description": _("Property database."),
+ },
+ {
+ "type": "doctype",
+ "name": "Checklist Checkup Area",
+ "icon": "fa fa-sitemap",
+ "label": _("Checklist Checkup Area"),
+ "description": _("Areas for Checklist Checkup."),
+ },
+ {
+ "type": "doctype",
+ "name": "Guard Shift",
+ "label": _("Guard Shift"),
+ "description": _("Shif for security guards."),
+ },
+ {
+ "type": "doctype",
+ "name": "Key Set",
+ "icon": "fa fa-sitemap",
+ "label": _("Key Set"),
+ "description": _("Key sets in custody."),
+ },
+ {
+ "type": "doctype",
+ "name": "Tool Item Set",
+ "icon": "fa fa-sitemap",
+ "label": _("Tool Item Set"),
+ "description": _("Tool Item Sets in custody."),
+ },
+ {
+ "type": "doctype",
+ "name": "Outsourcing Category",
+ "label": _("Outsourcing Category"),
+ "description": _("Outsourcing Category definition."),
+ },
+ {
+ "type": "doctype",
+ "name": "Property Amenity",
+ "label": _("Property Amenity"),
+ "description": _("Property Amenity definition."),
+ },
+ {
+ "type": "doctype",
+ "name": "Security Deposit Details",
+ "label": _("Security Deposit Details"),
+ "description": _("Security Deposit Details definition."),
+ },
+ {
+ "type": "doctype",
+ "name": "Meter",
+ "label": _("Meter database"),
+ "description": _("Register all meters here."),
+ },
+ ],
+ },
+ {
+ "label": _("Property Settings"),
+ "items": [
+ {
+ "type": "doctype",
+ "name": "Property Management Settings",
+ "label": _("Property Management Settings"),
+ "description": _("Property Management Settings"),
+ },
+ ],
+ },
+ {
+ "label": _("Property Analytics"),
+ "items": [
+ {
+ "type": "report",
+ "name": "Outsourcing Attendance",
+ "is_query_report": True,
+ "doctype": "Outsourcing Attendance",
+ },
+ {
+ "type": "report",
+ "name": "Security Attendance Report",
+ "is_query_report": True,
+ "doctype": "Security Attendance",
+ },
+ {
+ "type": "report",
+ "name": "Security Deposit",
+ "is_query_report": True,
+ "doctype": "Journal Entry",
+ },
+ {
+ "type": "report",
+ "name": "Debtors Report",
+ "is_query_report": True,
+ "doctype": "Sales Invoice",
+ },
+ {
+ "type": "report",
+ "name": "Creditors Report",
+ "is_query_report": True,
+ "doctype": "Purchase Invoice",
+ },
+ {
+ "type": "report",
+ "name": "Lease Information",
+ "is_query_report": True,
+ "doctype": "Lease",
+ "label": _("Lease Report"),
+ "description": _("This is to show status of every lease by type of property"),
+ },
+ {
+ "type": "report",
+ "name": "Property Status",
+ "is_query_report": True,
+ "doctype": "Property",
+ "label": _("Property Status"),
+ "description": _("Information about all properties in the system"),
+ },
+ ],
+ },
+ ]
+ return config
diff --git a/propms/hooks.py b/propms/hooks.py
index 074fe966..0d923bcc 100755
--- a/propms/hooks.py
+++ b/propms/hooks.py
@@ -1,7 +1,3 @@
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-from . import __version__ as app_version
-
app_name = "propms"
app_title = "Property Management Solution"
app_publisher = "Aakvatech"
@@ -25,17 +21,17 @@
# include js in page
# page_js = {"page" : "public/js/file.js"}
page_js = {
- "pos": "property_management_solution/point_of_sale.js",
- "point-of-sale": "property_management_solution/point_of_sale.js",
+ "pos": "property_management_solution/point_of_sale.js",
+ "point-of-sale": "property_management_solution/point_of_sale.js",
}
# include js in doctype views
# doctype_js = {"doctype" : "public/js/doctype.js"}
doctype_js = {
- "Sales Invoice": "property_management_solution/sales_invoice.js",
- "Journal Entry Account": "property_management_solution/journal_entry_account.js",
- "Issue": "property_management_solution/issue.js",
- "Company": "property_management_solution/company.js",
+ "Sales Invoice": "property_management_solution/sales_invoice.js",
+ "Journal Entry Account": "property_management_solution/journal_entry_account.js",
+ "Issue": "property_management_solution/issue.js",
+ "Company": "property_management_solution/company.js",
}
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
@@ -66,13 +62,13 @@
# before_install = "propms.install.before_install"
after_install = [
- "propms.utils.create_custom_fields.execute",
- "propms.utils.create_property_setter.execute",
+ "propms.utils.create_custom_fields.execute",
+ "propms.utils.create_property_setter.execute",
]
after_migrate = [
- "propms.utils.create_custom_fields.execute",
- "propms.utils.create_property_setter.execute",
+ "propms.utils.create_custom_fields.execute",
+ "propms.utils.create_property_setter.execute",
]
# Desk Notifications
@@ -102,41 +98,37 @@
doc_events = {
- "Issue": {
- "validate": [
- "propms.issue_hook.validate",
- ],
- },
- "Property": {
- "validate": "propms.property_increment.validate_property_increment_settings",
- },
- "Material Request": {
- "validate": "propms.auto_custom.makeSalesInvoice",
- "on_update": "propms.auto_custom.makeSalesInvoice",
- "on_change": "propms.auto_custom.makeSalesInvoice",
- },
- "Sales Order": {
- "validate": "propms.auto_custom.validateSalesInvoiceItemDuplication"
- },
- "Key Set Detail": {"on_change": "propms.auto_custom.changeStatusKeyset"},
- "Meter Reading": {"on_submit": "propms.auto_custom.make_invoice_meter_reading"},
+ "Issue": {
+ "validate": [
+ "propms.issue_hook.validate",
+ ],
+ },
+ "Property": {
+ "validate": "propms.property_increment.validate_property_increment_settings",
+ },
+ "Material Request": {
+ "validate": "propms.auto_custom.makeSalesInvoice",
+ "on_update": "propms.auto_custom.makeSalesInvoice",
+ "on_change": "propms.auto_custom.makeSalesInvoice",
+ },
+ "Sales Order": {"validate": "propms.auto_custom.validateSalesInvoiceItemDuplication"},
+ "Key Set Detail": {"on_change": "propms.auto_custom.changeStatusKeyset"},
+ "Meter Reading": {"on_submit": "propms.auto_custom.make_invoice_meter_reading"},
}
scheduler_events = {
- "daily_long": [
- "propms.property_management_solution.doctype.lease.lease.update_lease_statuses"
+ "daily_long": ["propms.property_management_solution.doctype.lease.lease.update_lease_statuses"],
+ "daily": [
+ "propms.auto_custom.statusChangeBeforeLeaseExpire",
+ "propms.auto_custom.statusChangeAfterLeaseExpire",
+ "propms.property_increment.run_property_increment_engine",
],
- "daily": [
- "propms.auto_custom.statusChangeBeforeLeaseExpire",
- "propms.auto_custom.statusChangeAfterLeaseExpire",
- "propms.property_increment.run_property_increment_engine",
- ],
- "cron": {
- # "00 12 * * *": ["propms.lease_invoice.leaseInvoiceAutoCreate"],
- "00 00 * * *": ["propms.lease_invoice_schedule.make_lease_invoice_schedule"],
- "00 12 * * *": ["propms.lease_invoice.enqueue_lease_invoice_auto_create"],
- }
+ "cron": {
+ # "00 12 * * *": ["propms.lease_invoice.leaseInvoiceAutoCreate"],
+ "00 00 * * *": ["propms.lease_invoice_schedule.make_lease_invoice_schedule"],
+ "00 12 * * *": ["propms.lease_invoice.enqueue_lease_invoice_auto_create"],
+ },
}
diff --git a/propms/issue_hook.py b/propms/issue_hook.py
index c8ddacd7..3773a21b 100644
--- a/propms/issue_hook.py
+++ b/propms/issue_hook.py
@@ -1,348 +1,325 @@
-from __future__ import unicode_literals
import frappe
+from erpnext.controllers.accounts_controller import get_taxes_and_charges
+from erpnext.stock.get_item_details import get_pos_profile
+from erpnext.utilities.product import get_price
from frappe import _
from frappe.utils import today
-from erpnext.utilities.product import get_price
-from erpnext.stock.get_item_details import get_pos_profile
+
from propms.auto_custom import get_latest_active_lease
-from erpnext.controllers.accounts_controller import get_taxes_and_charges
def make_transaction(doc, for_self_consumption=False):
- is_grouped = frappe.db.get_value(
- "Property Management Settings", None, "group_maintenance_job_items"
- )
- if not is_grouped:
- is_grouped = 0
- is_grouped = int(is_grouped)
- company = doc.company
- if not company:
- company = frappe.db.get_single_value("Global Defaults", "default_company")
- cost_center = frappe.db.get_value("Property", doc.property_name, "cost_center")
- submit_maintenance_stock_entry = frappe.db.get_value(
- "Property Management Settings", None, "submit_maintenance_stock_entry"
- )
- submit_maintenance_invoice = frappe.db.get_value(
- "Property Management Settings", None, "submit_maintenance_invoice"
- )
- # TODO: Remove this after stability of Stock Entry
- self_consumption_customer = frappe.db.get_value(
- "Property Management Settings", None, "self_consumption_customer"
- )
- if not submit_maintenance_stock_entry:
- submit_maintenance_stock_entry = 0
- submit_maintenance_stock_entry = int(submit_maintenance_stock_entry)
- user_remarks = "Transaction for Maintenance Job Card {0}".format(doc.name)
- lease = get_latest_active_lease(doc.property_name)
+ is_grouped = frappe.db.get_single_value("Property Management Settings", "group_maintenance_job_items")
+ if not is_grouped:
+ is_grouped = 0
+ is_grouped = int(is_grouped)
+ company = doc.company
+ if not company:
+ company = frappe.db.get_single_value("Global Defaults", "default_company")
+ cost_center = frappe.db.get_value("Property", doc.property_name, "cost_center")
+ submit_maintenance_stock_entry = frappe.db.get_single_value(
+ "Property Management Settings", "submit_maintenance_stock_entry"
+ )
+ submit_maintenance_invoice = frappe.db.get_single_value(
+ "Property Management Settings", "submit_maintenance_invoice"
+ )
+ # TODO: Remove this after stability of Stock Entry
+ self_consumption_customer = frappe.db.get_single_value(
+ "Property Management Settings", "self_consumption_customer"
+ )
+ if not submit_maintenance_stock_entry:
+ submit_maintenance_stock_entry = 0
+ submit_maintenance_stock_entry = int(submit_maintenance_stock_entry)
+ user_remarks = f"Transaction for Maintenance Job Card {doc.name}"
+ lease = get_latest_active_lease(doc.property_name)
+
+ def make_stock_entry(items_list=None, pos=None):
+ if not len(items_list) > 0:
+ return
- def make_stock_entry(items_list=None, pos=None):
- if not len(items_list) > 0:
- return
+ # Create a stock entry for purpose material issue
+ stock_entry_doc = frappe.get_doc(
+ {
+ "doctype": "Stock Entry",
+ "stock_entry_type": "Material Issue",
+ "purpose": "Material Issue",
+ "posting_date": today(),
+ "remarks": user_remarks,
+ "company": doc.company,
+ "items": items_list,
+ "from_warehouse": frappe.db.get_single_value("Stock Settings", "default_warehouse"),
+ }
+ )
+ if stock_entry_doc:
+ stock_entry_doc.insert(ignore_permissions=True)
+ stock_entry_url = frappe.utils.get_url_to_form(stock_entry_doc.doctype, stock_entry_doc.name)
+ se_msgprint = f"Stock Entry Created {stock_entry_doc.name}"
+ frappe.flags.ignore_account_permission = True
+ if submit_maintenance_stock_entry == 1 and not pos:
+ stock_entry_doc.submit()
+ if pos:
+ frappe.throw(_("POS Stock Entry cannot be created for Self Consumption items"))
+ frappe.msgprint(_(se_msgprint))
+ for item_row in doc.materials_billed:
+ if (
+ item_row.item
+ and item_row.quantity
+ and item_row.material_status == "Self Consumption"
+ and not item_row.stock_entry
+ ):
+ item_row.stock_entry = stock_entry_doc.name
+ frappe.db.set_value(
+ "Issue Materials Billed",
+ item_row.name,
+ "stock_entry",
+ stock_entry_doc.name,
+ )
+ frappe.db.commit()
- # Create a stock entry for purpose material issue
- stock_entry_doc = frappe.get_doc(
- {
- "doctype": "Stock Entry",
- "stock_entry_type": "Material Issue",
- "purpose": "Material Issue",
- "posting_date": today(),
- "remarks": user_remarks,
- "company": doc.company,
- "items": items_list,
- "from_warehouse": frappe.db.get_single_value("Stock Settings", "default_warehouse"),
- }
- )
- if stock_entry_doc:
- stock_entry_doc.insert(ignore_permissions=True)
- stock_entry_url = frappe.utils.get_url_to_form(
- stock_entry_doc.doctype, stock_entry_doc.name
- )
- se_msgprint = "Stock Entry Created {1}".format(
- stock_entry_url, stock_entry_doc.name
- )
- frappe.flags.ignore_account_permission = True
- if submit_maintenance_stock_entry == 1 and not pos:
- stock_entry_doc.submit()
- if pos:
- frappe.throw(_("POS Stock Entry cannot be created for Self Consumption items"))
- frappe.msgprint(_(se_msgprint))
- for item_row in doc.materials_billed:
- if (
- item_row.item
- and item_row.quantity
- and item_row.material_status == "Self Consumption"
- and not item_row.stock_entry
- ):
- item_row.stock_entry = stock_entry_doc.name
- frappe.db.set_value(
- "Issue Materials Billed",
- item_row.name,
- "stock_entry",
- stock_entry_doc.name,
- )
- frappe.db.commit()
+ def make_sales_invoice(items_list=None, pos=None, self_customer=None):
+ if not len(items_list) > 0 or not doc.customer:
+ return
+ default_tax_template = frappe.db.get_value("Company", company, "default_maintenance_tax_template")
+ if not default_tax_template:
+ url = frappe.utils.get_url_to_form("Company", company)
+ frappe.throw(_(f"Please Setup Default Maintenance Tax Template in {company}"))
+ if self_customer:
+ invoice_customer = self_consumption_customer
+ else:
+ invoice_customer = doc.customer
+ is_pos = 0
+ pos_profile = ""
+ naming_series = ""
+ if pos:
+ user_pos_profile = get_pos_profile(company)
+ is_pos = 1
+ pos_profile = user_pos_profile.name
+ naming_series = user_pos_profile.naming_series
+ default_tax_template = user_pos_profile.taxes_and_charges or default_tax_template
+ invoice_doc = frappe.get_doc(
+ dict(
+ is_pos=is_pos,
+ pos_profile=pos_profile,
+ naming_series=naming_series,
+ doctype="Sales Invoice",
+ customer=invoice_customer,
+ company=company,
+ posting_date=today(),
+ due_date=today(),
+ ignore_pricing_rule=1,
+ items=items_list,
+ update_stock=1,
+ remarks=user_remarks,
+ cost_center=cost_center,
+ lease=lease,
+ taxes_and_charges=default_tax_template,
+ job_card=doc.name,
+ )
+ ).insert(ignore_permissions=True)
+ invoice_doc.reload()
+ if invoice_doc.taxes_and_charges and not pos:
+ getTax(invoice_doc)
+ invoice_doc.calculate_taxes_and_totals()
+ invoice_doc.run_method("set_missing_values")
+ invoice_doc.run_method("calculate_taxes_and_totals")
+ invoice_doc.save()
+ if invoice_doc:
+ invoice_url = frappe.utils.get_url_to_form(invoice_doc.doctype, invoice_doc.name)
+ si_msgprint = f"Sales invoice Created {invoice_doc.name}"
+ frappe.flags.ignore_account_permission = True
+ if submit_maintenance_invoice == 1 and not pos:
+ invoice_doc.submit()
+ if pos:
+ make_sales_pos_payment(invoice_doc, user_pos_profile.name)
+ si_msgprint = "POS " + si_msgprint
+ frappe.msgprint(_(si_msgprint))
+ for item_row in doc.materials_billed:
+ if (
+ item_row.item
+ and item_row.quantity
+ and item_row.invoiced == 1
+ and not item_row.sales_invoice
+ ):
+ item_row.sales_invoice = invoice_doc.name
+ frappe.db.set_value(
+ "Issue Materials Billed",
+ item_row.name,
+ "sales_invoice",
+ invoice_doc.name,
+ )
+ frappe.db.commit()
- def make_sales_invoice(items_list=None, pos=None, self_customer=None):
- if not len(items_list) > 0 or not doc.customer:
- return
- default_tax_template = frappe.db.get_value(
- "Company", company, "default_maintenance_tax_template"
- )
- if not default_tax_template:
- url = frappe.utils.get_url_to_form("Company", company)
- frappe.throw(
- _(
- "Please Setup Default Maintenance Tax Template in {1}".format(
- url, company
- )
- )
- )
- if self_customer:
- invoice_customer = self_consumption_customer
- else:
- invoice_customer = doc.customer
- is_pos = 0
- pos_profile = ""
- naming_series = ""
- if pos:
- user_pos_profile = get_pos_profile(company)
- is_pos = 1
- pos_profile = user_pos_profile.name
- naming_series = user_pos_profile.naming_series
- default_tax_template = (
- user_pos_profile.taxes_and_charges or default_tax_template
- )
- invoice_doc = frappe.get_doc(
- dict(
- is_pos=is_pos,
- pos_profile=pos_profile,
- naming_series=naming_series,
- doctype="Sales Invoice",
- customer=invoice_customer,
- company=company,
- posting_date=today(),
- due_date=today(),
- ignore_pricing_rule=1,
- items=items_list,
- update_stock=1,
- remarks=user_remarks,
- cost_center=cost_center,
- lease=lease,
- taxes_and_charges=default_tax_template,
- job_card=doc.name,
- )
- ).insert(ignore_permissions=True)
- invoice_doc.reload()
- if invoice_doc.taxes_and_charges and not pos:
- getTax(invoice_doc)
- invoice_doc.calculate_taxes_and_totals()
- invoice_doc.run_method("set_missing_values")
- invoice_doc.run_method("calculate_taxes_and_totals")
- invoice_doc.save()
- if invoice_doc:
- invoice_url = frappe.utils.get_url_to_form(
- invoice_doc.doctype, invoice_doc.name
- )
- si_msgprint = "Sales invoice Created {1}".format(
- invoice_url, invoice_doc.name
- )
- frappe.flags.ignore_account_permission = True
- if submit_maintenance_invoice == 1 and not pos:
- invoice_doc.submit()
- if pos:
- make_sales_pos_payment(invoice_doc, user_pos_profile.name)
- si_msgprint = "POS " + si_msgprint
- frappe.msgprint(_(si_msgprint))
- for item_row in doc.materials_billed:
- if (
- item_row.item
- and item_row.quantity
- and item_row.invoiced == 1
- and not item_row.sales_invoice
- ):
- item_row.sales_invoice = invoice_doc.name
- frappe.db.set_value(
- "Issue Materials Billed",
- item_row.name,
- "sales_invoice",
- invoice_doc.name,
- )
- frappe.db.commit()
+ def getTax(sales_invoice):
+ taxes = get_taxes_and_charges("Sales Taxes and Charges Template", sales_invoice.taxes_and_charges)
+ for tax in taxes:
+ sales_invoice.append("taxes", tax)
- def getTax(sales_invoice):
- taxes = get_taxes_and_charges(
- "Sales Taxes and Charges Template", sales_invoice.taxes_and_charges
- )
- for tax in taxes:
- sales_invoice.append("taxes", tax)
+ def make_sales_pos_payment(invoice_doc, pos_profile_name):
+ default_mode_of_payment = frappe.db.get_value(
+ "Sales Invoice Payment",
+ {"parent": invoice_doc.name, "default": 1},
+ ["mode_of_payment", "type", "account"],
+ as_dict=1,
+ )
+ payment_row = invoice_doc.append("payments", {})
+ payment_row.mode_of_payment = default_mode_of_payment.mode_of_payment
+ payment_row.amount = invoice_doc.grand_total
+ payment_row.base_amount = invoice_doc.grand_total
+ payment_row.account = default_mode_of_payment.account
+ invoice_doc.submit()
- def make_sales_pos_payment(invoice_doc, pos_profile_name):
- default_mode_of_payment = frappe.db.get_value(
- "Sales Invoice Payment",
- {"parent": invoice_doc.name, "default": 1},
- ["mode_of_payment", "type", "account"],
- as_dict=1,
- )
- payment_row = invoice_doc.append("payments", {})
- payment_row.mode_of_payment = default_mode_of_payment.mode_of_payment
- payment_row.amount = invoice_doc.grand_total
- payment_row.base_amount = invoice_doc.grand_total
- payment_row.account = default_mode_of_payment.account
- invoice_doc.submit()
+ if is_grouped == 1:
+ # Make grouped Sales Invoice for POS items
+ items = []
+ for item_row in doc.materials_billed:
+ if (
+ item_row.item
+ and item_row.quantity
+ and item_row.material_status == "Bill"
+ and not item_row.sales_invoice
+ and item_row.is_pos
+ ):
+ item_dict = dict(
+ item_code=item_row.item,
+ qty=item_row.quantity,
+ rate=item_row.rate,
+ cost_center=cost_center,
+ item_tax_template=get_taxes_template(item_row.item),
+ )
+ items.append(item_dict)
+ item_row.invoiced = 1
+ make_sales_invoice(items, pos=True)
- if is_grouped == 1:
- # Make grouped Sales Invoice for POS items
- items = []
- for item_row in doc.materials_billed:
- if (
- item_row.item
- and item_row.quantity
- and item_row.material_status == "Bill"
- and not item_row.sales_invoice
- and item_row.is_pos
- ):
- item_dict = dict(
- item_code=item_row.item,
- qty=item_row.quantity,
- rate=item_row.rate,
- cost_center=cost_center,
- item_tax_template=get_taxes_template(item_row.item),
- )
- items.append(item_dict)
- item_row.invoiced = 1
- make_sales_invoice(items, pos=True)
+ # Make grouped items Sales Invoice for non-POS items
+ items = []
+ for item_row in doc.materials_billed:
+ if (
+ item_row.item
+ and item_row.quantity
+ and item_row.material_status == "Bill"
+ and not item_row.sales_invoice
+ and not item_row.is_pos
+ ):
+ item_dict = dict(
+ item_code=item_row.item,
+ qty=item_row.quantity,
+ rate=item_row.rate,
+ cost_center=cost_center,
+ item_tax_template=get_taxes_template(item_row.item),
+ )
+ items.append(item_dict)
+ item_row.invoiced = 1
+ make_sales_invoice(items, pos=False)
- # Make grouped items Sales Invoice for non-POS items
- items = []
- for item_row in doc.materials_billed:
- if (
- item_row.item
- and item_row.quantity
- and item_row.material_status == "Bill"
- and not item_row.sales_invoice
- and not item_row.is_pos
- ):
- item_dict = dict(
- item_code=item_row.item,
- qty=item_row.quantity,
- rate=item_row.rate,
- cost_center=cost_center,
- item_tax_template=get_taxes_template(item_row.item),
- )
- items.append(item_dict)
- item_row.invoiced = 1
- make_sales_invoice(items, pos=False)
+ else: # Not grouped
+ # Make Sales Invoice for non-grouped items
+ for item_row in doc.materials_billed:
+ items = []
+ if (
+ item_row.item
+ and item_row.quantity
+ and item_row.material_status == "Bill"
+ and not item_row.sales_invoice
+ ):
+ item_dict = dict(
+ item_code=item_row.item,
+ qty=item_row.quantity,
+ rate=item_row.rate,
+ cost_center=cost_center,
+ item_tax_template=get_taxes_template(item_row.item),
+ )
+ items.append(item_dict)
+ item_row.invoiced = 1
+ if item_row.is_pos:
+ pos = True
+ else:
+ pos = False
+ make_sales_invoice(items, pos)
- else: # Not grouped
- # Make Sales Invoice for non-grouped items
- for item_row in doc.materials_billed:
- items = []
- if (
- item_row.item
- and item_row.quantity
- and item_row.material_status == "Bill"
- and not item_row.sales_invoice
- ):
- item_dict = dict(
- item_code=item_row.item,
- qty=item_row.quantity,
- rate=item_row.rate,
- cost_center=cost_center,
- item_tax_template=get_taxes_template(item_row.item),
- )
- items.append(item_dict)
- item_row.invoiced = 1
- if item_row.is_pos:
- pos = True
- else:
- pos = False
- make_sales_invoice(items, pos)
+ # Make Stock Entry for Self Consumption items
+ if for_self_consumption and doc.status == "Closed":
+ items = []
+ for item_row in doc.materials_billed:
+ if (
+ item_row.item
+ and item_row.quantity
+ and item_row.material_status == "Self Consumption"
+ and not item_row.stock_entry
+ ):
+ item_dict = dict(
+ item_code=item_row.item,
+ qty=item_row.quantity,
+ rate=item_row.rate,
+ cost_center=cost_center,
+ )
+ items.append(item_dict)
+ make_stock_entry(items, False)
- # Make Stock Entry for Self Consumption items
- if for_self_consumption and doc.status == "Closed":
- items = []
- for item_row in doc.materials_billed:
- if (
- item_row.item
- and item_row.quantity
- and item_row.material_status == "Self Consumption"
- and not item_row.stock_entry
- ):
- item_dict = dict(
- item_code=item_row.item,
- qty=item_row.quantity,
- rate=item_row.rate,
- cost_center=cost_center,
- )
- items.append(item_dict)
- make_stock_entry(items, False)
@frappe.whitelist()
def get_item_rate(item, customer):
- price_list = frappe.db.get_single_value("Selling Settings", "selling_price_list")
- price_list = price_list or frappe.db.get_value(
- "Customer", customer, "default_price_list"
- )
- customer_group = frappe.db.get_value("Customer", customer, "customer_group")
- company = frappe.db.get_single_value("Global Defaults", "default_company")
- rate = get_price(item, price_list, customer_group, company)
- if rate:
- return rate["price_list_rate"]
+ price_list = frappe.db.get_single_value("Selling Settings", "selling_price_list")
+ price_list = price_list or frappe.db.get_value("Customer", customer, "default_price_list")
+ customer_group = frappe.db.get_value("Customer", customer, "customer_group")
+ company = frappe.db.get_single_value("Global Defaults", "default_company")
+ rate = get_price(item, price_list, customer_group, company)
+ if rate:
+ return rate["price_list_rate"]
@frappe.whitelist()
def get_items_group():
- property_doc = frappe.get_doc("Property Management Settings")
- items_group_list = []
- for items_group in property_doc.maintenance_item_group:
- items_group_list.append(items_group.item_group)
- return items_group_list
+ property_doc = frappe.get_doc("Property Management Settings")
+ items_group_list = []
+ for items_group in property_doc.maintenance_item_group:
+ items_group_list.append(items_group.item_group)
+ return items_group_list
def validate_materials_required(doc):
- have_items = 0
- for item in doc.materials_required:
- if item.material_status != "Self Consumption":
- have_items += 1
- if have_items > 0 and doc.status == "Closed":
- frappe.throw(
- _(
- "The materials required has items and so the job card cannot be closed. Please confirm billing status fo the materials required."
- )
- )
+ have_items = 0
+ for item in doc.materials_required:
+ if item.material_status != "Self Consumption":
+ have_items += 1
+ if have_items > 0 and doc.status == "Closed":
+ frappe.throw(
+ _(
+ "The materials required has items and so the job card cannot be closed. Please confirm billing status fo the materials required."
+ )
+ )
def validate(doc, method):
- validate_materials_required(doc)
- make_transaction(doc, for_self_consumption=False)
- if doc.status == "Closed":
- make_transaction(doc, for_self_consumption=True)
+ validate_materials_required(doc)
+ make_transaction(doc, for_self_consumption=False)
+ if doc.status == "Closed":
+ make_transaction(doc, for_self_consumption=True)
def get_taxes_template(item_code):
- item_tax_template = get_taxes_and_charges("Item", item_code)
- if len(item_tax_template) > 0:
- return item_tax_template[0]["item_tax_template"]
- else:
- return ""
+ item_tax_template = get_taxes_and_charges("Item", item_code)
+ if len(item_tax_template) > 0:
+ return item_tax_template[0]["item_tax_template"]
+ else:
+ return ""
@frappe.whitelist()
def get_stock_availability(item_code, company, is_pos):
- warehouse = ""
- if int(is_pos) == 1:
- user_pos_profile = get_pos_profile(company)
- warehouse = user_pos_profile.warehouse
- if not warehouse:
- warehouse = frappe.db.get_single_value("Stock Settings", "default_warehouse")
- latest_sle = frappe.db.sql(
- """select sum(actual_qty) as actual_qty
- from `tabStock Ledger Entry`
+ warehouse = ""
+ if int(is_pos) == 1:
+ user_pos_profile = get_pos_profile(company)
+ warehouse = user_pos_profile.warehouse
+ if not warehouse:
+ warehouse = frappe.db.get_single_value("Stock Settings", "default_warehouse")
+ latest_sle = frappe.db.sql(
+ """select sum(actual_qty) as actual_qty
+ from `tabStock Ledger Entry`
where item_code = %s and warehouse = %s
limit 1""",
- (item_code, warehouse),
- as_dict=1,
- )
+ (item_code, warehouse),
+ as_dict=1,
+ )
- sle_qty = latest_sle[0].actual_qty or 0 if latest_sle else 0
- return sle_qty
\ No newline at end of file
+ sle_qty = latest_sle[0].actual_qty or 0 if latest_sle else 0
+ return sle_qty
diff --git a/propms/lease_invoice.py b/propms/lease_invoice.py
index 5120c5b6..22eeb947 100755
--- a/propms/lease_invoice.py
+++ b/propms/lease_invoice.py
@@ -1,310 +1,274 @@
-from __future__ import unicode_literals
-from erpnext.controllers.accounts_controller import get_taxes_and_charges
-from erpnext.accounts.party import get_due_date
-from frappe.utils import add_days, today, add_months, getdate
+import json
+import traceback
+
import frappe
import frappe.permissions
import frappe.share
-import json
-import traceback
+from erpnext.accounts.party import get_due_date
+from erpnext.controllers.accounts_controller import get_taxes_and_charges
from frappe import _
+from frappe.utils import add_days, add_months, getdate, today
@frappe.whitelist()
def app_error_log(title, error):
- d = frappe.get_doc(
- {
- "doctype": "Custom Error Log",
- "title": str("User:") + str(title),
- "error": traceback.format_exc(),
- }
- )
- d = d.insert(ignore_permissions=True)
- return d
+ d = frappe.get_doc(
+ {
+ "doctype": "Custom Error Log",
+ "title": "User:" + str(title),
+ "error": traceback.format_exc(),
+ }
+ )
+ d = d.insert(ignore_permissions=True)
+ return d
@frappe.whitelist()
def makeInvoice(
- date,
- customer,
- items,
- currency=None,
- lease=None,
- lease_item=None,
- qty=None,
- schedule_start_date=None,
- doctype="Sales Invoice", # Allow to create Sales Invoice or Sales Order
+ date,
+ customer,
+ items,
+ currency=None,
+ lease=None,
+ lease_item=None,
+ qty=None,
+ schedule_start_date=None,
+ doctype="Sales Invoice", # Allow to create Sales Invoice or Sales Order
):
- """Create sales invoice from lease invoice schedule."""
- if not doctype:
- doctype = "Sales Invoice"
- try:
- if not customer:
- frappe.throw(_("Please select a Customer in Lease {0}").format(lease))
- company = frappe.get_value("Lease", lease, "company")
- default_tax_template = frappe.get_value(
- "Company", company, "default_tax_template"
- )
- if qty != int(qty):
- # it means the last invoice for the lease that may have fraction of months
- subs_end_date = frappe.get_value("Lease", lease, "end_date")
- else:
- # month qty is not fractional
- subs_end_date = add_days(add_months(schedule_start_date, qty), -1)
- items_data = frappe.parse_json(items)
- is_accrued = any(item.get("enable_deferred_revenue") for item in items_data)
- invoice_posting_date = date if (is_accrued and date) else today()
- doc = frappe.get_doc(
- dict(
- doctype=doctype,
- company=company,
- posting_date=invoice_posting_date,
- set_posting_time=1 if is_accrued else 0,
- items=items_data,
- customer=str(customer),
- due_date=getDueDate(invoice_posting_date, str(customer)),
- currency=currency,
- lease=lease,
- lease_item=lease_item,
- taxes_and_charges=default_tax_template,
- from_date=schedule_start_date,
- to_date=subs_end_date,
- cost_center=getCostCenter(lease),
- )
- )
- if doc.doctype == "Sales Order":
- sales_order_date = doc.from_date
- doc.transaction_date = sales_order_date
- doc.posting_date = sales_order_date
- doc.delivery_date = doc.to_date
- if not doc.due_date or getdate(doc.due_date) < getdate(sales_order_date):
- doc.due_date = sales_order_date
- doc.set_missing_values()
- if doc.doctype != "Sales Order":
- doc.insert()
- if doc.taxes_and_charges:
- getTax(doc)
- doc.calculate_taxes_and_totals()
- if doc.doctype == "Sales Order":
- doc.insert()
- return doc
- doc.save()
-
- if doctype == "Sales Order" and frappe.db.get_single_value("Property Management Settings", "auto_submit_sales_order"):
- doc.submit()
+ """Create sales invoice from lease invoice schedule."""
+ if not doctype:
+ doctype = "Sales Invoice"
+ try:
+ if not customer:
+ frappe.throw(_("Please select a Customer in Lease {0}").format(lease))
+ company = frappe.get_value("Lease", lease, "company")
+ default_tax_template = frappe.get_value("Company", company, "default_tax_template")
+ if qty != int(qty):
+ # it means the last invoice for the lease that may have fraction of months
+ subs_end_date = frappe.get_value("Lease", lease, "end_date")
+ else:
+ # month qty is not fractional
+ subs_end_date = add_days(add_months(schedule_start_date, qty), -1)
+ doc = frappe.get_doc(
+ dict(
+ doctype=doctype,
+ company=company,
+ posting_date=today(),
+ items=json.loads(items),
+ customer=str(customer),
+ due_date=getDueDate(today(), str(customer)),
+ currency=currency,
+ lease=lease,
+ lease_item=lease_item,
+ taxes_and_charges=default_tax_template,
+ from_date=schedule_start_date,
+ to_date=subs_end_date,
+ cost_center=getCostCenter(lease),
+ )
+ )
+ if doc.doctype == "Sales Order":
+ sales_order_date = doc.from_date
+ doc.transaction_date = sales_order_date
+ doc.posting_date = sales_order_date
+ doc.delivery_date = doc.to_date
+ if not doc.due_date or getdate(doc.due_date) < getdate(sales_order_date):
+ doc.due_date = sales_order_date
+ doc.set_missing_values()
+ if doc.doctype != "Sales Order":
+ doc.insert()
+ if doc.taxes_and_charges:
+ getTax(doc)
+ doc.calculate_taxes_and_totals()
+ if doc.doctype == "Sales Order":
+ doc.insert()
+ return doc
+ doc.save()
- # Check if auto submit is enabled in Property Management Settings
- if doctype == "Sales Invoice" and frappe.db.get_single_value("Property Management Settings", "auto_submit_sales_invoice"):
- doc.submit()
-
- return doc
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
+ if doctype == "Sales Order" and frappe.db.get_single_value(
+ "Property Management Settings", "auto_submit_sales_order"
+ ):
+ doc.submit()
+
+ # Check if auto submit is enabled in Property Management Settings
+ if doctype == "Sales Invoice" and frappe.db.get_single_value(
+ "Property Management Settings", "auto_submit_sales_invoice"
+ ):
+ doc.submit()
+
+ return doc
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def getTax(sales_invoice):
- taxes = get_taxes_and_charges(
- "Sales Taxes and Charges Template", sales_invoice.taxes_and_charges
- )
- for tax in taxes:
- sales_invoice.append("taxes", tax)
+ taxes = get_taxes_and_charges("Sales Taxes and Charges Template", sales_invoice.taxes_and_charges)
+ for tax in taxes:
+ sales_invoice.append("taxes", tax)
@frappe.whitelist()
def getDueDate(date, customer):
- return get_due_date(
- date,
- "Customer",
- str(customer),
- frappe.db.get_single_value("Global Defaults", "default_company"),
- date,
- )
+ return get_due_date(
+ date,
+ "Customer",
+ str(customer),
+ frappe.db.get_single_value("Global Defaults", "default_company"),
+ date,
+ )
@frappe.whitelist()
def getCostCenter(name):
- property_name = frappe.db.get_value("Lease", name, "property")
- return frappe.db.get_value("Property", property_name, "cost_center")
+ property_name = frappe.db.get_value("Lease", name, "property")
+ return frappe.db.get_value("Property", property_name, "cost_center")
@frappe.whitelist()
def leaseInvoiceAutoCreate():
- """Prepare data to create sales invoice from lease invoice schedule. This is called from form button as well as daily schedule"""
- try:
- # frappe.msgprint("Started")
- invoice_start_date = frappe.db.get_single_value(
- "Property Management Settings", "invoice_start_date"
- )
- lease_invoice = frappe.get_all(
- "Lease Invoice Schedule",
- filters={
- "date_to_invoice": ["between", (invoice_start_date, today())],
- "invoice_number": "",
- "sales_order_number": ""
- },
- fields=[
- "name",
- "date_to_invoice",
- "invoice_number",
- "sales_order_number",
- "parent",
- "parent",
- "invoice_item_group",
- "lease_item",
- "paid_by",
- "currency",
- ],
- order_by="parent, paid_by, invoice_item_group, date_to_invoice, currency, lease_item",
- )
- if not lease_invoice:
- return
- # frappe.msgprint("Lease being generated for " + str(lease_invoice))
- row_num = 1 # to identify the 1st line of the list
- prev_parent = ""
- prev_customer = ""
- prev_invoice_item_group = ""
- prev_date_to_invoice = ""
- lease_invoice_schedule_name = ""
- prev_currency = ""
- lease_invoice_schedule_list = []
- item_dict = []
- item_json = {}
- # frappe.msgprint(str(lease_invoice))
- for row in lease_invoice:
- # frappe.msgprint(str(invoice_item.name) + " " + str(invoice_item.lease_item))
- # Check if same lease, customer, invoice_item_group and date_to_invoice.
- # Also should not be 1st row of the list
- # frappe.msgprint(row.parent + " -- " + prev_parent + " -- " + row.paid_by + " -- " + prev_customer + " -- " + row.invoice_item_group + " -- " + prev_invoice_item_group + " -- " + str(row.date_to_invoice) + " -- " + str(prev_date_to_invoice) + " -- " + row.currency + " -- " + prev_currency)
- if (
- not (
- row.parent == prev_parent
- and row.paid_by == prev_customer
- and row.invoice_item_group == prev_invoice_item_group
- and row.date_to_invoice == prev_date_to_invoice
- and row.currency == prev_currency
- )
- and row_num != 1
- ):
- # frappe.msgprint("Creating invoice for: " + str(item_dict))
- res = makeInvoice(
- invoice_item.date_to_invoice,
- invoice_item.paid_by,
- json.dumps(item_dict),
- invoice_item.currency,
- invoice_item.parent,
- invoice_item.lease_item,
- invoice_item.qty,
- invoice_item.schedule_start_date,
- doctype=invoice_item.document_type,
- )
- # frappe.msgprint("Result: " + str(res))
- if res:
- # Loop through all list invoice names that were created and update them with same invoice number
- for lease_invoice_schedule_name in lease_invoice_schedule_list:
- # frappe.msgprint("---")
- # frappe.msgprint("The lease invoice schedule " + str(lease_invoice_schedule_name) + " would be updated with invoice number " + str(res.name) )
- frappe.db.set_value(
- "Lease Invoice Schedule",
- lease_invoice_schedule_name,
- "invoice_number"
- if res.doctype == "Sales Invoice"
- else "sales_order_number",
- res.name,
- )
- frappe.db.commit() # commit the changes to the database
- frappe.msgprint(_("Lease Invoice generated with number: {0}").format(res.name))
- item_dict = []
- lease_invoice_schedule_list = (
- []
- ) # reset the list of names of lease_invoice_schedule
- item_json = {}
- # Now that the invoice would be created if required, load the record for preparing item_dict
- invoice_item = frappe.get_doc("Lease Invoice Schedule", row.name)
- if not (invoice_item.schedule_start_date):
- invoice_item.schedule_start_date = invoice_item.date_to_invoice
- lease_end_date = frappe.get_value("Lease", invoice_item.parent, "end_date")
- item_json["item_code"] = invoice_item.lease_item
- item_json["qty"] = invoice_item.qty
- item_json["rate"] = invoice_item.rate
- item_json["cost_center"] = getCostCenter(invoice_item.parent)
- item_json["withholding_tax_rate"] = invoice_item.tax
-
- # Fetch deferred revenue configuration from Item Master and Item Defaults / Company
- item_doc = frappe.get_cached_doc("Item", invoice_item.lease_item)
- if item_doc.enable_deferred_revenue:
- item_json["enable_deferred_revenue"] = 1
- company = frappe.get_value("Lease", invoice_item.parent, "company")
- deferred_account = (
- frappe.get_cached_value(
- "Item Default",
- {"parent": invoice_item.lease_item, "company": company},
- "deferred_revenue_account",
- )
- or frappe.get_cached_value("Company", company, "default_deferred_revenue_account")
- )
- if deferred_account:
- item_json["deferred_revenue_account"] = deferred_account
- else:
- item_json["enable_deferred_revenue"] = 0
+ """Prepare data to create sales invoice from lease invoice schedule. This is called from form button as well as daily schedule"""
+ try:
+ # frappe.msgprint("Started")
+ invoice_start_date = frappe.db.get_single_value("Property Management Settings", "invoice_start_date")
+ lease_invoice = frappe.get_all(
+ "Lease Invoice Schedule",
+ filters={
+ "date_to_invoice": ["between", (invoice_start_date, today())],
+ "invoice_number": "",
+ "sales_order_number": "",
+ },
+ fields=[
+ "name",
+ "date_to_invoice",
+ "invoice_number",
+ "sales_order_number",
+ "parent",
+ "parent",
+ "invoice_item_group",
+ "lease_item",
+ "paid_by",
+ "currency",
+ ],
+ order_by="parent, paid_by, invoice_item_group, date_to_invoice, currency, lease_item",
+ )
+ # frappe.msgprint("Lease being generated for " + str(lease_invoice))
+ row_num = 1 # to identify the 1st line of the list
+ prev_parent = ""
+ prev_customer = ""
+ prev_invoice_item_group = ""
+ prev_date_to_invoice = ""
+ lease_invoice_schedule_name = ""
+ prev_currency = ""
+ invoice_item = None # previous row, used to flush a completed group
+ lease_invoice_schedule_list = []
+ item_dict = []
+ item_json = {}
+ # frappe.msgprint(str(lease_invoice))
+ for row in lease_invoice:
+ # frappe.msgprint(str(invoice_item.name) + " " + str(invoice_item.lease_item))
+ # Check if same lease, customer, invoice_item_group and date_to_invoice.
+ # Also should not be 1st row of the list
+ # frappe.msgprint(row.parent + " -- " + prev_parent + " -- " + row.paid_by + " -- " + prev_customer + " -- " + row.invoice_item_group + " -- " + prev_invoice_item_group + " -- " + str(row.date_to_invoice) + " -- " + str(prev_date_to_invoice) + " -- " + row.currency + " -- " + prev_currency)
+ if (
+ not (
+ row.parent == prev_parent
+ and row.paid_by == prev_customer
+ and row.invoice_item_group == prev_invoice_item_group
+ and row.date_to_invoice == prev_date_to_invoice
+ and row.currency == prev_currency
+ )
+ and row_num != 1
+ ):
+ # frappe.msgprint("Creating invoice for: " + str(item_dict))
+ res = makeInvoice(
+ invoice_item.date_to_invoice,
+ invoice_item.paid_by,
+ json.dumps(item_dict),
+ invoice_item.currency,
+ invoice_item.parent,
+ invoice_item.lease_item,
+ invoice_item.qty,
+ invoice_item.schedule_start_date,
+ doctype=invoice_item.document_type,
+ )
+ # frappe.msgprint("Result: " + str(res))
+ if res:
+ # Loop through all list invoice names that were created and update them with same invoice number
+ for lease_invoice_schedule_name in lease_invoice_schedule_list:
+ # frappe.msgprint("---")
+ # frappe.msgprint("The lease invoice schedule " + str(lease_invoice_schedule_name) + " would be updated with invoice number " + str(res.name) )
+ frappe.db.set_value(
+ "Lease Invoice Schedule",
+ lease_invoice_schedule_name,
+ "invoice_number" if res.doctype == "Sales Invoice" else "sales_order_number",
+ res.name,
+ )
+ frappe.db.commit() # commit the changes to the database
+ frappe.msgprint(_("Lease Invoice generated with number: {0}").format(res.name))
+ item_dict = []
+ lease_invoice_schedule_list = [] # reset the list of names of lease_invoice_schedule
+ item_json = {}
+ # Now that the invoice would be created if required, load the record for preparing item_dict
+ invoice_item = frappe.get_doc("Lease Invoice Schedule", row.name)
+ if not (invoice_item.schedule_start_date):
+ invoice_item.schedule_start_date = invoice_item.date_to_invoice
+ lease_end_date = frappe.get_value("Lease", invoice_item.parent, "end_date")
+ item_json["item_code"] = invoice_item.lease_item
+ item_json["qty"] = invoice_item.qty
+ item_json["rate"] = invoice_item.rate
+ item_json["cost_center"] = getCostCenter(invoice_item.parent)
+ item_json["withholding_tax_rate"] = invoice_item.tax
+ # item_json["enable_deferred_revenue"] = 1 # Set it to true
+ item_json["service_start_date"] = str(invoice_item.schedule_start_date)
+ if invoice_item.qty != int(invoice_item.qty):
+ # it means the last invoice for the lease that may have fraction of months
+ subs_end_date = lease_end_date
+ else:
+ # month qty is not fractional
+ subs_end_date = add_days(add_months(invoice_item.schedule_start_date, invoice_item.qty), -1)
+ item_json["service_end_date"] = str(subs_end_date)
+ # Append to the dictionary as a dict() so that the values for the new row can be set
+ item_dict.append(dict(item_json))
+ lease_invoice_schedule_list.append(invoice_item.name)
- item_json["service_start_date"] = str(invoice_item.schedule_start_date)
- if invoice_item.qty != int(invoice_item.qty):
- # it means the last invoice for the lease that may have fraction of months
- subs_end_date = lease_end_date
- else:
- # month qty is not fractional
- subs_end_date = add_days(
- add_months(invoice_item.schedule_start_date, invoice_item.qty), -1
- )
- item_json["service_end_date"] = str(subs_end_date)
- # Append to the dictionary as a dict() so that the values for the new row can be set
- item_dict.append(dict(item_json))
- lease_invoice_schedule_list.append(invoice_item.name)
+ # Remember the values for the next round
+ prev_parent = invoice_item.parent
+ prev_customer = invoice_item.paid_by
+ prev_invoice_item_group = invoice_item.invoice_item_group
+ prev_date_to_invoice = invoice_item.date_to_invoice
+ prev_currency = invoice_item.currency
+ row_num += 1 # increment by 1
+ # Create the last invoice
+ res = makeInvoice(
+ invoice_item.date_to_invoice,
+ invoice_item.paid_by,
+ json.dumps(item_dict),
+ invoice_item.currency,
+ invoice_item.parent,
+ invoice_item.lease_item,
+ invoice_item.qty,
+ invoice_item.schedule_start_date,
+ doctype=invoice_item.document_type,
+ )
+ if res:
+ # Loop through all list invoice names that were created and update them with same invoice number
+ for lease_invoice_schedule_name in lease_invoice_schedule_list:
+ # frappe.msgprint("The lease invoice schedule " + str(lease_invoice_schedule_name) + " would be updated with invoice number " + str(res.name))
+ frappe.db.set_value(
+ "Lease Invoice Schedule",
+ lease_invoice_schedule_name,
+ "invoice_number" if res.doctype == "Sales Invoice" else "sales_order_number",
+ res.name,
+ )
+ frappe.db.commit() # commit the changes to the database
+ frappe.msgprint(_("Lease Invoice generated with number: {0}").format(res.name))
- # Remember the values for the next round
- prev_parent = invoice_item.parent
- prev_customer = invoice_item.paid_by
- prev_invoice_item_group = invoice_item.invoice_item_group
- prev_date_to_invoice = invoice_item.date_to_invoice
- prev_currency = invoice_item.currency
- row_num += 1 # increment by 1
- # Create the last invoice
- res = makeInvoice(
- invoice_item.date_to_invoice,
- invoice_item.paid_by,
- json.dumps(item_dict),
- invoice_item.currency,
- invoice_item.parent,
- invoice_item.lease_item,
- invoice_item.qty,
- invoice_item.schedule_start_date,
- doctype=invoice_item.document_type,
- )
- if res:
- # Loop through all list invoice names that were created and update them with same invoice number
- for lease_invoice_schedule_name in lease_invoice_schedule_list:
- # frappe.msgprint("The lease invoice schedule " + str(lease_invoice_schedule_name) + " would be updated with invoice number " + str(res.name))
- frappe.db.set_value(
- "Lease Invoice Schedule",
- lease_invoice_schedule_name,
- "invoice_number"
- if res.doctype == "Sales Invoice"
- else "sales_order_number",
- res.name,
- )
- frappe.db.commit() # commit the changes to the database
- frappe.msgprint(_("Lease Invoice generated with number: {0}").format(res.name))
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def enqueue_lease_invoice_auto_create():
- """Enqueue leaseInvoiceAutoCreate as a background job on the 'long' queue."""
- frappe.enqueue(
- "propms.lease_invoice.leaseInvoiceAutoCreate",
- queue="long",
- now=False
- )
- return "Lease invoice auto creation has been queued. You will be notified once done."
+ """Enqueue leaseInvoiceAutoCreate as a background job on the 'long' queue."""
+ frappe.enqueue("propms.lease_invoice.leaseInvoiceAutoCreate", queue="long", now=False)
+ return "Lease invoice auto creation has been queued. You will be notified once done."
diff --git a/propms/lease_invoice_schedule.py b/propms/lease_invoice_schedule.py
index 982c4599..ff353334 100644
--- a/propms/lease_invoice_schedule.py
+++ b/propms/lease_invoice_schedule.py
@@ -1,145 +1,152 @@
-from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import add_days, today, getdate, add_months, get_first_day, get_last_day
-from propms.auto_custom import app_error_log, makeInvoiceSchedule, getDateMonthDiff
from frappe.query_builder import DocType
+from frappe.utils import add_days, add_months, get_first_day, get_last_day, getdate, today
+
+from propms.auto_custom import app_error_log, makeInvoiceSchedule
+
def get_aligned_invoice_date(date):
- """Returns the first day of the month for the given date"""
- return get_first_day(date)
+ """Returns the first day of the month for the given date"""
+ return get_first_day(date)
+
@frappe.whitelist()
def make_lease_invoice_schedule():
- # First check if make_invoice_schedule_up_to_tomorrow_only is enabled
- settings = frappe.get_single("Property Management Settings")
- make_schedule_up_to_tomorrow = settings.get("make_invoice_schedule_up_to_tomorrow_only", 0)
- if not make_schedule_up_to_tomorrow:
- return
-
- # Proceed with other computations only if the setting is enabled
- use_valid_from_date = settings.use_valid_from_date
- today_date = getdate(today())
- next_month_end = get_last_day(add_months(today_date, 1)) # End of next month
- invoice_start_date = getdate(settings.get("invoice_start_date", None))
-
- if not invoice_start_date:
- frappe.throw(_("Please set Invoice Start Date in Property Management Settings"))
-
- Lease = DocType("Lease")
- query = (
- frappe.qb.from_(Lease)
- .select(Lease.name)
- .where((Lease.start_date <= today_date)# Only check start_date, ignore end_date for inclusion
- & (Lease.lease_status == "Active") # Filter for active leases
- # Only check start_date, ignore end_date for inclusion
- )
- )
- lease_names = [row[0] for row in frappe.db.sql(query.get_sql())]
-
- for lease_name in lease_names:
- try:
- lease = frappe.get_doc("Lease", lease_name)
- lease_start = getdate(lease.start_date)
- schedule_end = getdate(lease.end_date) if lease.end_date else next_month_end
-
- # Use the later date between lease_start and invoice_start_date
- schedule_start = max(lease_start, invoice_start_date) if lease_start else invoice_start_date
- if not schedule_start:
- continue # skip if no start_date
-
- # Clean up schedule entries for removed lease items
- lease_item_names = [li.lease_item for li in lease.lease_item]
- schedule_items = frappe.get_all("Lease Invoice Schedule", filters={"parent": lease.name}, fields=["name", "lease_item"])
- for s in schedule_items:
- if s.lease_item not in lease_item_names:
- frappe.delete_doc("Lease Invoice Schedule", s.name)
-
- # Frequency map
- freq_map = {
- "Monthly": 1.0,
- "Bi-Monthly": 2.0,
- "Quarterly": 3.0,
- "6 months": 6.0,
- "Annually": 12.0,
- }
-
- idx = 1
- for item in lease.lease_item:
- if not item.frequency:
- continue
-
- freq = freq_map.get(item.frequency)
- if not freq:
- frappe.log_error(f"Invalid frequency '{item.frequency}' for item {item.lease_item} in lease {lease.name}", "Invalid Frequency")
- continue
-
- invoice_qty = float(freq)
-
- # Default to base amount
- item_amount = item.amount
-
- # Get the latest active lease item that has valid_from date
- lease_items = frappe.get_all(
- "Lease Item",
- filters={
- "parent": lease.name,
- "lease_item": item.lease_item,
- "valid_from": ("<=", today_date)
- },
- fields=["amount", "valid_from"],
- order_by="valid_from desc",
- limit=1
- )
-
- # Use amount from the most recent active lease item if found
- if use_valid_from_date and lease_items:
- item_amount = lease_items[0].amount
-
- # Get the first day of the month in which schedule_start falls
- invoice_date = get_first_day(schedule_start)
-
- # Generate invoice schedules up to next month
- while schedule_end >= invoice_date and invoice_date <= next_month_end:
- # Calculate period end as last day of the month after freq months
- invoice_period_end = get_last_day(add_months(invoice_date, freq - 1))
-
-
- # Only create schedule if date_to_invoice is between invoice start date and today + next month
- if schedule_start <= invoice_date <= next_month_end:
- exists = frappe.db.exists(
- "Lease Invoice Schedule",
- {
- "parent": lease.name,
- "lease_item": item.lease_item,
- "date_to_invoice": invoice_date,
- }
- )
-
- if not exists:
- makeInvoiceSchedule(
- invoice_date,
- item.lease_item,
- item.paid_by,
- item.lease_item,
- lease.name,
- invoice_qty,
- item_amount,
- idx,
- item.currency_code,
- item.witholding_tax,
- lease.days_to_invoice_in_advance,
- item.invoice_item_group,
- item.document_type,
- )
- frappe.db.commit()
- idx += 1
-
- # Move to first day of next period
- invoice_date = add_days(invoice_period_end, 1)
-
- frappe.msgprint(_(f"Completed invoice schedule for Lease: {lease.name}"))
-
- except Exception as e:
- frappe.msgprint(_(f"Error in {lease_name}. Check app error log."))
- app_error_log(frappe.session.user, f"{lease_name}: {str(e)}")
+ # First check if make_invoice_schedule_up_to_tomorrow_only is enabled
+ settings = frappe.get_single("Property Management Settings")
+ make_schedule_up_to_tomorrow = settings.get("make_invoice_schedule_up_to_tomorrow_only", 0)
+ if not make_schedule_up_to_tomorrow:
+ return
+
+ # Proceed with other computations only if the setting is enabled
+ use_valid_from_date = settings.use_valid_from_date
+ today_date = getdate(today())
+ next_month_end = get_last_day(add_months(today_date, 1)) # End of next month
+ invoice_start_date = getdate(settings.get("invoice_start_date", None))
+
+ if not invoice_start_date:
+ frappe.throw(_("Please set Invoice Start Date in Property Management Settings"))
+
+ Lease = DocType("Lease")
+ query = (
+ frappe.qb.from_(Lease)
+ .select(Lease.name)
+ .where(
+ (Lease.start_date <= today_date) # Only check start_date, ignore end_date for inclusion
+ & (Lease.lease_status == "Active") # Filter for active leases
+ # Only check start_date, ignore end_date for inclusion
+ )
+ )
+ lease_names = [row[0] for row in frappe.db.sql(query.get_sql())]
+
+ for lease_name in lease_names:
+ try:
+ lease = frappe.get_doc("Lease", lease_name)
+ lease_start = getdate(lease.start_date)
+ schedule_end = getdate(lease.end_date) if lease.end_date else next_month_end
+
+ # Use the later date between lease_start and invoice_start_date
+ schedule_start = max(lease_start, invoice_start_date) if lease_start else invoice_start_date
+ if not schedule_start:
+ continue # skip if no start_date
+
+ # Clean up schedule entries for removed lease items
+ lease_item_names = [li.lease_item for li in lease.lease_item]
+ schedule_items = frappe.get_all(
+ "Lease Invoice Schedule", filters={"parent": lease.name}, fields=["name", "lease_item"]
+ )
+ for s in schedule_items:
+ if s.lease_item not in lease_item_names:
+ frappe.delete_doc("Lease Invoice Schedule", s.name)
+
+ # Frequency map
+ freq_map = {
+ "Monthly": 1.0,
+ "Bi-Monthly": 2.0,
+ "Quarterly": 3.0,
+ "6 months": 6.0,
+ "Annually": 12.0,
+ }
+
+ idx = 1
+ for item in lease.lease_item:
+ if not item.frequency:
+ continue
+
+ freq = freq_map.get(item.frequency)
+ if not freq:
+ frappe.log_error(
+ f"Invalid frequency '{item.frequency}' for item {item.lease_item} in lease {lease.name}",
+ "Invalid Frequency",
+ )
+ continue
+
+ invoice_qty = float(freq)
+
+ # Default to base amount
+ item_amount = item.amount
+
+ # Get the latest active lease item that has valid_from date
+ lease_items = frappe.get_all(
+ "Lease Item",
+ filters={
+ "parent": lease.name,
+ "lease_item": item.lease_item,
+ "valid_from": ("<=", today_date),
+ },
+ fields=["amount", "valid_from"],
+ order_by="valid_from desc",
+ limit=1,
+ )
+
+ # Use amount from the most recent active lease item if found
+ if use_valid_from_date and lease_items:
+ item_amount = lease_items[0].amount
+
+ # Get the first day of the month in which schedule_start falls
+ invoice_date = get_first_day(schedule_start)
+
+ # Generate invoice schedules up to next month
+ while schedule_end >= invoice_date and invoice_date <= next_month_end:
+ # Calculate period end as last day of the month after freq months
+ invoice_period_end = get_last_day(add_months(invoice_date, freq - 1))
+
+ # Only create schedule if date_to_invoice is between invoice start date and today + next month
+ if schedule_start <= invoice_date <= next_month_end:
+ exists = frappe.db.exists(
+ "Lease Invoice Schedule",
+ {
+ "parent": lease.name,
+ "lease_item": item.lease_item,
+ "date_to_invoice": invoice_date,
+ },
+ )
+
+ if not exists:
+ makeInvoiceSchedule(
+ invoice_date,
+ item.lease_item,
+ item.paid_by,
+ item.lease_item,
+ lease.name,
+ invoice_qty,
+ item_amount,
+ idx,
+ item.currency_code,
+ item.witholding_tax,
+ lease.days_to_invoice_in_advance,
+ item.invoice_item_group,
+ item.document_type,
+ )
+ frappe.db.commit()
+ idx += 1
+
+ # Move to first day of next period
+ invoice_date = add_days(invoice_period_end, 1)
+
+ frappe.msgprint(_(f"Completed invoice schedule for Lease: {lease.name}"))
+
+ except Exception as e:
+ frappe.msgprint(_(f"Error in {lease_name}. Check app error log."))
+ app_error_log(frappe.session.user, f"{lease_name}: {str(e)}")
diff --git a/propms/modules.txt b/propms/modules.txt
index 86f0f772..f1785862 100755
--- a/propms/modules.txt
+++ b/propms/modules.txt
@@ -1 +1 @@
-Property Management Solution
\ No newline at end of file
+Property Management Solution
diff --git a/propms/pos.js b/propms/pos.js
index 602828b9..1b6857e0 100755
--- a/propms/pos.js
+++ b/propms/pos.js
@@ -426,8 +426,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
});
this.search_item.make_input();
-
-
+
+
this.cost_center = frappe.ui.form.make_control({
df: {
"fieldtype": "Data",
@@ -450,13 +450,13 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
}
}, 400);
});
-
+
this.search_cost_center = this.wrapper.find('.search-cost-center');
-
+
var dropdown_html = this.cost_center.map(function(cost_center) {
return "
"+cost_center+"";
}).join("");
-
+
this.search_item_group = this.wrapper.find('.search-item-group');
sorted_item_groups = this.get_sorted_item_groups()
var dropdown_html = sorted_item_groups.map(function(item_group) {
@@ -2113,4 +2113,4 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
frappe.throw(__("LocalStorage is full , did not save"))
}
}
-})
\ No newline at end of file
+})
diff --git a/propms/pos.py b/propms/pos.py
index 37ebcd40..bb36a3cc 100644
--- a/propms/pos.py
+++ b/propms/pos.py
@@ -1,19 +1,18 @@
-from __future__ import unicode_literals
import frappe
@frappe.whitelist()
def get_pos_data(cost_center):
- property = frappe.get_list("Property", filters={"cost_center": cost_center})
- lease = None
- one_lease = None
- if property:
- lease = frappe.get_all(
- "Lease",
- filters={"property": property[0].name},
- fields=["*"],
- order_by="end_date desc",
- )
- if lease:
- one_lease = frappe.get_doc("Lease", lease[0].name)
- return one_lease
+ property = frappe.get_list("Property", filters={"cost_center": cost_center})
+ lease = None
+ one_lease = None
+ if property:
+ lease = frappe.get_all(
+ "Lease",
+ filters={"property": property[0].name},
+ fields=["*"],
+ order_by="end_date desc",
+ )
+ if lease:
+ one_lease = frappe.get_doc("Lease", lease[0].name)
+ return one_lease
diff --git a/propms/property_increment.py b/propms/property_increment.py
index 2f0886bf..86ee3712 100644
--- a/propms/property_increment.py
+++ b/propms/property_increment.py
@@ -1,5 +1,3 @@
-from __future__ import unicode_literals
-
import math
import frappe
@@ -7,298 +5,303 @@
from frappe.utils import add_months, getdate, nowdate
from frappe.utils.data import cint, flt
-
VALID_UOM = {"Month": 1, "Year": 12}
VALID_INCREMENT_TYPES = {"Percent", "Amount"}
VALID_ROUNDING_MODES = {"Round", "Ceil", "Floor", "None"}
def validate_property_increment_settings(doc, method=None):
- if not cint(doc.get("enable_auto_increment")):
- return
+ if not cint(doc.get("enable_auto_increment")):
+ return
- horizon_months = cint(doc.get("auto_create_lease_items_for_months"))
- if horizon_months <= 0:
- frappe.throw(_("Auto Create Lease Items For Months must be greater than 0."))
+ horizon_months = cint(doc.get("auto_create_lease_items_for_months"))
+ if horizon_months <= 0:
+ frappe.throw(_("Auto Create Lease Items For Months must be greater than 0."))
- rules = doc.get("lease_increment_rules") or []
- if not rules:
- frappe.throw(_("Please add at least one Lease Increment Rule."))
+ rules = doc.get("lease_increment_rules") or []
+ if not rules:
+ frappe.throw(_("Please add at least one Lease Increment Rule."))
- rounding_mode = (doc.get("increment_rounding_mode") or "Round").strip()
- if rounding_mode not in VALID_ROUNDING_MODES:
- frappe.throw(_("Increment Rounding Mode must be one of: Round, Ceil, Floor, None."))
+ rounding_mode = (doc.get("increment_rounding_mode") or "Round").strip()
+ if rounding_mode not in VALID_ROUNDING_MODES:
+ frappe.throw(_("Increment Rounding Mode must be one of: Round, Ceil, Floor, None."))
- rounding_precision = cint(doc.get("increment_rounding_precision"))
- if rounding_precision < 0:
- frappe.throw(_("Increment Rounding Precision cannot be negative."))
+ rounding_precision = cint(doc.get("increment_rounding_precision"))
+ if rounding_precision < 0:
+ frappe.throw(_("Increment Rounding Precision cannot be negative."))
- default_effective_from = doc.get("increment_effective_from")
- seen_rules = set()
+ default_effective_from = doc.get("increment_effective_from")
+ seen_rules = set()
- for row in rules:
- if not cint(row.get("is_active")):
- continue
+ for row in rules:
+ if not cint(row.get("is_active")):
+ continue
- lease_item = row.get("lease_item")
- if not lease_item:
- frappe.throw(_("Lease Item is mandatory in Lease Increment Rules."))
+ lease_item = row.get("lease_item")
+ if not lease_item:
+ frappe.throw(_("Lease Item is mandatory in Lease Increment Rules."))
- increment_every = cint(row.get("increment_every"))
- if increment_every <= 0:
- frappe.throw(_("Increment Every must be greater than 0 for Lease Item {0}.").format(lease_item))
+ increment_every = cint(row.get("increment_every"))
+ if increment_every <= 0:
+ frappe.throw(_("Increment Every must be greater than 0 for Lease Item {0}.").format(lease_item))
- increment_uom = row.get("increment_uom")
- if increment_uom not in VALID_UOM:
- frappe.throw(_("Increment UOM must be Month or Year for Lease Item {0}.").format(lease_item))
+ increment_uom = row.get("increment_uom")
+ if increment_uom not in VALID_UOM:
+ frappe.throw(_("Increment UOM must be Month or Year for Lease Item {0}.").format(lease_item))
- increment_type = row.get("increment_type")
- if increment_type not in VALID_INCREMENT_TYPES:
- frappe.throw(_("Increment Type must be Percent or Amount for Lease Item {0}.").format(lease_item))
+ increment_type = row.get("increment_type")
+ if increment_type not in VALID_INCREMENT_TYPES:
+ frappe.throw(_("Increment Type must be Percent or Amount for Lease Item {0}.").format(lease_item))
- increment_value = flt(row.get("increment_value"))
- if increment_value <= 0:
- frappe.throw(_("Increment Value must be greater than 0 for Lease Item {0}.").format(lease_item))
+ increment_value = flt(row.get("increment_value"))
+ if increment_value <= 0:
+ frappe.throw(_("Increment Value must be greater than 0 for Lease Item {0}.").format(lease_item))
- rule_effective_from = row.get("rule_effective_from") or default_effective_from
- if not rule_effective_from:
- frappe.throw(_("Rule Effective From is required for Lease Item {0}.").format(lease_item))
+ rule_effective_from = row.get("rule_effective_from") or default_effective_from
+ if not rule_effective_from:
+ frappe.throw(_("Rule Effective From is required for Lease Item {0}.").format(lease_item))
- dedupe_key = (lease_item, str(getdate(rule_effective_from)))
- if dedupe_key in seen_rules:
- frappe.throw(_("Duplicate active rule found for Lease Item {0} on {1}.").format(lease_item, dedupe_key[1]))
- seen_rules.add(dedupe_key)
+ dedupe_key = (lease_item, str(getdate(rule_effective_from)))
+ if dedupe_key in seen_rules:
+ frappe.throw(
+ _("Duplicate active rule found for Lease Item {0} on {1}.").format(lease_item, dedupe_key[1])
+ )
+ seen_rules.add(dedupe_key)
def run_property_increment_engine():
- _run_increment_engine()
+ _run_increment_engine()
def _run_increment_engine():
- property_names = frappe.get_all("Property", filters={"enable_auto_increment": 1}, pluck="name")
- if not property_names:
- return
-
- for property_name in property_names:
- try:
- _process_property(property_name)
- except Exception:
- _log_increment_error(
- title="Property Increment Engine Error",
- details={
- "property": property_name,
- "stage": "property_loop",
- },
- )
+ property_names = frappe.get_all("Property", filters={"enable_auto_increment": 1}, pluck="name")
+ if not property_names:
+ return
+
+ for property_name in property_names:
+ try:
+ _process_property(property_name)
+ except Exception:
+ _log_increment_error(
+ title="Property Increment Engine Error",
+ details={
+ "property": property_name,
+ "stage": "property_loop",
+ },
+ )
def _process_property(property_name):
- property_doc = frappe.get_doc("Property", property_name)
-
- if not cint(property_doc.get("enable_auto_increment")):
- return
-
- horizon_months = cint(property_doc.get("auto_create_lease_items_for_months"))
- if horizon_months <= 0:
- return
-
- rules = [row for row in (property_doc.get("lease_increment_rules") or []) if cint(row.get("is_active"))]
- if not rules:
- return
-
- today = getdate(nowdate())
- horizon_end = getdate(add_months(today, horizon_months))
- default_effective_from = property_doc.get("increment_effective_from")
-
- leases = frappe.get_all(
- "Lease",
- filters={"property": property_doc.name, "lease_status": "Active", "docstatus": ["<", 2]},
- fields=["name", "start_date", "end_date"],
- )
-
- for lease_meta in leases:
- try:
- _process_lease(
- property_doc=property_doc,
- lease_name=lease_meta.name,
- rules=rules,
- default_effective_from=default_effective_from,
- today=today,
- horizon_end=horizon_end,
- )
- except Exception:
- _log_increment_error(
- title="Property Increment Engine Lease Error",
- details={
- "property": property_doc.name,
- "lease": lease_meta.name,
- "stage": "lease_loop",
- },
- )
- continue
+ property_doc = frappe.get_doc("Property", property_name)
+
+ if not cint(property_doc.get("enable_auto_increment")):
+ return
+
+ horizon_months = cint(property_doc.get("auto_create_lease_items_for_months"))
+ if horizon_months <= 0:
+ return
+
+ rules = [row for row in (property_doc.get("lease_increment_rules") or []) if cint(row.get("is_active"))]
+ if not rules:
+ return
+
+ today = getdate(nowdate())
+ horizon_end = getdate(add_months(today, horizon_months))
+ default_effective_from = property_doc.get("increment_effective_from")
+
+ leases = frappe.get_all(
+ "Lease",
+ filters={"property": property_doc.name, "lease_status": "Active", "docstatus": ["<", 2]},
+ fields=["name", "start_date", "end_date"],
+ )
+
+ for lease_meta in leases:
+ try:
+ _process_lease(
+ property_doc=property_doc,
+ lease_name=lease_meta.name,
+ rules=rules,
+ default_effective_from=default_effective_from,
+ today=today,
+ horizon_end=horizon_end,
+ )
+ except Exception:
+ _log_increment_error(
+ title="Property Increment Engine Lease Error",
+ details={
+ "property": property_doc.name,
+ "lease": lease_meta.name,
+ "stage": "lease_loop",
+ },
+ )
+ continue
def _process_lease(property_doc, lease_name, rules, default_effective_from, today, horizon_end):
- lease = frappe.get_doc("Lease", lease_name)
- lease_changed = False
-
- for rule in rules:
- rule_context = _build_rule_context(rule, default_effective_from)
- if not rule_context:
- continue
-
- item_rows = [row for row in (lease.get("lease_item") or []) if row.lease_item == rule_context["lease_item_code"]]
- if not item_rows:
- continue
-
- changed = _apply_rule_to_lease(
- lease=lease,
- property_doc=property_doc,
- rule=rule,
- rule_context=rule_context,
- item_rows=item_rows,
- today=today,
- horizon_end=horizon_end,
- )
- lease_changed = lease_changed or changed
-
- if lease_changed:
- lease.flags.ignore_validate_update_after_submit = True
- lease.save(ignore_permissions=True)
+ lease = frappe.get_doc("Lease", lease_name)
+ lease_changed = False
+
+ for rule in rules:
+ rule_context = _build_rule_context(rule, default_effective_from)
+ if not rule_context:
+ continue
+
+ item_rows = [
+ row
+ for row in (lease.get("lease_item") or [])
+ if row.lease_item == rule_context["lease_item_code"]
+ ]
+ if not item_rows:
+ continue
+
+ changed = _apply_rule_to_lease(
+ lease=lease,
+ property_doc=property_doc,
+ rule=rule,
+ rule_context=rule_context,
+ item_rows=item_rows,
+ today=today,
+ horizon_end=horizon_end,
+ )
+ lease_changed = lease_changed or changed
+
+ if lease_changed:
+ lease.flags.ignore_validate_update_after_submit = True
+ lease.save(ignore_permissions=True)
def _build_rule_context(rule, default_effective_from):
- effective_from = rule.get("rule_effective_from") or default_effective_from
- if not effective_from:
- return None
+ effective_from = rule.get("rule_effective_from") or default_effective_from
+ if not effective_from:
+ return None
- interval_months = cint(rule.get("increment_every")) * VALID_UOM.get(rule.get("increment_uom"), 0)
- if interval_months <= 0:
- return None
+ interval_months = cint(rule.get("increment_every")) * VALID_UOM.get(rule.get("increment_uom"), 0)
+ if interval_months <= 0:
+ return None
- lease_item_code = rule.get("lease_item")
- if not lease_item_code:
- return None
+ lease_item_code = rule.get("lease_item")
+ if not lease_item_code:
+ return None
- return {
- "effective_from": getdate(effective_from),
- "interval_months": interval_months,
- "lease_item_code": lease_item_code,
- }
+ return {
+ "effective_from": getdate(effective_from),
+ "interval_months": interval_months,
+ "lease_item_code": lease_item_code,
+ }
def _apply_rule_to_lease(lease, property_doc, rule, rule_context, item_rows, today, horizon_end):
- lease_changed = False
- current_rows = _get_effective_rows(lease, item_rows)
- existing_dates = {row["effective_date"] for row in current_rows}
- candidate_date = rule_context["effective_from"]
- interval_months = rule_context["interval_months"]
-
- while candidate_date <= horizon_end:
- if lease.end_date and candidate_date > getdate(lease.end_date):
- break
-
- if candidate_date >= today and candidate_date not in existing_dates:
- prev_row = _get_previous_row(current_rows, candidate_date)
- if prev_row:
- new_amount = _apply_increment(
- prev_row["amount"],
- rule.get("increment_type"),
- rule.get("increment_value"),
- property_doc.get("increment_rounding_mode"),
- property_doc.get("increment_rounding_precision"),
- )
- _append_lease_item_version(lease, prev_row["row"], candidate_date, new_amount)
- current_rows.append(
- {
- "row": lease.get("lease_item")[-1],
- "effective_date": candidate_date,
- "amount": new_amount,
- }
- )
- current_rows.sort(key=lambda x: x["effective_date"])
- existing_dates.add(candidate_date)
- lease_changed = True
-
- candidate_date = getdate(add_months(candidate_date, interval_months))
-
- return lease_changed
+ lease_changed = False
+ current_rows = _get_effective_rows(lease, item_rows)
+ existing_dates = {row["effective_date"] for row in current_rows}
+ candidate_date = rule_context["effective_from"]
+ interval_months = rule_context["interval_months"]
+
+ while candidate_date <= horizon_end:
+ if lease.end_date and candidate_date > getdate(lease.end_date):
+ break
+
+ if candidate_date >= today and candidate_date not in existing_dates:
+ prev_row = _get_previous_row(current_rows, candidate_date)
+ if prev_row:
+ new_amount = _apply_increment(
+ prev_row["amount"],
+ rule.get("increment_type"),
+ rule.get("increment_value"),
+ property_doc.get("increment_rounding_mode"),
+ property_doc.get("increment_rounding_precision"),
+ )
+ _append_lease_item_version(lease, prev_row["row"], candidate_date, new_amount)
+ current_rows.append(
+ {
+ "row": lease.get("lease_item")[-1],
+ "effective_date": candidate_date,
+ "amount": new_amount,
+ }
+ )
+ current_rows.sort(key=lambda x: x["effective_date"])
+ existing_dates.add(candidate_date)
+ lease_changed = True
+
+ candidate_date = getdate(add_months(candidate_date, interval_months))
+
+ return lease_changed
def _append_lease_item_version(lease, source_row, candidate_date, new_amount):
- lease.append(
- "lease_item",
- {
- "lease_item": source_row.lease_item,
- "frequency": source_row.frequency,
- "amount": new_amount,
- "currency_code": source_row.currency_code,
- "charge_basis": source_row.charge_basis,
- "charge_rate": source_row.charge_rate,
- "witholding_tax": source_row.witholding_tax,
- "paid_by": source_row.paid_by,
- "invoice_item_group": source_row.invoice_item_group,
- "document_type": source_row.document_type,
- "valid_from": candidate_date,
- "is_active": 0,
- },
- )
+ lease.append(
+ "lease_item",
+ {
+ "lease_item": source_row.lease_item,
+ "frequency": source_row.frequency,
+ "amount": new_amount,
+ "currency_code": source_row.currency_code,
+ "charge_basis": source_row.charge_basis,
+ "charge_rate": source_row.charge_rate,
+ "witholding_tax": source_row.witholding_tax,
+ "paid_by": source_row.paid_by,
+ "invoice_item_group": source_row.invoice_item_group,
+ "document_type": source_row.document_type,
+ "valid_from": candidate_date,
+ "is_active": 0,
+ },
+ )
def _get_effective_rows(lease, item_rows):
- lease_start = getdate(lease.start_date) if lease.start_date else getdate(nowdate())
- rows = []
- for row in item_rows:
- effective_date = getdate(row.valid_from) if row.valid_from else lease_start
- rows.append({"row": row, "effective_date": effective_date, "amount": flt(row.amount)})
- rows.sort(key=lambda x: x["effective_date"])
- return rows
+ lease_start = getdate(lease.start_date) if lease.start_date else getdate(nowdate())
+ rows = []
+ for row in item_rows:
+ effective_date = getdate(row.valid_from) if row.valid_from else lease_start
+ rows.append({"row": row, "effective_date": effective_date, "amount": flt(row.amount)})
+ rows.sort(key=lambda x: x["effective_date"])
+ return rows
def _get_previous_row(rows, candidate_date):
- previous = None
- for row in rows:
- if row["effective_date"] < candidate_date:
- previous = row
- else:
- break
- return previous
+ previous = None
+ for row in rows:
+ if row["effective_date"] < candidate_date:
+ previous = row
+ else:
+ break
+ return previous
def _apply_increment(amount, increment_type, increment_value, rounding_mode, rounding_precision):
- base_amount = flt(amount)
- increment_value = flt(increment_value)
- precision = cint(rounding_precision) if rounding_precision is not None else 2
- mode = (rounding_mode or "Round").strip()
+ base_amount = flt(amount)
+ increment_value = flt(increment_value)
+ precision = cint(rounding_precision) if rounding_precision is not None else 2
+ mode = (rounding_mode or "Round").strip()
- if increment_type == "Percent":
- new_amount = base_amount + (base_amount * increment_value / 100.0)
- else:
- new_amount = base_amount + increment_value
+ if increment_type == "Percent":
+ new_amount = base_amount + (base_amount * increment_value / 100.0)
+ else:
+ new_amount = base_amount + increment_value
- return _round_amount(new_amount, mode, precision)
+ return _round_amount(new_amount, mode, precision)
def _round_amount(amount, mode, precision):
- if mode == "None":
- return amount
- if mode == "Round":
- return round(amount, precision)
+ if mode == "None":
+ return amount
+ if mode == "Round":
+ return round(amount, precision)
- factor = 10 ** precision
- if mode == "Ceil":
- return math.ceil(amount * factor) / float(factor)
- if mode == "Floor":
- return math.floor(amount * factor) / float(factor)
+ factor = 10**precision
+ if mode == "Ceil":
+ return math.ceil(amount * factor) / float(factor)
+ if mode == "Floor":
+ return math.floor(amount * factor) / float(factor)
- return round(amount, precision)
+ return round(amount, precision)
def _log_increment_error(title, details=None):
- context = details or {}
- message_lines = ["Property Increment Engine exception."]
- for key, value in context.items():
- message_lines.append(f"{key}: {value}")
- message_lines.append("")
- message_lines.append(frappe.get_traceback())
- frappe.log_error("\n".join(message_lines), title)
+ context = details or {}
+ message_lines = ["Property Increment Engine exception."]
+ for key, value in context.items():
+ message_lines.append(f"{key}: {value}")
+ message_lines.append("")
+ message_lines.append(frappe.get_traceback())
+ frappe.log_error("\n".join(message_lines), title)
diff --git a/propms/property_management_solution/doctype/apartment_status/apartment_status.py b/propms/property_management_solution/doctype/apartment_status/apartment_status.py
index 60286c6d..56dfa1a5 100755
--- a/propms/property_management_solution/doctype/apartment_status/apartment_status.py
+++ b/propms/property_management_solution/doctype/apartment_status/apartment_status.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class ApartmentStatus(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/apartment_status/test_apartment_status.py b/propms/property_management_solution/doctype/apartment_status/test_apartment_status.py
index 8d61d6eb..4d30dd00 100755
--- a/propms/property_management_solution/doctype/apartment_status/test_apartment_status.py
+++ b/propms/property_management_solution/doctype/apartment_status/test_apartment_status.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestApartmentStatus(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/checklist_checkup_area/checklist_checkup_area.py b/propms/property_management_solution/doctype/checklist_checkup_area/checklist_checkup_area.py
index b04c40ba..807605e2 100755
--- a/propms/property_management_solution/doctype/checklist_checkup_area/checklist_checkup_area.py
+++ b/propms/property_management_solution/doctype/checklist_checkup_area/checklist_checkup_area.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class ChecklistCheckupArea(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/checklist_checkup_area/test_checklist_checkup_area.py b/propms/property_management_solution/doctype/checklist_checkup_area/test_checklist_checkup_area.py
index 7c422b99..53fdf172 100755
--- a/propms/property_management_solution/doctype/checklist_checkup_area/test_checklist_checkup_area.py
+++ b/propms/property_management_solution/doctype/checklist_checkup_area/test_checklist_checkup_area.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestChecklistCheckupArea(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/checklist_checkup_area_task/checklist_checkup_area_task.py b/propms/property_management_solution/doctype/checklist_checkup_area_task/checklist_checkup_area_task.py
index 63c2b827..04414257 100755
--- a/propms/property_management_solution/doctype/checklist_checkup_area_task/checklist_checkup_area_task.py
+++ b/propms/property_management_solution/doctype/checklist_checkup_area_task/checklist_checkup_area_task.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class ChecklistCheckupAreaTask(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/custom_error_log/custom_error_log.py b/propms/property_management_solution/doctype/custom_error_log/custom_error_log.py
index f5951e96..daf6e840 100755
--- a/propms/property_management_solution/doctype/custom_error_log/custom_error_log.py
+++ b/propms/property_management_solution/doctype/custom_error_log/custom_error_log.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class CustomErrorLog(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/custom_error_log/test_custom_error_log.py b/propms/property_management_solution/doctype/custom_error_log/test_custom_error_log.py
index 7f44d2d5..8fdcb072 100755
--- a/propms/property_management_solution/doctype/custom_error_log/test_custom_error_log.py
+++ b/propms/property_management_solution/doctype/custom_error_log/test_custom_error_log.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestCustomErrorLog(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/daily_checklist/daily_checklist.js b/propms/property_management_solution/doctype/daily_checklist/daily_checklist.js
index c0c79c9e..48b2acf7 100755
--- a/propms/property_management_solution/doctype/daily_checklist/daily_checklist.js
+++ b/propms/property_management_solution/doctype/daily_checklist/daily_checklist.js
@@ -31,4 +31,4 @@ cur_frm.fields_dict['daily_checklist_detail'].grid.get_field('job_card').get_que
['Issue', 'docstatus', '=', '1']
]
}
-};
\ No newline at end of file
+};
diff --git a/propms/property_management_solution/doctype/daily_checklist/daily_checklist.py b/propms/property_management_solution/doctype/daily_checklist/daily_checklist.py
index 408f09f6..ec8c2def 100755
--- a/propms/property_management_solution/doctype/daily_checklist/daily_checklist.py
+++ b/propms/property_management_solution/doctype/daily_checklist/daily_checklist.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class DailyChecklist(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/daily_checklist/test_daily_checklist.py b/propms/property_management_solution/doctype/daily_checklist/test_daily_checklist.py
index 8c2363d8..1de7f688 100755
--- a/propms/property_management_solution/doctype/daily_checklist/test_daily_checklist.py
+++ b/propms/property_management_solution/doctype/daily_checklist/test_daily_checklist.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestDailyChecklist(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/daily_checklist_detail/daily_checklist_detail.py b/propms/property_management_solution/doctype/daily_checklist_detail/daily_checklist_detail.py
index 673808d9..70cf0f2e 100755
--- a/propms/property_management_solution/doctype/daily_checklist_detail/daily_checklist_detail.py
+++ b/propms/property_management_solution/doctype/daily_checklist_detail/daily_checklist_detail.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class DailyChecklistDetail(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/door/door.py b/propms/property_management_solution/doctype/door/door.py
index 22712254..d52759ef 100755
--- a/propms/property_management_solution/doctype/door/door.py
+++ b/propms/property_management_solution/doctype/door/door.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class Door(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/exit/exit.py b/propms/property_management_solution/doctype/exit/exit.py
index fb2a797a..32d47269 100755
--- a/propms/property_management_solution/doctype/exit/exit.py
+++ b/propms/property_management_solution/doctype/exit/exit.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class Exit(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/exit/test_exit.py b/propms/property_management_solution/doctype/exit/test_exit.py
index 8fecabdb..31d2c407 100755
--- a/propms/property_management_solution/doctype/exit/test_exit.py
+++ b/propms/property_management_solution/doctype/exit/test_exit.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestExit(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/flooring/flooring.py b/propms/property_management_solution/doctype/flooring/flooring.py
index 70c4a7cf..33a553da 100755
--- a/propms/property_management_solution/doctype/flooring/flooring.py
+++ b/propms/property_management_solution/doctype/flooring/flooring.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class Flooring(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/guard_shift/guard_shift.py b/propms/property_management_solution/doctype/guard_shift/guard_shift.py
index 3eb758a7..8d9b0b30 100755
--- a/propms/property_management_solution/doctype/guard_shift/guard_shift.py
+++ b/propms/property_management_solution/doctype/guard_shift/guard_shift.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class GuardShift(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/guard_shift/test_guard_shift.py b/propms/property_management_solution/doctype/guard_shift/test_guard_shift.py
index e4cbb3c5..855995ab 100755
--- a/propms/property_management_solution/doctype/guard_shift/test_guard_shift.py
+++ b/propms/property_management_solution/doctype/guard_shift/test_guard_shift.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestGuardShift(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/guard_shift_location/guard_shift_location.py b/propms/property_management_solution/doctype/guard_shift_location/guard_shift_location.py
index abdf9dcc..0f4db64d 100755
--- a/propms/property_management_solution/doctype/guard_shift_location/guard_shift_location.py
+++ b/propms/property_management_solution/doctype/guard_shift_location/guard_shift_location.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class GuardShiftLocation(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/insurance/insurance.py b/propms/property_management_solution/doctype/insurance/insurance.py
index c0575bfb..02cd34c1 100755
--- a/propms/property_management_solution/doctype/insurance/insurance.py
+++ b/propms/property_management_solution/doctype/insurance/insurance.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class Insurance(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/insurance/test_insurance.py b/propms/property_management_solution/doctype/insurance/test_insurance.py
index 315b3bbd..1ba9515c 100755
--- a/propms/property_management_solution/doctype/insurance/test_insurance.py
+++ b/propms/property_management_solution/doctype/insurance/test_insurance.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestInsurance(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/issue_materials_billed/issue_materials_billed.py b/propms/property_management_solution/doctype/issue_materials_billed/issue_materials_billed.py
index 4b1014c3..a5e5a27b 100644
--- a/propms/property_management_solution/doctype/issue_materials_billed/issue_materials_billed.py
+++ b/propms/property_management_solution/doctype/issue_materials_billed/issue_materials_billed.py
@@ -1,12 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class IssueMaterialsBilled(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/issue_materials_detail/issue_materials_detail.py b/propms/property_management_solution/doctype/issue_materials_detail/issue_materials_detail.py
index bbd603e2..be98bff3 100755
--- a/propms/property_management_solution/doctype/issue_materials_detail/issue_materials_detail.py
+++ b/propms/property_management_solution/doctype/issue_materials_detail/issue_materials_detail.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class IssueMaterialsDetail(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/key/key.py b/propms/property_management_solution/doctype/key/key.py
index 0d386795..e2965972 100755
--- a/propms/property_management_solution/doctype/key/key.py
+++ b/propms/property_management_solution/doctype/key/key.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class Key(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/key_set/key_set.py b/propms/property_management_solution/doctype/key_set/key_set.py
index 5d500bff..f5ea7408 100755
--- a/propms/property_management_solution/doctype/key_set/key_set.py
+++ b/propms/property_management_solution/doctype/key_set/key_set.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class KeySet(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/key_set/test_key_set.py b/propms/property_management_solution/doctype/key_set/test_key_set.py
index 04276cf4..521cf70f 100755
--- a/propms/property_management_solution/doctype/key_set/test_key_set.py
+++ b/propms/property_management_solution/doctype/key_set/test_key_set.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestKeySet(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/key_set_detail/key_set_detail.js b/propms/property_management_solution/doctype/key_set_detail/key_set_detail.js
index 7002d899..c7fc9af3 100755
--- a/propms/property_management_solution/doctype/key_set_detail/key_set_detail.js
+++ b/propms/property_management_solution/doctype/key_set_detail/key_set_detail.js
@@ -26,4 +26,3 @@ cur_frm.set_query("key_set", function() {
}
}
});
-
diff --git a/propms/property_management_solution/doctype/key_set_detail/key_set_detail.py b/propms/property_management_solution/doctype/key_set_detail/key_set_detail.py
index b1474d4b..e3a8c4a9 100755
--- a/propms/property_management_solution/doctype/key_set_detail/key_set_detail.py
+++ b/propms/property_management_solution/doctype/key_set_detail/key_set_detail.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class KeySetDetail(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/key_set_detail/test_key_set_detail.py b/propms/property_management_solution/doctype/key_set_detail/test_key_set_detail.py
index 71848799..cc5cd01d 100755
--- a/propms/property_management_solution/doctype/key_set_detail/test_key_set_detail.py
+++ b/propms/property_management_solution/doctype/key_set_detail/test_key_set_detail.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestKeySetDetail(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/lease/lease.py b/propms/property_management_solution/doctype/lease/lease.py
index a3014347..61db1ec7 100755
--- a/propms/property_management_solution/doctype/lease/lease.py
+++ b/propms/property_management_solution/doctype/lease/lease.py
@@ -1,589 +1,564 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
import frappe
-from frappe.model.document import Document
-from frappe.utils import add_days, today, getdate, add_months, get_datetime, now, nowdate, cint
-from propms.auto_custom import app_error_log, makeInvoiceSchedule, getDateMonthDiff
from frappe import _
+from frappe.model.document import Document
+from frappe.utils import add_days, add_months, cint, get_datetime, getdate, now, nowdate, today
+
+from propms.auto_custom import app_error_log, getDateMonthDiff, makeInvoiceSchedule
class Lease(Document):
- def get_all_properties(self):
- properties = set()
- if self.property:
- properties.add(self.property)
- for item in self.lease_item:
- if item.property_unit:
- properties.add(item.property_unit)
- return list(properties)
-
- def on_submit(self):
- try:
- properties = self.get_all_properties()
- for prop in properties:
- checklist_doc = frappe.get_doc("Checklist Checkup Area", "Handover")
- if checklist_doc:
- check_list = []
- for task in checklist_doc.task:
- check = {}
- check["checklist_task"] = task.task_name
- check_list.append(check)
-
- frappe.get_doc(
- dict(
- doctype="Daily Checklist",
- area="Handover",
- checkup_date=self.start_date,
- daily_checklist_detail=check_list,
- property=prop,
- )
- ).insert()
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
-
- def validate(self):
- self.validate_days_to_invoice_in_advance()
- try:
- properties = self.get_all_properties()
- # Lease Status Validation: Prevent multiple active leases per property
- if self.lease_status == "Active":
- for prop in properties:
- max_allowed = cint(frappe.db.get_value("Property", prop, "max_active_leases")) or cint(frappe.db.get_single_value("Property Management Settings", "max_active_leases")) or 1
- # Query for other non-draft leases for the same property
- conflicting_leases = frappe.db.get_all(
- "Lease",
- filters={
- "property": prop,
- "lease_status": ["!=", "Draft"],
- "name": ["!=", self.name],
- "docstatus": ["<", 2], # Exclude cancelled
- },
- fields=["name", "end_date", "lease_status"],
- )
- active_conflicts = [
- l for l in conflicting_leases
- if not l["end_date"] or getdate(l["end_date"]) >= getdate(self.start_date)
- ]
- if len(active_conflicts) >= max_allowed:
- msg = _(
- "Cannot activate lease {0} for property {1}.
"
- "• Max Allowed Active Leases: {2}
"
- "• Currently Active Leases: {3}
"
- "Please contact Administrator if you need to increase the limit for this property."
- ).format(self.name, prop, max_allowed, len(active_conflicts))
- frappe.throw(msg, title=_("Active Lease Limit Exceeded"))
-
- for prop in properties:
- if (
- self.skip_end_date == None
- ):
- if (
- get_datetime(add_months(self.end_date, -3))
- <= get_datetime(now())
- <= get_datetime(add_months(self.end_date, 3))
- ):
- frappe.db.set_value(
- "Property", prop, "status", "Off Lease in 3 Months"
- )
- frappe.msgprint(_(f'Property "{prop}" has now been set Off Lease in 3 Months for Lease "{self.name}"'))
- elif (
- self.lease_status != "Draft"
- and (
- get_datetime(self.start_date)
- <= get_datetime(now())
- <= get_datetime(add_months(self.end_date, -3))
- )
- ):
- frappe.db.set_value("Property", prop, "status", "On Lease")
- frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"'))
- else:
- if self.lease_status != "Draft":
- frappe.db.set_value(
- "Property", prop, "status", "On Lease"
- )
- frappe.msgprint(_(f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"'))
- except frappe.ValidationError:
- raise
- except Exception as e:
- app_error_log(frappe.session.user, str(e))
- self.set_lease_status()
-
- def validate_days_to_invoice_in_advance(self):
- """Prevent changing 'Days to Invoice in Advance' once invoices have been generated."""
- if not self.is_new() and self.has_value_changed("days_to_invoice_in_advance"):
- has_generated_invoices = any(
- row.invoice_number or row.sales_order_number
- for row in (self.lease_invoice_schedule or [])
- )
- if not has_generated_invoices:
- has_generated_invoices = frappe.db.exists(
- "Lease Invoice Schedule",
- {
- "parent": self.name,
- "invoice_number": ["is", "set"],
- },
- )
- if has_generated_invoices:
- frappe.throw(
- _("Cannot change 'Days to Invoice in Advance' after invoices have been generated for this Lease."),
- title=_("Field Read Only"),
- )
-
-
- def set_lease_status(self):
- """
- Set lease status on save.
-
- Only system-controlled statuses are automatically changed:
- Upcoming, Active, Expired.
-
- All other statuses are considered manual and are not overwritten.
- """
-
- if self.lease_status not in get_system_controlled_statuses():
- return
-
- status = get_status_for_lease(self)
-
- if status:
- self.lease_status = status
+ def get_all_properties(self):
+ properties = set()
+ if self.property:
+ properties.add(self.property)
+ for item in self.lease_item:
+ if item.property_unit:
+ properties.add(item.property_unit)
+ return list(properties)
+
+ def on_submit(self):
+ try:
+ properties = self.get_all_properties()
+ for prop in properties:
+ checklist_doc = frappe.get_doc("Checklist Checkup Area", "Handover")
+ if checklist_doc:
+ check_list = []
+ for task in checklist_doc.task:
+ check = {}
+ check["checklist_task"] = task.task_name
+ check_list.append(check)
+
+ frappe.get_doc(
+ dict(
+ doctype="Daily Checklist",
+ area="Handover",
+ checkup_date=self.start_date,
+ daily_checklist_detail=check_list,
+ property=prop,
+ )
+ ).insert()
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
+
+ def validate(self):
+ try:
+ properties = self.get_all_properties()
+ # Lease Status Validation: Prevent multiple active leases per property
+ if self.lease_status == "Active":
+ for prop in properties:
+ max_allowed = (
+ cint(frappe.db.get_value("Property", prop, "max_active_leases"))
+ or cint(
+ frappe.db.get_single_value("Property Management Settings", "max_active_leases")
+ )
+ or 1
+ )
+ # Query for other non-draft leases for the same property
+ conflicting_leases = frappe.db.get_all(
+ "Lease",
+ filters={
+ "property": prop,
+ "lease_status": ["!=", "Draft"],
+ "name": ["!=", self.name],
+ "docstatus": ["<", 2], # Exclude cancelled
+ },
+ fields=["name", "end_date", "lease_status"],
+ )
+ active_conflicts = [
+ l
+ for l in conflicting_leases
+ if not l["end_date"] or getdate(l["end_date"]) >= getdate(self.start_date)
+ ]
+ if len(active_conflicts) >= max_allowed:
+ msg = _(
+ "Cannot activate lease {0} for property {1}.
"
+ "• Max Allowed Active Leases: {2}
"
+ "• Currently Active Leases: {3}
"
+ "Please contact Administrator if you need to increase the limit for this property."
+ ).format(self.name, prop, max_allowed, len(active_conflicts))
+ frappe.throw(msg, title=_("Active Lease Limit Exceeded"))
+
+ for prop in properties:
+ if self.skip_end_date is None:
+ if (
+ get_datetime(add_months(self.end_date, -3))
+ <= get_datetime(now())
+ <= get_datetime(add_months(self.end_date, 3))
+ ):
+ frappe.db.set_value("Property", prop, "status", "Off Lease in 3 Months")
+ frappe.msgprint(
+ _(
+ f'Property "{prop}" has now been set Off Lease in 3 Months for Lease "{self.name}"'
+ )
+ )
+ elif self.lease_status != "Draft" and (
+ get_datetime(self.start_date)
+ <= get_datetime(now())
+ <= get_datetime(add_months(self.end_date, -3))
+ ):
+ frappe.db.set_value("Property", prop, "status", "On Lease")
+ frappe.msgprint(
+ _(
+ f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"'
+ )
+ )
+ else:
+ if self.lease_status != "Draft":
+ frappe.db.set_value("Property", prop, "status", "On Lease")
+ frappe.msgprint(
+ _(
+ f'Property "{prop}" has now been set On Lease from Active for Lease "{self.name}"'
+ )
+ )
+ except frappe.ValidationError:
+ raise
+ except Exception as e:
+ app_error_log(frappe.session.user, str(e))
+ self.set_lease_status()
+
+ def set_lease_status(self):
+ """
+ Set lease status on save.
+
+ Only system-controlled statuses are automatically changed:
+ Upcoming, Active, Expired.
+
+ All other statuses are considered manual and are not overwritten.
+ """
+
+ if self.lease_status not in get_system_controlled_statuses():
+ return
+
+ status = get_status_for_lease(self)
+
+ if status:
+ self.lease_status = status
def get_system_controlled_statuses():
- """
- Statuses controlled by system automation.
+ """
+ Statuses controlled by system automation.
- Any lease_status outside this set is treated as manually controlled
- and will not be overwritten by validate() or the daily scheduler.
- """
+ Any lease_status outside this set is treated as manually controlled
+ and will not be overwritten by validate() or the daily scheduler.
+ """
- return {"Upcoming", "Active", "Expired"}
+ return {"Upcoming", "Active", "Expired"}
def update_lease_statuses():
- """
- Daily scheduler method.
-
- Updates only system-controlled Lease statuses:
- Upcoming, Active, Expired.
-
- Uses frappe.db.set_value() to avoid full document save hooks,
- and adds a timeline comment for audit visibility.
- """
-
- today_date = getdate(nowdate())
- system_controlled_statuses = list(get_system_controlled_statuses())
-
- leases = frappe.get_all(
- "Lease",
- fields=[
- "name",
- "lease_status",
- "start_date",
- "end_date",
- "skip_end_date",
- ],
- filters=[
- ["lease_status", "in", system_controlled_statuses],
- ["docstatus", "<", 2],
- ],
- )
-
- for lease in leases:
- old_status = lease.lease_status
- new_status = get_status_for_lease(lease, today_date)
-
- if not new_status or new_status == old_status:
- continue
-
- frappe.db.set_value(
- "Lease",
- lease.name,
- "lease_status",
- new_status,
- update_modified=True,
- )
-
- doc = frappe.get_doc("Lease", lease.name)
- doc.add_comment(
- "Info",
- _(
- "Lease Status automatically changed from {0} to {1} by daily scheduler."
- ).format(old_status or "blank", new_status),
- )
-
- frappe.db.commit()
+ """
+ Daily scheduler method.
+
+ Updates only system-controlled Lease statuses:
+ Upcoming, Active, Expired.
+
+ Uses frappe.db.set_value() to avoid full document save hooks,
+ and adds a timeline comment for audit visibility.
+ """
+
+ today_date = getdate(nowdate())
+ system_controlled_statuses = list(get_system_controlled_statuses())
+
+ leases = frappe.get_all(
+ "Lease",
+ fields=[
+ "name",
+ "lease_status",
+ "start_date",
+ "end_date",
+ "skip_end_date",
+ ],
+ filters=[
+ ["lease_status", "in", system_controlled_statuses],
+ ["docstatus", "<", 2],
+ ],
+ )
+
+ for lease in leases:
+ old_status = lease.lease_status
+ new_status = get_status_for_lease(lease, today_date)
+
+ if not new_status or new_status == old_status:
+ continue
+
+ frappe.db.set_value(
+ "Lease",
+ lease.name,
+ "lease_status",
+ new_status,
+ update_modified=True,
+ )
+
+ doc = frappe.get_doc("Lease", lease.name)
+ doc.add_comment(
+ "Info",
+ _("Lease Status automatically changed from {0} to {1} by daily scheduler.").format(
+ old_status or "blank", new_status
+ ),
+ )
+
+ frappe.db.commit()
def get_status_for_lease(lease, today_date=None):
- """
- Return calculated Lease Status.
+ """
+ Return calculated Lease Status.
- Rules:
- - Future start_date => Upcoming
- - start_date <= today and end_date >= today => Active
- - end_date < today => Expired
- - If skip_end_date is checked, do not mark as Expired
- - end_date equal to today remains Active until the next day
- """
+ Rules:
+ - Future start_date => Upcoming
+ - start_date <= today and end_date >= today => Active
+ - end_date < today => Expired
+ - If skip_end_date is checked, do not mark as Expired
+ - end_date equal to today remains Active until the next day
+ """
- today_date = today_date or getdate(nowdate())
+ today_date = today_date or getdate(nowdate())
- start_date = getdate(lease.start_date) if lease.start_date else None
- end_date = getdate(lease.end_date) if lease.end_date else None
- skip_end_date = bool(lease.skip_end_date)
+ start_date = getdate(lease.start_date) if lease.start_date else None
+ end_date = getdate(lease.end_date) if lease.end_date else None
+ skip_end_date = bool(lease.skip_end_date)
- if start_date and start_date > today_date:
- return "Upcoming"
+ if start_date and start_date > today_date:
+ return "Upcoming"
- if not skip_end_date and end_date and end_date < today_date:
- return "Expired"
+ if not skip_end_date and end_date and end_date < today_date:
+ return "Expired"
- if start_date and start_date <= today_date:
- if skip_end_date or not end_date or end_date >= today_date:
- return "Active"
+ if start_date and start_date <= today_date:
+ if skip_end_date or not end_date or end_date >= today_date:
+ return "Active"
- return None
+ return None
@frappe.whitelist()
def getAllLease():
- # Below is temporarily created to manually run through all lease and refresh lease invoice schedule. Hardcoded to start from 1st Jan 2020.
- frappe.msgprint(_(
- "The task of making lease invoice schedule for all users has been sent for background processing."
- ))
- invoice_start_date = frappe.db.get_single_value(
- "Property Management Settings", "invoice_start_date"
- )
- lease_list = frappe.get_all(
- "Lease", filters={"end_date": (">=", invoice_start_date)}, fields=["name"]
- )
- # frappe.msgprint("Working on lease_list" + str(lease_list))
- lease_list_len = len(lease_list)
- frappe.msgprint(_("Total number of lease to be processed is {0}").format(lease_list_len))
- for lease in lease_list:
- make_lease_invoice_schedule(lease.name)
+ # Below is temporarily created to manually run through all lease and refresh lease invoice schedule. Hardcoded to start from 1st Jan 2020.
+ frappe.msgprint(
+ _("The task of making lease invoice schedule for all users has been sent for background processing.")
+ )
+ invoice_start_date = frappe.db.get_single_value("Property Management Settings", "invoice_start_date")
+ lease_list = frappe.get_all("Lease", filters={"end_date": (">=", invoice_start_date)}, fields=["name"])
+ # frappe.msgprint("Working on lease_list" + str(lease_list))
+ lease_list_len = len(lease_list)
+ frappe.msgprint(_("Total number of lease to be processed is {0}").format(lease_list_len))
+ for lease in lease_list:
+ make_lease_invoice_schedule(lease.name)
# def on_update(self):
@frappe.whitelist()
def make_lease_invoice_schedule(leasedoc):
- # frappe.msgprint("This is the parameter passed: " + str(leasedoc))
- lease = frappe.get_doc("Lease", str(leasedoc))
- try:
- # Delete unnecessary records after lease end date
- lease_invoice_schedule_list = frappe.get_list(
- "Lease Invoice Schedule",
- fields=[
- "name",
- "parent",
- "lease_item",
- "invoice_number",
- "date_to_invoice",
- ],
- filters={"parent": lease.name, "date_to_invoice": (">", lease.end_date)}, parent_doctype='Lease',
- )
- for lease_invoice_schedule in lease_invoice_schedule_list:
- frappe.delete_doc("Lease Invoice Schedule", lease_invoice_schedule.name)
- # Only process lease that items and is current
- if len(lease.lease_item) >= 1 and lease.end_date >= getdate(today()):
- # Clean up records that are no longer required, i.e. of unnecessary lease items and unnecessary dates
- # Records before Invoice Start Date
- invoice_start_date = frappe.db.get_single_value(
- "Property Management Settings", "invoice_start_date"
- )
- lease_invoice_schedule_list = frappe.get_list(
- "Lease Invoice Schedule",
- fields=["name", "parent", "invoice_number", "date_to_invoice"],
- filters={
- "parent": lease.name,
- "date_to_invoice": ("<", invoice_start_date),
- }, parent_doctype='Lease',
- )
- # frappe.msgprint("Records before Invoice Start Date " + str(lease_invoice_schedule_list))
- for lease_invoice_schedule in lease_invoice_schedule_list:
- # frappe.msgprint("Deleting Record before Invoice Start Date " + str(invoice_start_date) + str(lease_invoice_schedule.name))
- frappe.delete_doc("Lease Invoice Schedule", lease_invoice_schedule.name)
- # Records of lease_items that no longer existing in lease.lease_item
- lease_invoice_schedule_list = frappe.get_list(
- "Lease Invoice Schedule",
- fields=[
- "name",
- "parent",
- "lease_item",
- "invoice_number",
- "date_to_invoice",
- ],
- filters={"parent": lease.name}, parent_doctype='Lease',
- )
- lease_items_list = frappe.get_list(
- "Lease Item",
- fields=["name", "parent", "lease_item"],
- filters={"parent": lease.name}, parent_doctype='Lease',
- )
- # Create list of lease items that are part of lease.lease_item
- lease_item_name_list = [
- lease_item["lease_item"] for lease_item in lease_items_list
- ]
- # frappe.msgprint(str(lease_item_list))
- for lease_invoice_schedule in lease_invoice_schedule_list:
- if lease_invoice_schedule.lease_item not in lease_item_name_list:
- # frappe.msgprint("This lease item will be removed from invoice schedule " + str(lease_invoice_schedule.lease_item))
- frappe.delete_doc(
- "Lease Invoice Schedule", lease_invoice_schedule.name
- )
- item_invoice_frequency = {
- "Monthly": 1.00, # .00 to make it float type
- "Bi-Monthly": 2.00,
- "Quarterly": 3.00,
- "6 months": 6.00,
- "Annually": 12.00,
- }
- idx = 1
- for item in lease.lease_item:
- # frappe.msgprint("Lease item being processed: " + str(item.lease_item))
- lease_invoice_schedule_list = frappe.get_all(
- "Lease Invoice Schedule",
- fields=[
- "name",
- "parent",
- "lease_item",
- "schedule_start_date",
- "qty",
- "invoice_number",
- "date_to_invoice",
- ],
- filters={"parent": lease.name, "lease_item": item.lease_item},
- order_by="date_to_invoice",
- )
- # frappe.msgprint(str(lease_invoice_schedule_list))
- # Get the latest item frequency incase lease was changed.
- frequency_factor = item_invoice_frequency.get(
- item.frequency, "Invalid frequency"
- )
- # frappe.msgprint("Next Invoice date calculated: " + str(invoice_date))
- if frequency_factor == "Invalid frequency":
- message = (
- "Invalid frequency: "
- + str(item.frequency)
- + " for "
- + str(leasedoc)
- + " not found. Contact the developers!"
- )
- frappe.log_error("Frequency incorrect", message)
- break
- invoice_qty = float(frequency_factor)
- end_date = lease.end_date
- invoice_date = lease.start_date
- # Find out the first invoice date on or after Invoice Start Date process.
- while end_date >= invoice_date and invoice_date < invoice_start_date:
- invoice_period_end = add_days(
- add_months(invoice_date, frequency_factor), -1
- )
- # Set invoice_Qty as appropriate fraction of frequency_factor
- if invoice_period_end > end_date:
- invoice_qty = getDateMonthDiff(invoice_date, end_date, 1)
- # frappe.msgprint("Invoice quantity corrected as " + str(invoice_qty))
- invoice_date = add_days(invoice_period_end, 1)
- # If there is no lease_invoice_schedule_list found, i.e. it is fresh new list to be created
- if not lease_invoice_schedule_list:
- while end_date >= invoice_date:
- invoice_period_end = add_days(
- add_months(invoice_date, frequency_factor), -1
- )
- # frappe.msgprint("Invoice period end: " + str(invoice_period_end) + "--- Invoice Date: " + str(invoice_date))
- # frappe.msgprint("End Date: " + str(end_date))
- # set invoice_Qty as appropriate fraction of frequency_factor
- if invoice_period_end > end_date:
- invoice_qty = getDateMonthDiff(invoice_date, end_date, 1)
- # frappe.msgprint("Invoice quantity corrected as " + str(invoice_qty))
- # frappe.msgprint("Making Fresh Invoice Schedule for " + str(invoice_date)
- # + ", Quantity calculated: " + str(invoice_qty))
- makeInvoiceSchedule(
- invoice_date,
- item.lease_item,
- item.paid_by,
- item.lease_item,
- lease.name,
- invoice_qty,
- item.amount,
- idx,
- item.currency_code,
- item.witholding_tax,
- lease.days_to_invoice_in_advance,
- item.invoice_item_group,
- item.document_type,
- )
- idx += 1
- invoice_date = add_days(invoice_period_end, 1)
- for lease_invoice_schedule in lease_invoice_schedule_list:
- # frappe.msgprint("Upon entering lease_invoice_schedule_list - Date to invoice: " + str(lease_invoice_schedule.date_to_invoice)
- # + " and invoice date to process is " + str(invoice_date))
- if not (lease_invoice_schedule.schedule_start_date):
- lease_invoice_schedule.schedule_start_date = (
- lease_invoice_schedule.date_to_invoice
- )
- while (
- end_date >= invoice_date
- and lease_invoice_schedule.schedule_start_date > invoice_date
- ):
- invoice_period_end = add_days(
- add_months(invoice_date, frequency_factor), -1
- )
- # frappe.msgprint("Upon entering Invoice period end: " + str(invoice_period_end) + "--- Invoice Date: " + str(invoice_date))
- # frappe.msgprint("End Date: " + str(end_date))
- # set invoice_Qty as appropriate fraction of frequency_factor
- if invoice_period_end > end_date:
- invoice_qty = getDateMonthDiff(invoice_date, end_date, 1)
- # frappe.msgprint("Invoice quantity corrected as " + str(invoice_qty))
- # frappe.msgprint("Making Pre Invoice Schedule for " + str(invoice_date) + ", Quantity calculated: " + str(invoice_qty))
- makeInvoiceSchedule(
- invoice_date,
- item.lease_item,
- item.paid_by,
- item.lease_item,
- lease.name,
- invoice_qty,
- item.amount,
- idx,
- item.currency_code,
- item.witholding_tax,
- lease.days_to_invoice_in_advance,
- item.invoice_item_group,
- item.document_type,
- )
- idx += 1
- invoice_date = add_days(invoice_period_end, 1)
- # frappe.msgprint(str(lease_invoice_schedule))
- # If the record already exists and invoice is generated
- if (
- lease_invoice_schedule.invoice_number is not None
- and lease_invoice_schedule.invoice_number != ""
- ):
- # frappe.msgprint("Lease Invoice Schedule retained: " + lease_invoice_schedule.name
- # + " for invoice number: " + str(lease_invoice_schedule.invoice_number)
- # + " dated " + str(lease_invoice_schedule.date_to_invoice)
- # )
- # Set months as rounded up by 1 if the month is a fraction (last invoice for the lease item already created).
- # Above needed to escape from infinite loop of rounded down date and therefore never reaching end of the lease.
- if lease_invoice_schedule.qty != round(
- lease_invoice_schedule.qty, 0
- ):
- add_months_value = round(lease_invoice_schedule.qty, 0) + 1
- else:
- add_months_value = lease_invoice_schedule.qty
- # frappe.msgprint("Add Months Value" + str(add_months_value) + " due to qty = " + str(lease_invoice_schedule.qty))
- invoice_date = add_months(
- lease_invoice_schedule.schedule_start_date, add_months_value
- )
- # Set sequence to show it on the top
- frappe.db.set_value(
- "Lease Invoice Schedule",
- lease_invoice_schedule.name,
- "idx",
- idx,
- )
- idx += 1
- # If the invoice is not created
- else:
- # frappe.msgprint("Deleting schedule :" + lease_invoice_schedule.name + " dated: " + str(lease_invoice_schedule.date_to_invoice) + " for " + str(lease_invoice_schedule.lease_item))
- frappe.delete_doc(
- "Lease Invoice Schedule", lease_invoice_schedule.name
- )
- # frappe.msgprint("first invoice_date: " + str(invoice_date), "Lease Invoice Schedule")
- while end_date >= invoice_date:
- invoice_period_end = add_days(
- add_months(invoice_date, frequency_factor), -1
- )
- # frappe.msgprint("Invoice period end: " + str(invoice_period_end) + "--- Invoice Date: " + str(invoice_date))
- # frappe.msgprint("End Date: " + str(end_date))
- # set invoice_Qty as appropriate fraction of frequency_factor
- if invoice_period_end > end_date:
- invoice_qty = getDateMonthDiff(invoice_date, end_date, 1)
- # frappe.msgprint("Invoice quantity corrected as " + str(invoice_qty))
- # frappe.msgprint("Making Post Invoice Schedule for " + str(invoice_date) + ", Quantity calculated: " + str(invoice_qty))
- makeInvoiceSchedule(
- invoice_date,
- item.lease_item,
- item.paid_by,
- item.lease_item,
- lease.name,
- invoice_qty,
- item.amount,
- idx,
- item.currency_code,
- item.witholding_tax,
- lease.days_to_invoice_in_advance,
- item.invoice_item_group,
- item.document_type,
- )
- idx += 1
- invoice_date = add_days(invoice_period_end, 1)
-
- frappe.msgprint("Completed making of invoice schedule.")
-
- except Exception as e:
- frappe.msgprint("Exception error! Check app error log.")
- app_error_log(frappe.session.user, str(e))
+ # frappe.msgprint("This is the parameter passed: " + str(leasedoc))
+ lease = frappe.get_doc("Lease", str(leasedoc))
+ try:
+ # Delete unnecessary records after lease end date
+ lease_invoice_schedule_list = frappe.get_list(
+ "Lease Invoice Schedule",
+ fields=[
+ "name",
+ "parent",
+ "lease_item",
+ "invoice_number",
+ "date_to_invoice",
+ ],
+ filters={"parent": lease.name, "date_to_invoice": (">", lease.end_date)},
+ parent_doctype="Lease",
+ )
+ for lease_invoice_schedule in lease_invoice_schedule_list:
+ frappe.delete_doc("Lease Invoice Schedule", lease_invoice_schedule.name)
+ # Only process lease that items and is current
+ if len(lease.lease_item) >= 1 and lease.end_date >= getdate(today()):
+ # Clean up records that are no longer required, i.e. of unnecessary lease items and unnecessary dates
+ # Records before Invoice Start Date
+ invoice_start_date = frappe.db.get_single_value(
+ "Property Management Settings", "invoice_start_date"
+ )
+ lease_invoice_schedule_list = frappe.get_list(
+ "Lease Invoice Schedule",
+ fields=["name", "parent", "invoice_number", "date_to_invoice"],
+ filters={
+ "parent": lease.name,
+ "date_to_invoice": ("<", invoice_start_date),
+ },
+ parent_doctype="Lease",
+ )
+ # frappe.msgprint("Records before Invoice Start Date " + str(lease_invoice_schedule_list))
+ for lease_invoice_schedule in lease_invoice_schedule_list:
+ # frappe.msgprint("Deleting Record before Invoice Start Date " + str(invoice_start_date) + str(lease_invoice_schedule.name))
+ frappe.delete_doc("Lease Invoice Schedule", lease_invoice_schedule.name)
+ # Records of lease_items that no longer existing in lease.lease_item
+ lease_invoice_schedule_list = frappe.get_list(
+ "Lease Invoice Schedule",
+ fields=[
+ "name",
+ "parent",
+ "lease_item",
+ "invoice_number",
+ "date_to_invoice",
+ ],
+ filters={"parent": lease.name},
+ parent_doctype="Lease",
+ )
+ lease_items_list = frappe.get_list(
+ "Lease Item",
+ fields=["name", "parent", "lease_item"],
+ filters={"parent": lease.name},
+ parent_doctype="Lease",
+ )
+ # Create list of lease items that are part of lease.lease_item
+ lease_item_name_list = [lease_item["lease_item"] for lease_item in lease_items_list]
+ # frappe.msgprint(str(lease_item_list))
+ for lease_invoice_schedule in lease_invoice_schedule_list:
+ if lease_invoice_schedule.lease_item not in lease_item_name_list:
+ # frappe.msgprint("This lease item will be removed from invoice schedule " + str(lease_invoice_schedule.lease_item))
+ frappe.delete_doc("Lease Invoice Schedule", lease_invoice_schedule.name)
+ item_invoice_frequency = {
+ "Monthly": 1.00, # .00 to make it float type
+ "Bi-Monthly": 2.00,
+ "Quarterly": 3.00,
+ "6 months": 6.00,
+ "Annually": 12.00,
+ }
+ idx = 1
+ for item in lease.lease_item:
+ # frappe.msgprint("Lease item being processed: " + str(item.lease_item))
+ lease_invoice_schedule_list = frappe.get_all(
+ "Lease Invoice Schedule",
+ fields=[
+ "name",
+ "parent",
+ "lease_item",
+ "schedule_start_date",
+ "qty",
+ "invoice_number",
+ "date_to_invoice",
+ ],
+ filters={"parent": lease.name, "lease_item": item.lease_item},
+ order_by="date_to_invoice",
+ )
+ # frappe.msgprint(str(lease_invoice_schedule_list))
+ # Get the latest item frequency incase lease was changed.
+ frequency_factor = item_invoice_frequency.get(item.frequency, "Invalid frequency")
+ # frappe.msgprint("Next Invoice date calculated: " + str(invoice_date))
+ if frequency_factor == "Invalid frequency":
+ message = (
+ "Invalid frequency: "
+ + str(item.frequency)
+ + " for "
+ + str(leasedoc)
+ + " not found. Contact the developers!"
+ )
+ frappe.log_error("Frequency incorrect", message)
+ break
+ invoice_qty = float(frequency_factor)
+ end_date = lease.end_date
+ invoice_date = lease.start_date
+ # Find out the first invoice date on or after Invoice Start Date process.
+ while end_date >= invoice_date and invoice_date < invoice_start_date:
+ invoice_period_end = add_days(add_months(invoice_date, frequency_factor), -1)
+ # Set invoice_Qty as appropriate fraction of frequency_factor
+ if invoice_period_end > end_date:
+ invoice_qty = getDateMonthDiff(invoice_date, end_date, 1)
+ # frappe.msgprint("Invoice quantity corrected as " + str(invoice_qty))
+ invoice_date = add_days(invoice_period_end, 1)
+ # If there is no lease_invoice_schedule_list found, i.e. it is fresh new list to be created
+ if not lease_invoice_schedule_list:
+ while end_date >= invoice_date:
+ invoice_period_end = add_days(add_months(invoice_date, frequency_factor), -1)
+ # frappe.msgprint("Invoice period end: " + str(invoice_period_end) + "--- Invoice Date: " + str(invoice_date))
+ # frappe.msgprint("End Date: " + str(end_date))
+ # set invoice_Qty as appropriate fraction of frequency_factor
+ if invoice_period_end > end_date:
+ invoice_qty = getDateMonthDiff(invoice_date, end_date, 1)
+ # frappe.msgprint("Invoice quantity corrected as " + str(invoice_qty))
+ # frappe.msgprint("Making Fresh Invoice Schedule for " + str(invoice_date)
+ # + ", Quantity calculated: " + str(invoice_qty))
+ makeInvoiceSchedule(
+ invoice_date,
+ item.lease_item,
+ item.paid_by,
+ item.lease_item,
+ lease.name,
+ invoice_qty,
+ item.amount,
+ idx,
+ item.currency_code,
+ item.witholding_tax,
+ lease.days_to_invoice_in_advance,
+ item.invoice_item_group,
+ item.document_type,
+ )
+ idx += 1
+ invoice_date = add_days(invoice_period_end, 1)
+ for lease_invoice_schedule in lease_invoice_schedule_list:
+ # frappe.msgprint("Upon entering lease_invoice_schedule_list - Date to invoice: " + str(lease_invoice_schedule.date_to_invoice)
+ # + " and invoice date to process is " + str(invoice_date))
+ if not (lease_invoice_schedule.schedule_start_date):
+ lease_invoice_schedule.schedule_start_date = lease_invoice_schedule.date_to_invoice
+ while (
+ end_date >= invoice_date and lease_invoice_schedule.schedule_start_date > invoice_date
+ ):
+ invoice_period_end = add_days(add_months(invoice_date, frequency_factor), -1)
+ # frappe.msgprint("Upon entering Invoice period end: " + str(invoice_period_end) + "--- Invoice Date: " + str(invoice_date))
+ # frappe.msgprint("End Date: " + str(end_date))
+ # set invoice_Qty as appropriate fraction of frequency_factor
+ if invoice_period_end > end_date:
+ invoice_qty = getDateMonthDiff(invoice_date, end_date, 1)
+ # frappe.msgprint("Invoice quantity corrected as " + str(invoice_qty))
+ # frappe.msgprint("Making Pre Invoice Schedule for " + str(invoice_date) + ", Quantity calculated: " + str(invoice_qty))
+ makeInvoiceSchedule(
+ invoice_date,
+ item.lease_item,
+ item.paid_by,
+ item.lease_item,
+ lease.name,
+ invoice_qty,
+ item.amount,
+ idx,
+ item.currency_code,
+ item.witholding_tax,
+ lease.days_to_invoice_in_advance,
+ item.invoice_item_group,
+ item.document_type,
+ )
+ idx += 1
+ invoice_date = add_days(invoice_period_end, 1)
+ # frappe.msgprint(str(lease_invoice_schedule))
+ # If the record already exists and invoice is generated
+ if (
+ lease_invoice_schedule.invoice_number is not None
+ and lease_invoice_schedule.invoice_number != ""
+ ):
+ # frappe.msgprint("Lease Invoice Schedule retained: " + lease_invoice_schedule.name
+ # + " for invoice number: " + str(lease_invoice_schedule.invoice_number)
+ # + " dated " + str(lease_invoice_schedule.date_to_invoice)
+ # )
+ # Set months as rounded up by 1 if the month is a fraction (last invoice for the lease item already created).
+ # Above needed to escape from infinite loop of rounded down date and therefore never reaching end of the lease.
+ if lease_invoice_schedule.qty != round(lease_invoice_schedule.qty, 0):
+ add_months_value = round(lease_invoice_schedule.qty, 0) + 1
+ else:
+ add_months_value = lease_invoice_schedule.qty
+ # frappe.msgprint("Add Months Value" + str(add_months_value) + " due to qty = " + str(lease_invoice_schedule.qty))
+ invoice_date = add_months(
+ lease_invoice_schedule.schedule_start_date, add_months_value
+ )
+ # Set sequence to show it on the top
+ frappe.db.set_value(
+ "Lease Invoice Schedule",
+ lease_invoice_schedule.name,
+ "idx",
+ idx,
+ )
+ idx += 1
+ # If the invoice is not created
+ else:
+ # frappe.msgprint("Deleting schedule :" + lease_invoice_schedule.name + " dated: " + str(lease_invoice_schedule.date_to_invoice) + " for " + str(lease_invoice_schedule.lease_item))
+ frappe.delete_doc("Lease Invoice Schedule", lease_invoice_schedule.name)
+ # frappe.msgprint("first invoice_date: " + str(invoice_date), "Lease Invoice Schedule")
+ while end_date >= invoice_date:
+ invoice_period_end = add_days(add_months(invoice_date, frequency_factor), -1)
+ # frappe.msgprint("Invoice period end: " + str(invoice_period_end) + "--- Invoice Date: " + str(invoice_date))
+ # frappe.msgprint("End Date: " + str(end_date))
+ # set invoice_Qty as appropriate fraction of frequency_factor
+ if invoice_period_end > end_date:
+ invoice_qty = getDateMonthDiff(invoice_date, end_date, 1)
+ # frappe.msgprint("Invoice quantity corrected as " + str(invoice_qty))
+ # frappe.msgprint("Making Post Invoice Schedule for " + str(invoice_date) + ", Quantity calculated: " + str(invoice_qty))
+ makeInvoiceSchedule(
+ invoice_date,
+ item.lease_item,
+ item.paid_by,
+ item.lease_item,
+ lease.name,
+ invoice_qty,
+ item.amount,
+ idx,
+ item.currency_code,
+ item.witholding_tax,
+ lease.days_to_invoice_in_advance,
+ item.invoice_item_group,
+ item.document_type,
+ )
+ idx += 1
+ invoice_date = add_days(invoice_period_end, 1)
+
+ frappe.msgprint("Completed making of invoice schedule.")
+
+ except Exception as e:
+ frappe.msgprint("Exception error! Check app error log.")
+ app_error_log(frappe.session.user, str(e))
@frappe.whitelist()
def initiate_lease_renewal(source_lease_name):
- # 1. Permission checks
- if not any(r in frappe.get_roles() for r in ["Property Manager", "System Manager"]):
- frappe.throw(_("You are not authorized to renew leases. Only Property Managers and System Managers can perform this action."), frappe.PermissionError)
-
- # 2. Fetch source document
- source_doc = frappe.get_doc("Lease", source_lease_name)
-
- # 3. Status checks
- if source_doc.lease_status not in ["Active", "Expired"]:
- frappe.throw(_("Lease {0} is not eligible for renewal. Status must be Active or Expired.").format(source_lease_name), frappe.ValidationError)
-
- # 4. Duplicate renewal check (ignoring terminated or aborted renewals)
- duplicate_exists = frappe.db.exists("Lease", {
- "renewed_from": source_lease_name,
- "lease_status": ["not in", ["Not Materialized", "Terminated"]]
- })
- if duplicate_exists:
- frappe.throw(_("A renewal lease already exists for this lease: {0}").format(duplicate_exists), frappe.ValidationError)
-
- # 5. Clone Lease using standard frappe.copy_doc
- new_lease = frappe.copy_doc(source_doc)
- new_lease.set("lease_invoice_schedule", []) # Clear old invoice schedules
-
- # Set renewal reference fields
- new_lease.renewed_from = source_lease_name
- new_lease.lease_status = "Renewal to Previous Lease"
- new_lease.renewal_initiated_by = frappe.session.user
-
- # Calculate dates
- if source_doc.end_date:
- new_lease.start_date = add_days(source_doc.end_date, 1)
- if source_doc.start_date:
- duration_days = (getdate(source_doc.end_date) - getdate(source_doc.start_date)).days
- new_lease.end_date = add_days(new_lease.start_date, duration_days)
- else:
- new_lease.start_date = today()
-
- # Update valid_from date for items
- for item in new_lease.lease_item:
- item.valid_from = new_lease.start_date
-
- # Insert and save the new Lease as draft
- new_lease.insert(ignore_permissions=True)
-
- # Post a message/comment to the old lease with initiator and link details
- comment_text = _("Lease renewal draft {0} has been initiated by {1} on {2}.").format(
- new_lease.name,
- frappe.session.user,
- frappe.utils.formatdate(today())
- )
- source_doc.add_comment(text=comment_text)
-
- return new_lease.name
-
-
+ # 1. Permission checks
+ if not any(r in frappe.get_roles() for r in ["Property Manager", "System Manager"]):
+ frappe.throw(
+ _(
+ "You are not authorized to renew leases. Only Property Managers and System Managers can perform this action."
+ ),
+ frappe.PermissionError,
+ )
+
+ # 2. Fetch source document
+ source_doc = frappe.get_doc("Lease", source_lease_name)
+
+ # 3. Status checks
+ if source_doc.lease_status not in ["Active", "Expired"]:
+ frappe.throw(
+ _("Lease {0} is not eligible for renewal. Status must be Active or Expired.").format(
+ source_lease_name
+ ),
+ frappe.ValidationError,
+ )
+
+ # 4. Duplicate renewal check (ignoring terminated or aborted renewals)
+ duplicate_exists = frappe.db.exists(
+ "Lease",
+ {"renewed_from": source_lease_name, "lease_status": ["not in", ["Not Materialized", "Terminated"]]},
+ )
+ if duplicate_exists:
+ frappe.throw(
+ _("A renewal lease already exists for this lease: {0}").format(duplicate_exists),
+ frappe.ValidationError,
+ )
+
+ # 5. Clone Lease using standard frappe.copy_doc
+ new_lease = frappe.copy_doc(source_doc)
+ new_lease.set("lease_invoice_schedule", []) # Clear old invoice schedules
+
+ # Set renewal reference fields
+ new_lease.renewed_from = source_lease_name
+ new_lease.lease_status = "Renewal to Previous Lease"
+ new_lease.renewal_initiated_by = frappe.session.user
+
+ # Calculate dates
+ if source_doc.end_date:
+ new_lease.start_date = add_days(source_doc.end_date, 1)
+ if source_doc.start_date:
+ duration_days = (getdate(source_doc.end_date) - getdate(source_doc.start_date)).days
+ new_lease.end_date = add_days(new_lease.start_date, duration_days)
+ else:
+ new_lease.start_date = today()
+
+ # Update valid_from date for items
+ for item in new_lease.lease_item:
+ item.valid_from = new_lease.start_date
+
+ # Insert and save the new Lease as draft
+ new_lease.insert(ignore_permissions=True)
+
+ # Post a message/comment to the old lease with initiator and link details
+ comment_text = _(
+ "Lease renewal draft {0} has been initiated by {1} on {2}."
+ ).format(new_lease.name, frappe.session.user, frappe.utils.formatdate(today()))
+ source_doc.add_comment(text=comment_text)
+
+ return new_lease.name
diff --git a/propms/property_management_solution/doctype/lease/test_lease.py b/propms/property_management_solution/doctype/lease/test_lease.py
index d1a28862..229e5e29 100755
--- a/propms/property_management_solution/doctype/lease/test_lease.py
+++ b/propms/property_management_solution/doctype/lease/test_lease.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestLease(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/lease_invoice_schedule/lease_invoice_schedule.py b/propms/property_management_solution/doctype/lease_invoice_schedule/lease_invoice_schedule.py
index f44a6b2c..cf21dd93 100755
--- a/propms/property_management_solution/doctype/lease_invoice_schedule/lease_invoice_schedule.py
+++ b/propms/property_management_solution/doctype/lease_invoice_schedule/lease_invoice_schedule.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class LeaseInvoiceSchedule(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/lease_invoice_schedule/test_lease_invoice_schedule.py b/propms/property_management_solution/doctype/lease_invoice_schedule/test_lease_invoice_schedule.py
index d8770772..7f5c0ea2 100755
--- a/propms/property_management_solution/doctype/lease_invoice_schedule/test_lease_invoice_schedule.py
+++ b/propms/property_management_solution/doctype/lease_invoice_schedule/test_lease_invoice_schedule.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestLeaseInvoiceSchedule(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/lease_item/lease_item.py b/propms/property_management_solution/doctype/lease_item/lease_item.py
index dc69b9a3..7b1e543e 100755
--- a/propms/property_management_solution/doctype/lease_item/lease_item.py
+++ b/propms/property_management_solution/doctype/lease_item/lease_item.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class LeaseItem(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/lease_item/test_lease_item.py b/propms/property_management_solution/doctype/lease_item/test_lease_item.py
index a5efc62d..70deac9f 100755
--- a/propms/property_management_solution/doctype/lease_item/test_lease_item.py
+++ b/propms/property_management_solution/doctype/lease_item/test_lease_item.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestLeaseItem(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/meter/meter.js b/propms/property_management_solution/doctype/meter/meter.js
index ff6cc36a..37ff9a6a 100644
--- a/propms/property_management_solution/doctype/meter/meter.js
+++ b/propms/property_management_solution/doctype/meter/meter.js
@@ -13,6 +13,3 @@ frappe.ui.form.on('Meter', {
}
});
-
-
-
diff --git a/propms/property_management_solution/doctype/meter/meter.py b/propms/property_management_solution/doctype/meter/meter.py
index 68270d46..83a34132 100644
--- a/propms/property_management_solution/doctype/meter/meter.py
+++ b/propms/property_management_solution/doctype/meter/meter.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class Meter(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/meter/test_meter.py b/propms/property_management_solution/doctype/meter/test_meter.py
index 60710e74..758e30cf 100644
--- a/propms/property_management_solution/doctype/meter/test_meter.py
+++ b/propms/property_management_solution/doctype/meter/test_meter.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestMeter(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/meter_reading/meter_reading.py b/propms/property_management_solution/doctype/meter_reading/meter_reading.py
index da298956..e89272a0 100644
--- a/propms/property_management_solution/doctype/meter_reading/meter_reading.py
+++ b/propms/property_management_solution/doctype/meter_reading/meter_reading.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class MeterReading(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/meter_reading/test_meter_reading.py b/propms/property_management_solution/doctype/meter_reading/test_meter_reading.py
index 2b348ae5..c287788e 100644
--- a/propms/property_management_solution/doctype/meter_reading/test_meter_reading.py
+++ b/propms/property_management_solution/doctype/meter_reading/test_meter_reading.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestMeterReading(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/meter_reading_detail/meter_reading_detail.py b/propms/property_management_solution/doctype/meter_reading_detail/meter_reading_detail.py
index 0f910ff9..40a853aa 100644
--- a/propms/property_management_solution/doctype/meter_reading_detail/meter_reading_detail.py
+++ b/propms/property_management_solution/doctype/meter_reading_detail/meter_reading_detail.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class MeterReadingDetail(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/meter_reading_detail/test_meter_reading_detail.py b/propms/property_management_solution/doctype/meter_reading_detail/test_meter_reading_detail.py
index d414da4f..0b0577bc 100644
--- a/propms/property_management_solution/doctype/meter_reading_detail/test_meter_reading_detail.py
+++ b/propms/property_management_solution/doctype/meter_reading_detail/test_meter_reading_detail.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestMeterReadingDetail(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/multiselect_item_group/multiselect_item_group.py b/propms/property_management_solution/doctype/multiselect_item_group/multiselect_item_group.py
index 5f4e55ee..35bfca61 100644
--- a/propms/property_management_solution/doctype/multiselect_item_group/multiselect_item_group.py
+++ b/propms/property_management_solution/doctype/multiselect_item_group/multiselect_item_group.py
@@ -1,12 +1,10 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2020, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class MultiSelectItemGroup(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsource_contact/outsource_contact.py b/propms/property_management_solution/doctype/outsource_contact/outsource_contact.py
index a49be7a3..3f764305 100755
--- a/propms/property_management_solution/doctype/outsource_contact/outsource_contact.py
+++ b/propms/property_management_solution/doctype/outsource_contact/outsource_contact.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class OutsourceContact(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsource_contact/test_outsource_contact.py b/propms/property_management_solution/doctype/outsource_contact/test_outsource_contact.py
index af8252eb..08183dfe 100755
--- a/propms/property_management_solution/doctype/outsource_contact/test_outsource_contact.py
+++ b/propms/property_management_solution/doctype/outsource_contact/test_outsource_contact.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestOutsourceContact(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsourcing_attendance/outsourcing_attendance.py b/propms/property_management_solution/doctype/outsourcing_attendance/outsourcing_attendance.py
index 5de918ad..92f145c1 100755
--- a/propms/property_management_solution/doctype/outsourcing_attendance/outsourcing_attendance.py
+++ b/propms/property_management_solution/doctype/outsourcing_attendance/outsourcing_attendance.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class OutsourcingAttendance(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsourcing_attendance/test_outsourcing_attendance.py b/propms/property_management_solution/doctype/outsourcing_attendance/test_outsourcing_attendance.py
index 909a56a8..b0eb102a 100755
--- a/propms/property_management_solution/doctype/outsourcing_attendance/test_outsourcing_attendance.py
+++ b/propms/property_management_solution/doctype/outsourcing_attendance/test_outsourcing_attendance.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestOutsourcingAttendance(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsourcing_attendance_details/outsourcing_attendance_details.py b/propms/property_management_solution/doctype/outsourcing_attendance_details/outsourcing_attendance_details.py
index 06efbb08..bbf168d1 100755
--- a/propms/property_management_solution/doctype/outsourcing_attendance_details/outsourcing_attendance_details.py
+++ b/propms/property_management_solution/doctype/outsourcing_attendance_details/outsourcing_attendance_details.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class OutsourcingAttendanceDetails(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsourcing_category/outsourcing_category.py b/propms/property_management_solution/doctype/outsourcing_category/outsourcing_category.py
index 2316b31b..511ed538 100755
--- a/propms/property_management_solution/doctype/outsourcing_category/outsourcing_category.py
+++ b/propms/property_management_solution/doctype/outsourcing_category/outsourcing_category.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class OutsourcingCategory(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsourcing_category/test_outsourcing_category.py b/propms/property_management_solution/doctype/outsourcing_category/test_outsourcing_category.py
index 69dbdae1..dddba74a 100755
--- a/propms/property_management_solution/doctype/outsourcing_category/test_outsourcing_category.py
+++ b/propms/property_management_solution/doctype/outsourcing_category/test_outsourcing_category.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestOutsourcingCategory(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsourcing_shift/outsourcing_shift.py b/propms/property_management_solution/doctype/outsourcing_shift/outsourcing_shift.py
index 671acc00..3f750c0f 100755
--- a/propms/property_management_solution/doctype/outsourcing_shift/outsourcing_shift.py
+++ b/propms/property_management_solution/doctype/outsourcing_shift/outsourcing_shift.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class OutsourcingShift(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsourcing_shift/test_outsourcing_shift.py b/propms/property_management_solution/doctype/outsourcing_shift/test_outsourcing_shift.py
index 1ab8d31a..fb198142 100755
--- a/propms/property_management_solution/doctype/outsourcing_shift/test_outsourcing_shift.py
+++ b/propms/property_management_solution/doctype/outsourcing_shift/test_outsourcing_shift.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestOutsourcingShift(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/outsourcing_shift_location/outsourcing_shift_location.py b/propms/property_management_solution/doctype/outsourcing_shift_location/outsourcing_shift_location.py
index 8a27d188..01ebc0b6 100755
--- a/propms/property_management_solution/doctype/outsourcing_shift_location/outsourcing_shift_location.py
+++ b/propms/property_management_solution/doctype/outsourcing_shift_location/outsourcing_shift_location.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class OutsourcingShiftLocation(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/paint/paint.py b/propms/property_management_solution/doctype/paint/paint.py
index a7252f57..6992e62c 100755
--- a/propms/property_management_solution/doctype/paint/paint.py
+++ b/propms/property_management_solution/doctype/paint/paint.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class Paint(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/property/property.py b/propms/property_management_solution/doctype/property/property.py
index 779b199b..2abc6932 100755
--- a/propms/property_management_solution/doctype/property/property.py
+++ b/propms/property_management_solution/doctype/property/property.py
@@ -1,75 +1,74 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-from frappe.utils.nestedset import NestedSet
+
import frappe
from frappe import _
+from frappe.utils.nestedset import NestedSet
class Property(NestedSet):
- nsm_parent_field = "parent_property"
-
- def validate(self):
- self.validate_status_with_active_leases()
-
- def validate_status_with_active_leases(self):
- """Prevent property status updates when active leases exist"""
- if not self.name or self.is_new():
- return
-
- # Check if status is changing
- old_doc = self.get_doc_before_save()
- if old_doc and old_doc.status == self.status:
- return # Status not changing
-
- # If property has active leases, prevent status change
- active_leases = self.get_active_leases()
- if active_leases:
- lease_names = [lease.name for lease in active_leases]
- frappe.throw(
- _("Cannot change property status. Active leases exist: {0}").format(", ".join(lease_names))
- )
-
- def get_active_leases(self):
- """Get active leases for this property"""
- if not self.name:
- return []
-
- from frappe.query_builder import DocType
- Lease = DocType('Lease')
-
- active_leases = (
- frappe.qb.from_(Lease)
- .select(Lease.name, Lease.start_date, Lease.end_date, Lease.skip_end_date)
- .where(Lease.property == self.name)
- .where(Lease.start_date <= frappe.utils.now())
- .where(
- (Lease.end_date >= frappe.utils.now()) |
- (Lease.skip_end_date == 1)
- )
- ).run(as_dict=True)
-
- return active_leases
-
- def on_trash(self, allow_root_deletion=True):
- super().on_trash(allow_root_deletion)
+ nsm_parent_field = "parent_property"
+
+ def validate(self):
+ self.validate_status_with_active_leases()
+
+ def validate_status_with_active_leases(self):
+ """Prevent property status updates when active leases exist"""
+ if not self.name or self.is_new():
+ return
+
+ # Check if status is changing
+ old_doc = self.get_doc_before_save()
+ if old_doc and old_doc.status == self.status:
+ return # Status not changing
+
+ # If property has active leases, prevent status change
+ active_leases = self.get_active_leases()
+ if active_leases:
+ lease_names = [lease.name for lease in active_leases]
+ frappe.throw(
+ _("Cannot change property status. Active leases exist: {0}").format(", ".join(lease_names))
+ )
+
+ def get_active_leases(self):
+ """Get active leases for this property"""
+ if not self.name:
+ return []
+
+ from frappe.query_builder import DocType
+
+ Lease = DocType("Lease")
+
+ active_leases = (
+ frappe.qb.from_(Lease)
+ .select(Lease.name, Lease.start_date, Lease.end_date, Lease.skip_end_date)
+ .where(Lease.property == self.name)
+ .where(Lease.start_date <= frappe.utils.now())
+ .where((Lease.end_date >= frappe.utils.now()) | (Lease.skip_end_date == 1))
+ ).run(as_dict=True)
+
+ return active_leases
+
+ def on_trash(self, allow_root_deletion=True):
+ super().on_trash(allow_root_deletion)
@frappe.whitelist()
def add_node():
- from frappe.desk.treeview import make_tree_args
+ from frappe.desk.treeview import make_tree_args
+
+ args = frappe.form_dict
+ args = make_tree_args(**frappe.form_dict)
- args = frappe.form_dict
- args = make_tree_args(**frappe.form_dict)
+ if args["is_root"]:
+ args["parent_property"] = None
- if args["is_root"]:
- args["parent_property"] = None
+ doc = frappe.get_doc(args)
+
+ doc.save()
- doc = frappe.get_doc(args)
- doc.save()
@frappe.whitelist()
def get_children(doctype, parent=None, company=None, is_root=False):
if is_root:
@@ -82,4 +81,3 @@ def get_children(doctype, parent=None, company=None, is_root=False):
]
return frappe.get_list(doctype, fields=fields, filters=filters, order_by="name")
-
diff --git a/propms/property_management_solution/doctype/property/property_tree.js b/propms/property_management_solution/doctype/property/property_tree.js
index 9039c7d6..4c9d00a7 100644
--- a/propms/property_management_solution/doctype/property/property_tree.js
+++ b/propms/property_management_solution/doctype/property/property_tree.js
@@ -5,7 +5,7 @@ frappe.treeview_settings["Property"] = {
root_label: "Property",
filters: [
{
- fieldname: "company",
+ fieldname: "company",
fieldtype:"Link",
options: "Company",
label: __("Company"),
diff --git a/propms/property_management_solution/doctype/property/test_property.py b/propms/property_management_solution/doctype/property/test_property.py
index 7c400f44..edaa54ce 100755
--- a/propms/property_management_solution/doctype/property/test_property.py
+++ b/propms/property_management_solution/doctype/property/test_property.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestProperty(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/property_amenity/property_amenity.py b/propms/property_management_solution/doctype/property_amenity/property_amenity.py
index 29a4da9d..ddc6ac66 100755
--- a/propms/property_management_solution/doctype/property_amenity/property_amenity.py
+++ b/propms/property_management_solution/doctype/property_amenity/property_amenity.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class PropertyAmenity(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/property_amenity/test_property_amenity.py b/propms/property_management_solution/doctype/property_amenity/test_property_amenity.py
index 388d426e..8dd6ab5e 100755
--- a/propms/property_management_solution/doctype/property_amenity/test_property_amenity.py
+++ b/propms/property_management_solution/doctype/property_amenity/test_property_amenity.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestPropertyAmenity(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/property_management_settings/property_management_settings.js b/propms/property_management_solution/doctype/property_management_settings/property_management_settings.js
index adc55369..aa0ba363 100755
--- a/propms/property_management_solution/doctype/property_management_settings/property_management_settings.js
+++ b/propms/property_management_solution/doctype/property_management_settings/property_management_settings.js
@@ -2,5 +2,5 @@
// For license information, please see license.txt
frappe.ui.form.on('Property Management Settings', {
-
+
});
diff --git a/propms/property_management_solution/doctype/property_management_settings/property_management_settings.py b/propms/property_management_solution/doctype/property_management_settings/property_management_settings.py
index 3573d6aa..c43417b6 100755
--- a/propms/property_management_solution/doctype/property_management_settings/property_management_settings.py
+++ b/propms/property_management_solution/doctype/property_management_settings/property_management_settings.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class PropertyManagementSettings(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/property_management_settings/test_property_management_settings.py b/propms/property_management_solution/doctype/property_management_settings/test_property_management_settings.py
index 52771dd8..73a87052 100755
--- a/propms/property_management_solution/doctype/property_management_settings/test_property_management_settings.py
+++ b/propms/property_management_solution/doctype/property_management_settings/test_property_management_settings.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestPropertyManagementSettings(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/property_meter_reading/property_meter_reading.py b/propms/property_management_solution/doctype/property_meter_reading/property_meter_reading.py
index 279401ed..5aa5c9f0 100644
--- a/propms/property_management_solution/doctype/property_meter_reading/property_meter_reading.py
+++ b/propms/property_management_solution/doctype/property_meter_reading/property_meter_reading.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class PropertyMeterReading(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/property_meter_reading/test_property_meter_reading.py b/propms/property_management_solution/doctype/property_meter_reading/test_property_meter_reading.py
index 200d12a5..40449cad 100644
--- a/propms/property_management_solution/doctype/property_meter_reading/test_property_meter_reading.py
+++ b/propms/property_management_solution/doctype/property_meter_reading/test_property_meter_reading.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2019, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestPropertyMeterReading(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/property_unit/property_unit.py b/propms/property_management_solution/doctype/property_unit/property_unit.py
index 03ed1abc..a84fef11 100755
--- a/propms/property_management_solution/doctype/property_unit/property_unit.py
+++ b/propms/property_management_solution/doctype/property_unit/property_unit.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class PropertyUnit(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/security_attendance/security_attendance.js b/propms/property_management_solution/doctype/security_attendance/security_attendance.js
index 8b3d6fcc..b22842b8 100755
--- a/propms/property_management_solution/doctype/security_attendance/security_attendance.js
+++ b/propms/property_management_solution/doctype/security_attendance/security_attendance.js
@@ -31,4 +31,4 @@ cur_frm.fields_dict['attendance_details'].grid.get_field('guard_empid').get_quer
['Employee', 'department', 'like', 'Security -%']
]
}
-};
\ No newline at end of file
+};
diff --git a/propms/property_management_solution/doctype/security_attendance/security_attendance.py b/propms/property_management_solution/doctype/security_attendance/security_attendance.py
index 92fd1026..6c003229 100755
--- a/propms/property_management_solution/doctype/security_attendance/security_attendance.py
+++ b/propms/property_management_solution/doctype/security_attendance/security_attendance.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class SecurityAttendance(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/security_attendance/test_security_attendance.py b/propms/property_management_solution/doctype/security_attendance/test_security_attendance.py
index 51f5833b..b97a6116 100755
--- a/propms/property_management_solution/doctype/security_attendance/test_security_attendance.py
+++ b/propms/property_management_solution/doctype/security_attendance/test_security_attendance.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestSecurityAttendance(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/security_attendance_details/security_attendance_details.js b/propms/property_management_solution/doctype/security_attendance_details/security_attendance_details.js
index 1402e1b7..80cd5e4f 100755
--- a/propms/property_management_solution/doctype/security_attendance_details/security_attendance_details.js
+++ b/propms/property_management_solution/doctype/security_attendance_details/security_attendance_details.js
@@ -7,4 +7,4 @@ cur_frm.set_query("guard_empid", function() {
"department": ["like", "Security - "]
}
}
-});
\ No newline at end of file
+});
diff --git a/propms/property_management_solution/doctype/security_attendance_details/security_attendance_details.py b/propms/property_management_solution/doctype/security_attendance_details/security_attendance_details.py
index 4a41de52..5a478f6d 100755
--- a/propms/property_management_solution/doctype/security_attendance_details/security_attendance_details.py
+++ b/propms/property_management_solution/doctype/security_attendance_details/security_attendance_details.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class SecurityAttendanceDetails(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/security_attendance_details/test_security_attendance_details.py b/propms/property_management_solution/doctype/security_attendance_details/test_security_attendance_details.py
index f047a1f8..355e0f07 100755
--- a/propms/property_management_solution/doctype/security_attendance_details/test_security_attendance_details.py
+++ b/propms/property_management_solution/doctype/security_attendance_details/test_security_attendance_details.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestSecurityAttendanceDetails(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/security_deposit_details/security_deposit_details.py b/propms/property_management_solution/doctype/security_deposit_details/security_deposit_details.py
index ba9f161b..eeb12905 100755
--- a/propms/property_management_solution/doctype/security_deposit_details/security_deposit_details.py
+++ b/propms/property_management_solution/doctype/security_deposit_details/security_deposit_details.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class SecurityDepositDetails(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/security_deposit_details/test_security_deposit_details.py b/propms/property_management_solution/doctype/security_deposit_details/test_security_deposit_details.py
index 5df5dee8..a08795ce 100755
--- a/propms/property_management_solution/doctype/security_deposit_details/test_security_deposit_details.py
+++ b/propms/property_management_solution/doctype/security_deposit_details/test_security_deposit_details.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestSecurityDepositDetails(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/tool_item/tool_item.py b/propms/property_management_solution/doctype/tool_item/tool_item.py
index 1953a603..562630a1 100644
--- a/propms/property_management_solution/doctype/tool_item/tool_item.py
+++ b/propms/property_management_solution/doctype/tool_item/tool_item.py
@@ -6,4 +6,4 @@
class ToolItem(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/tool_item_record/test_tool_item_record.py b/propms/property_management_solution/doctype/tool_item_record/test_tool_item_record.py
index b679d62f..6537e331 100644
--- a/propms/property_management_solution/doctype/tool_item_record/test_tool_item_record.py
+++ b/propms/property_management_solution/doctype/tool_item_record/test_tool_item_record.py
@@ -6,4 +6,4 @@
class TestToolItemRecord(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/tool_item_record/tool_item_record.js b/propms/property_management_solution/doctype/tool_item_record/tool_item_record.js
index e1160168..cb1320c3 100644
--- a/propms/property_management_solution/doctype/tool_item_record/tool_item_record.js
+++ b/propms/property_management_solution/doctype/tool_item_record/tool_item_record.js
@@ -25,4 +25,3 @@ cur_frm.set_query("tool_item_set", function () {
}
}
});
-
diff --git a/propms/property_management_solution/doctype/tool_item_record/tool_item_record.py b/propms/property_management_solution/doctype/tool_item_record/tool_item_record.py
index e8a2b490..24009ca1 100644
--- a/propms/property_management_solution/doctype/tool_item_record/tool_item_record.py
+++ b/propms/property_management_solution/doctype/tool_item_record/tool_item_record.py
@@ -6,4 +6,4 @@
class ToolItemRecord(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/tool_item_set/test_tool_item_set.py b/propms/property_management_solution/doctype/tool_item_set/test_tool_item_set.py
index fb33d388..a6c72c80 100644
--- a/propms/property_management_solution/doctype/tool_item_set/test_tool_item_set.py
+++ b/propms/property_management_solution/doctype/tool_item_set/test_tool_item_set.py
@@ -6,4 +6,4 @@
class TestToolItemSet(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/tool_item_set/tool_item_set.py b/propms/property_management_solution/doctype/tool_item_set/tool_item_set.py
index df1ba4c7..6e4bf5dd 100644
--- a/propms/property_management_solution/doctype/tool_item_set/tool_item_set.py
+++ b/propms/property_management_solution/doctype/tool_item_set/tool_item_set.py
@@ -6,4 +6,4 @@
class ToolItemSet(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/unit_assets/unit_assets.py b/propms/property_management_solution/doctype/unit_assets/unit_assets.py
index d3e3709d..ee1851f6 100755
--- a/propms/property_management_solution/doctype/unit_assets/unit_assets.py
+++ b/propms/property_management_solution/doctype/unit_assets/unit_assets.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class UnitAssets(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/unit_type/test_unit_type.py b/propms/property_management_solution/doctype/unit_type/test_unit_type.py
index b255391c..5471a2e1 100755
--- a/propms/property_management_solution/doctype/unit_type/test_unit_type.py
+++ b/propms/property_management_solution/doctype/unit_type/test_unit_type.py
@@ -1,9 +1,8 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and Contributors
# See license.txt
-from __future__ import unicode_literals
+
import unittest
class TestUnitType(unittest.TestCase):
- pass
+ pass
diff --git a/propms/property_management_solution/doctype/unit_type/unit_type.py b/propms/property_management_solution/doctype/unit_type/unit_type.py
index 3d20b5a0..e343de98 100755
--- a/propms/property_management_solution/doctype/unit_type/unit_type.py
+++ b/propms/property_management_solution/doctype/unit_type/unit_type.py
@@ -1,10 +1,9 @@
-# -*- coding: utf-8 -*-
# Copyright (c) 2018, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
+
from frappe.model.document import Document
class UnitType(Document):
- pass
+ pass
diff --git a/propms/property_management_solution/journal_entry_account.js b/propms/property_management_solution/journal_entry_account.js
index 15801403..56d8b6e8 100644
--- a/propms/property_management_solution/journal_entry_account.js
+++ b/propms/property_management_solution/journal_entry_account.js
@@ -37,4 +37,4 @@ frappe.ui.form.on('Journal Entry Account', {
frappe.model.set_value(cdt, cdn, "party", "");
}
}
-})
\ No newline at end of file
+})
diff --git a/propms/property_management_solution/notification/daily_checkup_report/daily_checkup_report.md b/propms/property_management_solution/notification/daily_checkup_report/daily_checkup_report.md
index b54ea659..09953ee4 100644
--- a/propms/property_management_solution/notification/daily_checkup_report/daily_checkup_report.md
+++ b/propms/property_management_solution/notification/daily_checkup_report/daily_checkup_report.md
@@ -1 +1 @@
-This email has been sent with data from daily report submission
\ No newline at end of file
+This email has been sent with data from daily report submission
diff --git a/propms/property_management_solution/notification/daily_checkup_report/daily_checkup_report.py b/propms/property_management_solution/notification/daily_checkup_report/daily_checkup_report.py
index 2f1b3d1b..02e3e933 100644
--- a/propms/property_management_solution/notification/daily_checkup_report/daily_checkup_report.py
+++ b/propms/property_management_solution/notification/daily_checkup_report/daily_checkup_report.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-
-
def get_context(context):
- # do your magic here
- pass
+ # do your magic here
+ pass
diff --git a/propms/property_management_solution/notification/outsourcing_attendance/outsourcing_attendance.md b/propms/property_management_solution/notification/outsourcing_attendance/outsourcing_attendance.md
index 5f1c19e1..fe91b8d2 100644
--- a/propms/property_management_solution/notification/outsourcing_attendance/outsourcing_attendance.md
+++ b/propms/property_management_solution/notification/outsourcing_attendance/outsourcing_attendance.md
@@ -1 +1 @@
-This email has been sent with data from security attendance submission
\ No newline at end of file
+This email has been sent with data from security attendance submission
diff --git a/propms/property_management_solution/notification/outsourcing_attendance/outsourcing_attendance.py b/propms/property_management_solution/notification/outsourcing_attendance/outsourcing_attendance.py
index 2f1b3d1b..02e3e933 100644
--- a/propms/property_management_solution/notification/outsourcing_attendance/outsourcing_attendance.py
+++ b/propms/property_management_solution/notification/outsourcing_attendance/outsourcing_attendance.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-
-
def get_context(context):
- # do your magic here
- pass
+ # do your magic here
+ pass
diff --git a/propms/property_management_solution/notification/security_attendance/security_attendance.md b/propms/property_management_solution/notification/security_attendance/security_attendance.md
index 5f1c19e1..fe91b8d2 100644
--- a/propms/property_management_solution/notification/security_attendance/security_attendance.md
+++ b/propms/property_management_solution/notification/security_attendance/security_attendance.md
@@ -1 +1 @@
-This email has been sent with data from security attendance submission
\ No newline at end of file
+This email has been sent with data from security attendance submission
diff --git a/propms/property_management_solution/notification/security_attendance/security_attendance.py b/propms/property_management_solution/notification/security_attendance/security_attendance.py
index 2f1b3d1b..02e3e933 100644
--- a/propms/property_management_solution/notification/security_attendance/security_attendance.py
+++ b/propms/property_management_solution/notification/security_attendance/security_attendance.py
@@ -1,6 +1,3 @@
-from __future__ import unicode_literals
-
-
def get_context(context):
- # do your magic here
- pass
+ # do your magic here
+ pass
diff --git a/propms/property_management_solution/report/debtors_report/debtors_report.js b/propms/property_management_solution/report/debtors_report/debtors_report.js
index b015a25e..7cbc9326 100644
--- a/propms/property_management_solution/report/debtors_report/debtors_report.js
+++ b/propms/property_management_solution/report/debtors_report/debtors_report.js
@@ -34,4 +34,4 @@ frappe.query_reports["Debtors Report"] = {
return value;
},
-};
\ No newline at end of file
+};
diff --git a/propms/property_management_solution/report/invoice_details/invoice_details.py b/propms/property_management_solution/report/invoice_details/invoice_details.py
index f96aea47..7bbeeada 100644
--- a/propms/property_management_solution/report/invoice_details/invoice_details.py
+++ b/propms/property_management_solution/report/invoice_details/invoice_details.py
@@ -1,17 +1,15 @@
# Copyright (c) 2013, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
# import frappe
-from .other_methods import get_residential_columns
-from .other_methods import get_sales_invoice
+from .other_methods import get_residential_columns, get_sales_invoice
def execute(filters=None):
- columns, data = [], []
- if filters.get("rental") and filters.get("year"):
- columns = get_residential_columns(filters.get("year"))
- get_sales_invoice(filters, data)
+ columns, data = [], []
+ if filters.get("rental") and filters.get("year"):
+ columns = get_residential_columns(filters.get("year"))
+ get_sales_invoice(filters, data)
- return columns, data
+ return columns, data
diff --git a/propms/property_management_solution/report/invoice_details/other_methods.py b/propms/property_management_solution/report/invoice_details/other_methods.py
index eef08723..b5ab9611 100644
--- a/propms/property_management_solution/report/invoice_details/other_methods.py
+++ b/propms/property_management_solution/report/invoice_details/other_methods.py
@@ -1,216 +1,193 @@
-import frappe, calendar
-from frappe import _
+import calendar
from datetime import date, timedelta
+import frappe
+from frappe import _
+
def get_residential_columns(year):
- columns = [
- {
- "fieldname": "apartment_no",
- "label": _("Apartment No."),
- "fieldtype": "Data",
- "width": 150,
- },
- {
- "fieldname": "client",
- "label": _("Client"),
- "fieldtype": "Data",
- "width": 150,
- },
- {
- "fieldname": "advance_prev_year",
- "label": _("Advance RECD in 2019"),
- "fieldtype": "Data",
- "width": 150,
- },
- {
- "fieldname": "invoice_no",
- "label": _("Invoice No."),
- "fieldtype": "Link",
- "options": "Sales Invoice",
- "width": 150,
- },
- {"fieldname": "from", "label": _("From"), "fieldtype": "Data", "width": 150},
- {"fieldname": "to", "label": _("To"), "fieldtype": "Data", "width": 150},
- {
- "fieldname": "invoice_amount",
- "label": _("Invoice Amount"),
- "fieldtype": "Data",
- "width": 150,
- },
- ]
- months = months_array()
- for i in months:
- columns.append(
- {
- "fieldname": i.lower(),
- "label": i + " " + str(year),
- "fieldtype": "Data",
- "width": 150,
- }
- )
-
- return columns
+ columns = [
+ {
+ "fieldname": "apartment_no",
+ "label": _("Apartment No."),
+ "fieldtype": "Data",
+ "width": 150,
+ },
+ {
+ "fieldname": "client",
+ "label": _("Client"),
+ "fieldtype": "Data",
+ "width": 150,
+ },
+ {
+ "fieldname": "advance_prev_year",
+ "label": _("Advance RECD in 2019"),
+ "fieldtype": "Data",
+ "width": 150,
+ },
+ {
+ "fieldname": "invoice_no",
+ "label": _("Invoice No."),
+ "fieldtype": "Link",
+ "options": "Sales Invoice",
+ "width": 150,
+ },
+ {"fieldname": "from", "label": _("From"), "fieldtype": "Data", "width": 150},
+ {"fieldname": "to", "label": _("To"), "fieldtype": "Data", "width": 150},
+ {
+ "fieldname": "invoice_amount",
+ "label": _("Invoice Amount"),
+ "fieldtype": "Data",
+ "width": 150,
+ },
+ ]
+ months = months_array()
+ for i in months:
+ columns.append(
+ {
+ "fieldname": i.lower(),
+ "label": i + " " + str(year),
+ "fieldtype": "Data",
+ "width": 150,
+ }
+ )
+
+ return columns
def get_sales_invoice(filters, data, from_other=None, months=None):
- total = {}
- lease_item = "'" + filters.get("rental") + "' "
- print(lease_item)
- if filters.get("maintenance"):
- lease_item = "'Service Charge - " + filters.get("rental").split()[0] + "'"
+ total = {}
+ lease_item = "'" + filters.get("rental") + "' "
+ print(lease_item)
+ if filters.get("maintenance"):
+ lease_item = "'Service Charge - " + filters.get("rental").split()[0] + "'"
- query = """ SELECT * FROM `tabSales Invoice` AS SI WHERE EXISTS (SELECT * FROM `tabSales Invoice Item` AS SIT WHERE SIT.item_code = {0} and SIT.parent = SI.name )
+ query = f""" SELECT * FROM `tabSales Invoice` AS SI WHERE EXISTS (SELECT * FROM `tabSales Invoice Item` AS SIT WHERE SIT.item_code = {lease_item} and SIT.parent = SI.name )
and SI.docstatus=%s
- ORDER by SI.customer,SI.from_date ASC""".format(
- lease_item
- ) % (
- 1
- )
-
- sales_invoices = frappe.db.sql(query, as_dict=True)
- previuos_customer = ""
- for i in sales_invoices:
- lease = frappe.get_value("Lease", i.lease, "property")
- obj = {
- "apartment_no": lease or "",
- "client": i.customer,
- "advance_prev_year": "",
- "invoice_no": i.name,
- "from": i.from_date if i.from_date else i.posting_date,
- "to": i.to_date - timedelta(days=1) if i.to_date else i.posting_date,
- "invoice_amount": i.total,
- }
- set_monthly_amount(
- i.from_date,
- i.to_date - timedelta(days=1) if i.to_date else "",
- obj,
- filters,
- total,
- months,
- )
- if previuos_customer != i.customer:
- data.append({})
- previuos_customer = i.customer
- data.append(obj)
- if from_other:
- data.append(total)
+ ORDER by SI.customer,SI.from_date ASC""" % (1)
+
+ sales_invoices = frappe.db.sql(query, as_dict=True)
+ previuos_customer = ""
+ for i in sales_invoices:
+ lease = frappe.get_value("Lease", i.lease, "property")
+ obj = {
+ "apartment_no": lease or "",
+ "client": i.customer,
+ "advance_prev_year": "",
+ "invoice_no": i.name,
+ "from": i.from_date if i.from_date else i.posting_date,
+ "to": i.to_date - timedelta(days=1) if i.to_date else i.posting_date,
+ "invoice_amount": i.total,
+ }
+ set_monthly_amount(
+ i.from_date,
+ i.to_date - timedelta(days=1) if i.to_date else "",
+ obj,
+ filters,
+ total,
+ months,
+ )
+ if previuos_customer != i.customer:
+ data.append({})
+ previuos_customer = i.customer
+ data.append(obj)
+ if from_other:
+ data.append(total)
def set_monthly_amount(start_date, end_date, obj, filters, total, months):
- rate = get_rate(obj["invoice_no"], filters)
- if end_date and rate:
- check_dates(start_date, end_date, rate, obj, total, months)
+ rate = get_rate(obj["invoice_no"], filters)
+ if end_date and rate:
+ check_dates(start_date, end_date, rate, obj, total, months)
def check_dates(start_date, end_date, rate, obj, total, months):
- start = start_date
- no_minus = 0
-
- while start < end_date:
- month_string = start.strftime("%b")
- month_no_of_days = calendar.monthrange(start.year, start.month)[1]
- last_date = date(start.year, start.month, month_no_of_days)
- if (last_date - start).days >= 29 or (
- month_string == "Feb" and (last_date - start).days >= 27
- ):
- if start.year == start_date.year:
- obj[month_string.lower()] = round(rate, 2)
- if months and month_string.lower() in months:
- total[month_string.lower()] = (
- round(rate, 2) + round(total[month_string.lower()], 2)
- if month_string.lower() in total
- else round(rate, 2)
- )
- else:
- if start.year == start_date.year:
- obj[month_string.lower()] = round(
- round(rate / month_no_of_days, 2)
- * (month_no_of_days - int(start.day)),
- 2,
- )
- if months and month_string.lower() in months:
- total[month_string.lower()] = (
- round(
- round(rate / month_no_of_days, 2)
- * (month_no_of_days - int(start.day)),
- 2,
- )
- + round(total[month_string.lower()], 2)
- if month_string.lower() in total
- else round(
- round(rate / month_no_of_days, 2)
- * (month_no_of_days - int(start.day)),
- 2,
- )
- )
- no_minus = month_no_of_days
- start += timedelta(days=month_no_of_days)
-
- start_last = start - timedelta(days=no_minus)
-
- if (end_date - start_last).days > 0 and start_last.month != end_date.month:
- if start_last.year == start_date.year and start_last.year == end_date.year:
- month_string = end_date.strftime("%b")
- month_no_of_days = calendar.monthrange(end_date.year, end_date.month)[1]
- if int(end_date.day) >= 29 or (
- month_string == "Feb" and (end_date - start_last).days >= 27
- ):
- obj[month_string.lower()] = round(rate, 2)
- if months and month_string.lower() in months:
- total[month_string.lower()] = (
- round(rate, 2) + round(total[month_string.lower()], 2)
- if month_string.lower() in total
- else round(rate, 2)
- )
- else:
- obj[month_string.lower()] = round(
- round(rate / month_no_of_days, 2) * (int(end_date.day)), 2
- )
- if months and month_string.lower() in months:
- total[month_string.lower()] = (
- round(
- round(rate / month_no_of_days, 2) * (int(end_date.day)), 2
- )
- + round(total[month_string.lower()], 2)
- if month_string.lower() in total
- else round(
- round(rate / month_no_of_days, 2) * (int(end_date.day)), 2
- )
- )
+ start = start_date
+ no_minus = 0
+
+ while start < end_date:
+ month_string = start.strftime("%b")
+ month_no_of_days = calendar.monthrange(start.year, start.month)[1]
+ last_date = date(start.year, start.month, month_no_of_days)
+ if (last_date - start).days >= 29 or (month_string == "Feb" and (last_date - start).days >= 27):
+ if start.year == start_date.year:
+ obj[month_string.lower()] = round(rate, 2)
+ if months and month_string.lower() in months:
+ total[month_string.lower()] = (
+ round(rate, 2) + round(total[month_string.lower()], 2)
+ if month_string.lower() in total
+ else round(rate, 2)
+ )
+ else:
+ if start.year == start_date.year:
+ obj[month_string.lower()] = round(
+ round(rate / month_no_of_days, 2) * (month_no_of_days - int(start.day)),
+ 2,
+ )
+ if months and month_string.lower() in months:
+ total[month_string.lower()] = (
+ round(
+ round(rate / month_no_of_days, 2) * (month_no_of_days - int(start.day)),
+ 2,
+ )
+ + round(total[month_string.lower()], 2)
+ if month_string.lower() in total
+ else round(
+ round(rate / month_no_of_days, 2) * (month_no_of_days - int(start.day)),
+ 2,
+ )
+ )
+ no_minus = month_no_of_days
+ start += timedelta(days=month_no_of_days)
+
+ start_last = start - timedelta(days=no_minus)
+
+ if (end_date - start_last).days > 0 and start_last.month != end_date.month:
+ if start_last.year == start_date.year and start_last.year == end_date.year:
+ month_string = end_date.strftime("%b")
+ month_no_of_days = calendar.monthrange(end_date.year, end_date.month)[1]
+ if int(end_date.day) >= 29 or (month_string == "Feb" and (end_date - start_last).days >= 27):
+ obj[month_string.lower()] = round(rate, 2)
+ if months and month_string.lower() in months:
+ total[month_string.lower()] = (
+ round(rate, 2) + round(total[month_string.lower()], 2)
+ if month_string.lower() in total
+ else round(rate, 2)
+ )
+ else:
+ obj[month_string.lower()] = round(round(rate / month_no_of_days, 2) * (int(end_date.day)), 2)
+ if months and month_string.lower() in months:
+ total[month_string.lower()] = (
+ round(round(rate / month_no_of_days, 2) * (int(end_date.day)), 2)
+ + round(total[month_string.lower()], 2)
+ if month_string.lower() in total
+ else round(round(rate / month_no_of_days, 2) * (int(end_date.day)), 2)
+ )
def months_array():
- return [
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
- ]
+ return [
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+ ]
def get_rate(invoice_name, filters):
- filters_value = " and item_code= '" + filters.get("rental") + "' "
- if filters.get("maintenance"):
- filters_value = (
- "and item_code = 'Service Charge - "
- + filters.get("rental").split()[0]
- + "'"
- )
- query = """ SELECT rate FROM `tabSales Invoice Item` WHERE {0} {1}""".format(
- "parent = '" + invoice_name + "' ", filters_value
- )
-
- return (
- frappe.db.sql(query, as_dict=True)[0].rate
- if len(frappe.db.sql(query, as_dict=True)) > 0
- else ""
- )
+ filters_value = " and item_code= '" + filters.get("rental") + "' "
+ if filters.get("maintenance"):
+ filters_value = "and item_code = 'Service Charge - " + filters.get("rental").split()[0] + "'"
+ query = """ SELECT rate FROM `tabSales Invoice Item` WHERE {} {}""".format(
+ "parent = '" + invoice_name + "' ", filters_value
+ )
+
+ return frappe.db.sql(query, as_dict=True)[0].rate if len(frappe.db.sql(query, as_dict=True)) > 0 else ""
diff --git a/propms/property_management_solution/report/mis_income_break_up/mis_income_break_up.py b/propms/property_management_solution/report/mis_income_break_up/mis_income_break_up.py
index 13494643..a1fce66a 100644
--- a/propms/property_management_solution/report/mis_income_break_up/mis_income_break_up.py
+++ b/propms/property_management_solution/report/mis_income_break_up/mis_income_break_up.py
@@ -1,15 +1,13 @@
# Copyright (c) 2013, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-from .other_methods import get_columns
-from .other_methods import get_rentals
-from .other_methods import get_rental_maintenance
+
+from .other_methods import get_columns, get_rental_maintenance, get_rentals
def execute(filters=None):
- columns, data = get_columns(filters), get_rentals(filters)
+ columns, data = get_columns(filters), get_rentals(filters)
- get_rental_maintenance(filters, data)
+ get_rental_maintenance(filters, data)
- return columns, data
+ return columns, data
diff --git a/propms/property_management_solution/report/mis_income_break_up/other_methods.py b/propms/property_management_solution/report/mis_income_break_up/other_methods.py
index 73ec6d8f..f45ced49 100644
--- a/propms/property_management_solution/report/mis_income_break_up/other_methods.py
+++ b/propms/property_management_solution/report/mis_income_break_up/other_methods.py
@@ -1,6 +1,7 @@
-from time import strptime
import calendar
from collections import Counter
+from time import strptime
+
from ..invoice_details.other_methods import get_sales_invoice
from ..utility_invoices.other_methods import get_utility_sales_invoice
@@ -8,102 +9,93 @@
def get_rentals(filters):
- return_data = []
- if filters.get("year"):
-
- return_data.append({"income": "RENTAL INCOME"})
- sum_monthly = Counter({})
- tax = {"income": "LESS: W.TAX 10%"}
- net_rent = {"income": "NET RENT RECEIVED"}
- rentals = ["Commercial Rent", "Residential Rent"]
- for i in rentals:
- data = []
- _filters = {"rental": i, "year": filters.get("year")}
- get_sales_invoice(_filters, data, "Mis Income Break Up", months)
- if len(data) > 0 and len(data[len(data) - 1]) > 0:
- data[len(data) - 1]["total"] = sum(data[len(data) - 1].values()) / len(
- data[len(data) - 1]
- )
-
- sum_monthly += Counter(data[len(data) - 1])
-
- data[len(data) - 1]["income"] = i
- return_data.append(data[len(data) - 1])
-
- sum_monthly["income"] = "Total Rentals Received"
-
- return_data.append(sum_monthly)
- return_data.append(tax)
- return_data.append(net_rent)
- for i in sum_monthly:
- tax[i] = float(sum_monthly[i]) * float(0.10)
- net_rent[i] = sum_monthly[i] - tax[i]
- return return_data
+ return_data = []
+ if filters.get("year"):
+ return_data.append({"income": "RENTAL INCOME"})
+ sum_monthly = Counter({})
+ tax = {"income": "LESS: W.TAX 10%"}
+ net_rent = {"income": "NET RENT RECEIVED"}
+ rentals = ["Commercial Rent", "Residential Rent"]
+ for i in rentals:
+ data = []
+ _filters = {"rental": i, "year": filters.get("year")}
+ get_sales_invoice(_filters, data, "Mis Income Break Up", months)
+ if len(data) > 0 and len(data[len(data) - 1]) > 0:
+ data[len(data) - 1]["total"] = sum(data[len(data) - 1].values()) / len(data[len(data) - 1])
+
+ sum_monthly += Counter(data[len(data) - 1])
+
+ data[len(data) - 1]["income"] = i
+ return_data.append(data[len(data) - 1])
+
+ sum_monthly["income"] = "Total Rentals Received"
+
+ return_data.append(sum_monthly)
+ return_data.append(tax)
+ return_data.append(net_rent)
+ for i in sum_monthly:
+ tax[i] = float(sum_monthly[i]) * 0.10
+ net_rent[i] = sum_monthly[i] - tax[i]
+ return return_data
def get_rental_maintenance(filters, return_data):
+ if filters.get("year"):
+ return_data.append({"income": "MAINTENANCE INCOME"})
+ sum_monthly = Counter({})
- if filters.get("year"):
-
- return_data.append({"income": "MAINTENANCE INCOME"})
- sum_monthly = Counter({})
-
- rentals = ["Commercial Rent", "Residential Rent", "Utility Charges"]
- for ii in rentals:
- data = []
- _filters = {"rental": ii, "year": filters.get("year"), "maintenance": 1}
- if ii == "Utility Charges":
- get_utility_sales_invoice(data, "Mis Income Break Up", months)
- else:
- get_sales_invoice(_filters, data, "Mis Income Break Up", months)
- if len(data) > 0 and len(data[len(data) - 1]) > 0:
- data[len(data) - 1]["total"] = sum(data[len(data) - 1].values()) / len(
- data[len(data) - 1]
- )
+ rentals = ["Commercial Rent", "Residential Rent", "Utility Charges"]
+ for ii in rentals:
+ data = []
+ _filters = {"rental": ii, "year": filters.get("year"), "maintenance": 1}
+ if ii == "Utility Charges":
+ get_utility_sales_invoice(data, "Mis Income Break Up", months)
+ else:
+ get_sales_invoice(_filters, data, "Mis Income Break Up", months)
+ if len(data) > 0 and len(data[len(data) - 1]) > 0:
+ data[len(data) - 1]["total"] = sum(data[len(data) - 1].values()) / len(data[len(data) - 1])
- sum_monthly += Counter(data[len(data) - 1])
+ sum_monthly += Counter(data[len(data) - 1])
- data[len(data) - 1]["income"] = (
- ii + " Maintenance" if ii != "Utility Charges" else ii
- )
- return_data.append(data[len(data) - 1])
+ data[len(data) - 1]["income"] = ii + " Maintenance" if ii != "Utility Charges" else ii
+ return_data.append(data[len(data) - 1])
- sum_monthly["income"] = "Maintenance Total"
+ sum_monthly["income"] = "Maintenance Total"
- return_data.append(sum_monthly)
- return_data.append({})
- return return_data
+ return_data.append(sum_monthly)
+ return_data.append({})
+ return return_data
def get_columns(filters):
- columns = [
- {
- "label": "Income",
- "fieldname": "income",
- "fieldtype": "Data",
- "width": 200,
- }
- ]
- month_int_from = int(strptime(filters.get("from"), "%B").tm_mon)
- month_int_to = int(strptime(filters.get("to"), "%B").tm_mon)
-
- while month_int_from <= month_int_to:
- months.append(calendar.month_name[month_int_from].lower()[:3])
- columns.append(
- {
- "label": calendar.month_name[month_int_from],
- "fieldname": calendar.month_name[month_int_from].lower()[:3],
- "fieldtype": "Currency",
- "width": 180,
- }
- )
- month_int_from += 1
- columns.append(
- {
- "label": "Total",
- "fieldname": "total",
- "fieldtype": "Currency",
- "width": 180,
- }
- )
- return columns
+ columns = [
+ {
+ "label": "Income",
+ "fieldname": "income",
+ "fieldtype": "Data",
+ "width": 200,
+ }
+ ]
+ month_int_from = int(strptime(filters.get("from"), "%B").tm_mon)
+ month_int_to = int(strptime(filters.get("to"), "%B").tm_mon)
+
+ while month_int_from <= month_int_to:
+ months.append(calendar.month_name[month_int_from].lower()[:3])
+ columns.append(
+ {
+ "label": calendar.month_name[month_int_from],
+ "fieldname": calendar.month_name[month_int_from].lower()[:3],
+ "fieldtype": "Currency",
+ "width": 180,
+ }
+ )
+ month_int_from += 1
+ columns.append(
+ {
+ "label": "Total",
+ "fieldname": "total",
+ "fieldtype": "Currency",
+ "width": 180,
+ }
+ )
+ return columns
diff --git a/propms/property_management_solution/report/rent_invoices_details/rent_invoices_details.py b/propms/property_management_solution/report/rent_invoices_details/rent_invoices_details.py
index b8bf7439..182a5fc7 100644
--- a/propms/property_management_solution/report/rent_invoices_details/rent_invoices_details.py
+++ b/propms/property_management_solution/report/rent_invoices_details/rent_invoices_details.py
@@ -1,506 +1,468 @@
# Copyright (c) 2013, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
-from time import strptime
-import calendar
-from datetime import date, timedelta, datetime
+
from collections import OrderedDict
+from datetime import datetime, timedelta
+
+import frappe
+from erpnext import get_company_currency, get_default_company
from frappe.utils import (
- getdate,
- date_diff,
- month_diff,
- get_last_day,
- get_first_day,
- add_months,
- floor,
- add_days,
- flt,
- cint,
+ add_days,
+ add_months,
+ cint,
+ date_diff,
+ floor,
+ flt,
+ get_first_day,
+ get_last_day,
+ getdate,
)
-from erpnext import get_company_currency, get_default_company
def execute(filters=None):
- data = get_data(filters)
- columns = get_columns(filters)
- return columns, data
+ data = get_data(filters)
+ columns = get_columns(filters)
+ return columns, data
def get_data(filters):
- rows = []
- _from_date = "'{from_date}'".format(from_date=filters["from_date"])
- _to_date = "'{to_date}'".format(to_date=filters["to_date"])
- _company = "'{company}'".format(company=filters["company"])
- _items_grupe = filters.get("type_name")
- float_precision = cint(frappe.db.get_default("float_precision")) or 2
- if filters.get("company"):
- default_currency = get_company_currency(filters["company"])
- else:
- company = get_default_company()
- default_currency = get_company_currency(company)
-
- conditions = ""
- if not filters.get("extand"):
- conditions = "AND DATE(posting_date) BETWEEN {start} AND {end}".format(
- start=_from_date, end=_to_date
- )
-
- query = """
+ rows = []
+ _from_date = "'{from_date}'".format(from_date=filters["from_date"])
+ _to_date = "'{to_date}'".format(to_date=filters["to_date"])
+ _company = "'{company}'".format(company=filters["company"])
+ _items_grupe = filters.get("type_name")
+ float_precision = cint(frappe.db.get_default("float_precision")) or 2
+ if filters.get("company"):
+ default_currency = get_company_currency(filters["company"])
+ else:
+ company = get_default_company()
+ default_currency = get_company_currency(company)
+
+ conditions = ""
+ if not filters.get("extand"):
+ conditions = f"AND DATE(posting_date) BETWEEN {_from_date} AND {_to_date}"
+
+ query = f"""
SELECT
- name as invoice_id,
- customer,
- base_net_total as total,
+ name as invoice_id,
+ customer,
+ base_net_total as total,
net_total as foreign_total,
- currency,
- conversion_rate as exchange_rate,
- posting_date as date,
+ currency,
+ conversion_rate as exchange_rate,
+ posting_date as date,
lease
FROM
`tabSales Invoice`
WHERE
- docstatus = 1
- AND company = {company}
+ docstatus = 1
+ AND company = {_company}
AND lease != ""
AND from_date != ""
AND to_date != ""
AND is_return != 1
{conditions}
ORDER BY lease DESC, posting_date DESC
- """.format(
- conditions=conditions, company=_company
- )
-
- sales_invoices = frappe.db.sql(query, as_dict=True)
-
- for invoice in sales_invoices:
- _items_rwos = []
- append = False
- property_name = frappe.db.get_value("Lease", invoice["lease"], "property")
- invoice["property_name"] = property_name
- if invoice.total == invoice.foreign_total:
- invoice.foreign_total, invoice.exchange_rate = "", ""
- # if filters.get("foreign_currency") and get_company_currency(filters.company) != filters.foreign_currency:
- # months_obj = calculate_monthly_ammount(invoice.foreign_total,invoice.from_date,invoice.to_date)
- # else:
- # months_obj = calculate_monthly_ammount(invoice.total,invoice.from_date,invoice.to_date)
- # if months_obj:
- # for key,value in months_obj.items():
- # invoice[key] = value
-
- invoice_id = "'{invoice_id}'".format(invoice_id=invoice["invoice_id"])
-
- query_items = """
+ """
+
+ sales_invoices = frappe.db.sql(query, as_dict=True)
+
+ for invoice in sales_invoices:
+ _items_rwos = []
+ append = False
+ property_name = frappe.db.get_value("Lease", invoice["lease"], "property")
+ invoice["property_name"] = property_name
+ if invoice.total == invoice.foreign_total:
+ invoice.foreign_total, invoice.exchange_rate = "", ""
+ # if filters.get("foreign_currency") and get_company_currency(filters.company) != filters.foreign_currency:
+ # months_obj = calculate_monthly_ammount(invoice.foreign_total,invoice.from_date,invoice.to_date)
+ # else:
+ # months_obj = calculate_monthly_ammount(invoice.total,invoice.from_date,invoice.to_date)
+ # if months_obj:
+ # for key,value in months_obj.items():
+ # invoice[key] = value
+
+ invoice_id = "'{invoice_id}'".format(invoice_id=invoice["invoice_id"])
+
+ query_items = f"""
SELECT
- item_code,
- base_net_amount as item_total,
+ item_code,
+ base_net_amount as item_total,
net_amount as item_foreign_total,
- service_start_date as from_date,
- service_end_date as to_date,
- qty as quantity,
+ service_start_date as from_date,
+ service_end_date as to_date,
+ qty as quantity,
net_rate
FROM
`tabSales Invoice Item`
WHERE
parent = {invoice_id}
- """.format(
- invoice_id=invoice_id
- )
-
- items = frappe.db.sql(query_items, as_dict=True)
- for item in items:
- item_group = frappe.db.get_value("Item", item["item_code"], "item_group")
- item["item_group"] = item_group
- item.item_foreign_total = flt(item.item_foreign_total, float_precision)
- item.item_total = flt(item.item_total, float_precision)
- months_obj = calculate_monthly_ammount(
- item.item_total,
- default_currency,
- item.from_date,
- item.to_date,
- item.item_foreign_total,
- filters.get("foreign_currency"),
- filters,
- )
- if months_obj:
- for key, value in months_obj.items():
- item[key] = value
- if _items_grupe == "All Item Groups":
- _items_rwos.append(item)
- append = True
- elif _items_grupe == item_group:
- _items_rwos.append(item)
- append = True
- if filters.get("foreign_currency"):
- item.item_total = flt(item.item_foreign_total, float_precision)
- else:
- item.item_total = flt(item.item_total, float_precision)
- if append and (
- filters.foreign_currency == invoice.currency or not filters.foreign_currency
- ):
- # rows.append(invoice)
- for item in _items_rwos:
- item.update(invoice)
- rows.append(item)
- # rows.append({})
-
- return rows
+ """
+
+ items = frappe.db.sql(query_items, as_dict=True)
+ for item in items:
+ item_group = frappe.db.get_value("Item", item["item_code"], "item_group")
+ item["item_group"] = item_group
+ item.item_foreign_total = flt(item.item_foreign_total, float_precision)
+ item.item_total = flt(item.item_total, float_precision)
+ months_obj = calculate_monthly_ammount(
+ item.item_total,
+ default_currency,
+ item.from_date,
+ item.to_date,
+ item.item_foreign_total,
+ filters.get("foreign_currency"),
+ filters,
+ )
+ if months_obj:
+ for key, value in months_obj.items():
+ item[key] = value
+ if _items_grupe == "All Item Groups":
+ _items_rwos.append(item)
+ append = True
+ elif _items_grupe == item_group:
+ _items_rwos.append(item)
+ append = True
+ if filters.get("foreign_currency"):
+ item.item_total = flt(item.item_foreign_total, float_precision)
+ else:
+ item.item_total = flt(item.item_total, float_precision)
+ if append and (filters.foreign_currency == invoice.currency or not filters.foreign_currency):
+ # rows.append(invoice)
+ for item in _items_rwos:
+ item.update(invoice)
+ rows.append(item)
+ # rows.append({})
+
+ return rows
def get_columns(filters):
- if filters.get("company"):
- currency = get_company_currency(filters["company"])
- else:
- company = get_default_company()
- currency = get_company_currency(company)
-
- if filters.get("foreign_currency"):
- _foreign_currency = filters["foreign_currency"]
- else:
- _foreign_currency = currency
-
- if _foreign_currency == currency:
- foreign_currency = "Foreign"
- else:
- foreign_currency = filters["foreign_currency"]
-
- columns = [
- {
- "label": "Property",
- "fieldname": "property_name",
- "fieldtype": "Link",
- "options": "Property",
- "width": 100,
- },
- {
- "label": "Customer",
- "fieldname": "customer",
- "fieldtype": "Link",
- "options": "Customer",
- "width": 100,
- },
- {
- "label": "Lease",
- "fieldname": "lease",
- "fieldtype": "Link",
- "options": "Lease",
- "width": 100,
- },
- {
- "label": "Advance Before {0}".format(_foreign_currency),
- "fieldname": "advance_before",
- "fieldtype": "Float",
- "width": 100,
- },
- {
- "label": "Invoice",
- "fieldname": "invoice_id",
- "fieldtype": "Link",
- "options": "Sales Invoice",
- "width": 150,
- },
- {
- "label": "Date",
- "fieldname": "date",
- "fieldtype": "date",
- "width": 100,
- },
- # {
- # "label": "Total {0}".format(currency),
- # "fieldname": "total",
- # "fieldtype": "Float",
- # "width": 100,
- # },
- {
- "label": "Exchange Rate",
- "fieldname": "exchange_rate",
- "fieldtype": "Float",
- "width": 100,
- },
- # {
- # "label": "Total {0}".format(foreign_currency or "Foreign"),
- # "fieldname": "foreign_total",
- # "fieldtype": "Float",
- # "width": 100,
- # },
- {
- "label": "Item",
- "fieldname": "item_code",
- "fieldtype": "Link",
- "options": "Item",
- "width": 100,
- },
- {
- "label": "Quantity",
- "fieldname": "quantity",
- "width": 75,
- },
- {
- "label": "Item Total {0}".format(_foreign_currency),
- "fieldname": "item_total",
- "fieldtype": "Float",
- "width": 100,
- },
- {
- "label": "From Date",
- "fieldname": "from_date",
- "fieldtype": "date",
- "width": 100,
- },
- {
- "label": "To Date",
- "fieldname": "to_date",
- "fieldtype": "date",
- "width": 100,
- },
- ]
-
- months_list = get_months(filters["from_date"], filters["to_date"])
-
- for month in months_list:
- columns.append(
- {
- "label": "{0} {1}".format(month, currency),
- "fieldname": "{0} {1}".format(month.lower(), currency),
- "fieldtype": "Float",
- "width": 100,
- }
- )
- if (
- filters.get("foreign_currency")
- and filters.get("foreign_currency") != currency
- ):
- columns.append(
- {
- "label": "{0} {1}".format(month, filters.get("foreign_currency")),
- "fieldname": "{0} {1}".format(
- month.lower(), filters.get("foreign_currency")
- ),
- "fieldtype": "Float",
- "width": 100,
- }
- )
-
- columns.append(
- {
- "label": "Advance After {0}".format(_foreign_currency),
- "fieldname": "advance_after",
- "fieldtype": "Float",
- "width": 100,
- }
- )
-
- return columns
+ if filters.get("company"):
+ currency = get_company_currency(filters["company"])
+ else:
+ company = get_default_company()
+ currency = get_company_currency(company)
+
+ if filters.get("foreign_currency"):
+ _foreign_currency = filters["foreign_currency"]
+ else:
+ _foreign_currency = currency
+
+ columns = [
+ {
+ "label": "Property",
+ "fieldname": "property_name",
+ "fieldtype": "Link",
+ "options": "Property",
+ "width": 100,
+ },
+ {
+ "label": "Customer",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "options": "Customer",
+ "width": 100,
+ },
+ {
+ "label": "Lease",
+ "fieldname": "lease",
+ "fieldtype": "Link",
+ "options": "Lease",
+ "width": 100,
+ },
+ {
+ "label": f"Advance Before {_foreign_currency}",
+ "fieldname": "advance_before",
+ "fieldtype": "Float",
+ "width": 100,
+ },
+ {
+ "label": "Invoice",
+ "fieldname": "invoice_id",
+ "fieldtype": "Link",
+ "options": "Sales Invoice",
+ "width": 150,
+ },
+ {
+ "label": "Date",
+ "fieldname": "date",
+ "fieldtype": "date",
+ "width": 100,
+ },
+ # {
+ # "label": "Total {0}".format(currency),
+ # "fieldname": "total",
+ # "fieldtype": "Float",
+ # "width": 100,
+ # },
+ {
+ "label": "Exchange Rate",
+ "fieldname": "exchange_rate",
+ "fieldtype": "Float",
+ "width": 100,
+ },
+ # {
+ # "label": "Total {0}".format(foreign_currency or "Foreign"),
+ # "fieldname": "foreign_total",
+ # "fieldtype": "Float",
+ # "width": 100,
+ # },
+ {
+ "label": "Item",
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "options": "Item",
+ "width": 100,
+ },
+ {
+ "label": "Quantity",
+ "fieldname": "quantity",
+ "width": 75,
+ },
+ {
+ "label": f"Item Total {_foreign_currency}",
+ "fieldname": "item_total",
+ "fieldtype": "Float",
+ "width": 100,
+ },
+ {
+ "label": "From Date",
+ "fieldname": "from_date",
+ "fieldtype": "date",
+ "width": 100,
+ },
+ {
+ "label": "To Date",
+ "fieldname": "to_date",
+ "fieldtype": "date",
+ "width": 100,
+ },
+ ]
+
+ months_list = get_months(filters["from_date"], filters["to_date"])
+
+ for month in months_list:
+ columns.append(
+ {
+ "label": f"{month} {currency}",
+ "fieldname": f"{month.lower()} {currency}",
+ "fieldtype": "Float",
+ "width": 100,
+ }
+ )
+ if filters.get("foreign_currency") and filters.get("foreign_currency") != currency:
+ columns.append(
+ {
+ "label": "{} {}".format(month, filters.get("foreign_currency")),
+ "fieldname": "{} {}".format(month.lower(), filters.get("foreign_currency")),
+ "fieldtype": "Float",
+ "width": 100,
+ }
+ )
+
+ columns.append(
+ {
+ "label": f"Advance After {_foreign_currency}",
+ "fieldname": "advance_after",
+ "fieldtype": "Float",
+ "width": 100,
+ }
+ )
+
+ return columns
def get_months(from_date, to_date):
- months_list = []
- dates = [from_date, to_date]
- start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
- months_obj = OrderedDict(
- ((start + timedelta(_)).strftime(r"%b-%y"), None)
- for _ in range((end - start).days)
- )
- for key, value in months_obj.items():
- months_list.append(key)
- return months_list
+ months_list = []
+ dates = [from_date, to_date]
+ start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
+ months_obj = OrderedDict(
+ ((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in range((end - start).days)
+ )
+ for key, _value in months_obj.items():
+ months_list.append(key)
+ return months_list
def check_full_month(from_date, to_date):
- month_start_day = get_first_day(from_date)
- month_end_day = get_last_day(from_date)
- if from_date == month_start_day and to_date == month_end_day:
- return True
- else:
- return False
+ month_start_day = get_first_day(from_date)
+ month_end_day = get_last_day(from_date)
+ if from_date == month_start_day and to_date == month_end_day:
+ return True
+ else:
+ return False
def calculate_monthly_ammount(
- ammount,
- default_currency,
- from_date,
- to_date,
- foreign_ammount,
- foreign_currency=None,
- filters=None,
+ ammount,
+ default_currency,
+ from_date,
+ to_date,
+ foreign_ammount,
+ foreign_currency=None,
+ filters=None,
):
- float_precision = cint(frappe.db.get_default("float_precision")) or 2
- months_report_list = []
- for month in get_months(filters["from_date"], filters["to_date"]):
- months_report_list.append("{0} {1}".format(month.lower(), default_currency))
- if (
- filters.get("foreign_currency")
- and filters.get("foreign_currency") != default_currency
- ):
- months_report_list.append(
- "{0} {1}".format(month.lower(), filters.get("foreign_currency"))
- )
- if ammount and from_date and to_date:
- monthly_ammount_obj = {}
- days = 0
- date = from_date
- end_date = to_date
- field_list = []
- field_list_foreign = []
- first_last = 0
- first_last_foreign = 0
- sub_ammount = 0
- sub_ammount_foreign = 0
- # days_list= []
-
- while date <= end_date:
- start_month = getdate(date).month
- end_month = getdate(to_date).month
-
- if start_month == end_month:
- last_day = end_date
- days_diff = date_diff(last_day, date) + 1
- if check_full_month(date, last_day):
- days_diff = 30
- days += days_diff
- # days_list.append(days_diff)
- if date == last_day:
- last_day = add_days(last_day, 1)
- month_filed = (get_months(str(date), str(last_day))[0]).lower()
- month_len = date_diff(get_last_day(date), get_first_day(date))
- field_list.append(
- {
- "days_diff": days_diff,
- "month_filed": "{0} {1}".format(month_filed, default_currency),
- "month_len": month_len,
- "foreign": False,
- }
- )
- if foreign_currency and foreign_currency != default_currency:
- field_list_foreign.append(
- {
- "days_diff": days_diff,
- "month_filed": "{0} {1}".format(
- month_filed, foreign_currency
- ),
- "month_len": month_len,
- "foreign": True,
- }
- )
- date = get_first_day(add_months(date, 1))
-
- else:
- last_day = get_last_day(date)
- days_diff = date_diff(last_day, date) + 1
- if check_full_month(date, last_day):
- days_diff = 30
- days += days_diff
- # days_list.append(days_diff)
- if date == last_day:
- last_day = add_days(last_day, 1)
- month_filed = (get_months(str(date), str(last_day))[0]).lower()
- month_len = date_diff(get_last_day(date), get_first_day(date))
- field_list.append(
- {
- "days_diff": days_diff,
- "month_filed": "{0} {1}".format(month_filed, default_currency),
- "month_len": month_len,
- "foreign": False,
- }
- )
- if foreign_currency and foreign_currency != default_currency:
- field_list_foreign.append(
- {
- "days_diff": days_diff,
- "month_filed": "{0} {1}".format(
- month_filed, foreign_currency
- ),
- "month_len": month_len,
- "foreign": True,
- }
- )
- date = get_first_day(add_months(date, 1))
-
- if floor(days / 30) != (days / 30) and (floor(days / 30) * 30 + 6) < days:
- days = (floor(days / 30) + 1) * 30
- elif floor(days / 30) != (days / 30) and floor(days / 30) * 30 < days:
- days = floor(days / 30) * 30
-
- daily_ammount = ammount / (days)
- daily_ammount_foreign = foreign_ammount / (days)
-
- m = 1
- for i in field_list:
- if m == 1 and i["days_diff"] < 30:
- first_last += i["days_diff"]
- elif m == len(field_list) and i["days_diff"] < 30:
- first_last += i["days_diff"]
- else:
- sub_ammount += i["days_diff"] * daily_ammount
- m += 1
-
- m = 1
- for i in field_list_foreign:
- if m == 1 and i["days_diff"] < 30:
- first_last_foreign += i["days_diff"]
- elif m == len(field_list) and i["days_diff"] < 30:
- first_last_foreign += i["days_diff"]
- else:
- sub_ammount_foreign += i["days_diff"] * daily_ammount_foreign
- m += 1
-
- n = 1
- for i in field_list_foreign:
- if n == 1 and i["days_diff"] < 30:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"]
- * ((foreign_ammount - sub_ammount_foreign) / first_last),
- float_precision,
- )
- elif n == len(field_list) and i["days_diff"] < 30:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"]
- * ((foreign_ammount - sub_ammount_foreign) / first_last),
- float_precision,
- )
- else:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"] * daily_ammount_foreign, float_precision
- )
- n += 1
-
- n = 1
- for i in field_list:
- if n == 1 and i["days_diff"] < 30:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"] * ((ammount - sub_ammount) / first_last),
- float_precision,
- )
- elif n == len(field_list) and i["days_diff"] < 30:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"] * ((ammount - sub_ammount) / first_last),
- float_precision,
- )
- else:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"] * daily_ammount, float_precision
- )
- n += 1
- advance_before = 0
- advance_after = 0
- first_month = datetime.strptime(
- months_report_list[0][:6].replace("-", " 20"), "%b %Y"
- )
- last_month = datetime.strptime(
- months_report_list[-1][:6].replace("-", " 20"), "%b %Y"
- )
- for key, value in monthly_ammount_obj.items():
- key_currency = key[7:]
- currency = foreign_currency or default_currency
- if key not in months_report_list and key_currency == currency:
- mydate = datetime.strptime(str(key)[:6].replace("-", " 20"), "%b %Y")
- if mydate > last_month:
- advance_after += value
- elif mydate < first_month:
- advance_before += value
- if advance_after:
- monthly_ammount_obj["advance_after"] = advance_after
- if advance_before:
- monthly_ammount_obj["advance_before"] = advance_before
- return monthly_ammount_obj
+ float_precision = cint(frappe.db.get_default("float_precision")) or 2
+ months_report_list = []
+ for month in get_months(filters["from_date"], filters["to_date"]):
+ months_report_list.append(f"{month.lower()} {default_currency}")
+ if filters.get("foreign_currency") and filters.get("foreign_currency") != default_currency:
+ months_report_list.append("{} {}".format(month.lower(), filters.get("foreign_currency")))
+ if ammount and from_date and to_date:
+ monthly_ammount_obj = {}
+ days = 0
+ date = from_date
+ end_date = to_date
+ field_list = []
+ field_list_foreign = []
+ first_last = 0
+ first_last_foreign = 0
+ sub_ammount = 0
+ sub_ammount_foreign = 0
+ # days_list= []
+
+ while date <= end_date:
+ start_month = getdate(date).month
+ end_month = getdate(to_date).month
+
+ if start_month == end_month:
+ last_day = end_date
+ days_diff = date_diff(last_day, date) + 1
+ if check_full_month(date, last_day):
+ days_diff = 30
+ days += days_diff
+ # days_list.append(days_diff)
+ if date == last_day:
+ last_day = add_days(last_day, 1)
+ month_filed = (get_months(str(date), str(last_day))[0]).lower()
+ month_len = date_diff(get_last_day(date), get_first_day(date))
+ field_list.append(
+ {
+ "days_diff": days_diff,
+ "month_filed": f"{month_filed} {default_currency}",
+ "month_len": month_len,
+ "foreign": False,
+ }
+ )
+ if foreign_currency and foreign_currency != default_currency:
+ field_list_foreign.append(
+ {
+ "days_diff": days_diff,
+ "month_filed": f"{month_filed} {foreign_currency}",
+ "month_len": month_len,
+ "foreign": True,
+ }
+ )
+ date = get_first_day(add_months(date, 1))
+
+ else:
+ last_day = get_last_day(date)
+ days_diff = date_diff(last_day, date) + 1
+ if check_full_month(date, last_day):
+ days_diff = 30
+ days += days_diff
+ # days_list.append(days_diff)
+ if date == last_day:
+ last_day = add_days(last_day, 1)
+ month_filed = (get_months(str(date), str(last_day))[0]).lower()
+ month_len = date_diff(get_last_day(date), get_first_day(date))
+ field_list.append(
+ {
+ "days_diff": days_diff,
+ "month_filed": f"{month_filed} {default_currency}",
+ "month_len": month_len,
+ "foreign": False,
+ }
+ )
+ if foreign_currency and foreign_currency != default_currency:
+ field_list_foreign.append(
+ {
+ "days_diff": days_diff,
+ "month_filed": f"{month_filed} {foreign_currency}",
+ "month_len": month_len,
+ "foreign": True,
+ }
+ )
+ date = get_first_day(add_months(date, 1))
+
+ if floor(days / 30) != (days / 30) and (floor(days / 30) * 30 + 6) < days:
+ days = (floor(days / 30) + 1) * 30
+ elif floor(days / 30) != (days / 30) and floor(days / 30) * 30 < days:
+ days = floor(days / 30) * 30
+
+ daily_ammount = ammount / (days)
+ daily_ammount_foreign = foreign_ammount / (days)
+
+ m = 1
+ for i in field_list:
+ if m == 1 and i["days_diff"] < 30:
+ first_last += i["days_diff"]
+ elif m == len(field_list) and i["days_diff"] < 30:
+ first_last += i["days_diff"]
+ else:
+ sub_ammount += i["days_diff"] * daily_ammount
+ m += 1
+
+ m = 1
+ for i in field_list_foreign:
+ if m == 1 and i["days_diff"] < 30:
+ first_last_foreign += i["days_diff"]
+ elif m == len(field_list) and i["days_diff"] < 30:
+ first_last_foreign += i["days_diff"]
+ else:
+ sub_ammount_foreign += i["days_diff"] * daily_ammount_foreign
+ m += 1
+
+ n = 1
+ for i in field_list_foreign:
+ if n == 1 and i["days_diff"] < 30:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * ((foreign_ammount - sub_ammount_foreign) / first_last),
+ float_precision,
+ )
+ elif n == len(field_list) and i["days_diff"] < 30:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * ((foreign_ammount - sub_ammount_foreign) / first_last),
+ float_precision,
+ )
+ else:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * daily_ammount_foreign, float_precision
+ )
+ n += 1
+
+ n = 1
+ for i in field_list:
+ if n == 1 and i["days_diff"] < 30:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * ((ammount - sub_ammount) / first_last),
+ float_precision,
+ )
+ elif n == len(field_list) and i["days_diff"] < 30:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * ((ammount - sub_ammount) / first_last),
+ float_precision,
+ )
+ else:
+ monthly_ammount_obj[i["month_filed"]] = flt(i["days_diff"] * daily_ammount, float_precision)
+ n += 1
+ advance_before = 0
+ advance_after = 0
+ first_month = datetime.strptime(months_report_list[0][:6].replace("-", " 20"), "%b %Y")
+ last_month = datetime.strptime(months_report_list[-1][:6].replace("-", " 20"), "%b %Y")
+ for key, value in monthly_ammount_obj.items():
+ key_currency = key[7:]
+ currency = foreign_currency or default_currency
+ if key not in months_report_list and key_currency == currency:
+ mydate = datetime.strptime(str(key)[:6].replace("-", " 20"), "%b %Y")
+ if mydate > last_month:
+ advance_after += value
+ elif mydate < first_month:
+ advance_before += value
+ if advance_after:
+ monthly_ammount_obj["advance_after"] = advance_after
+ if advance_before:
+ monthly_ammount_obj["advance_before"] = advance_before
+ return monthly_ammount_obj
diff --git a/propms/property_management_solution/report/rent_invoices_details_usd/rent_invoices_details_usd.py b/propms/property_management_solution/report/rent_invoices_details_usd/rent_invoices_details_usd.py
index 9fd8a510..d3fef271 100644
--- a/propms/property_management_solution/report/rent_invoices_details_usd/rent_invoices_details_usd.py
+++ b/propms/property_management_solution/report/rent_invoices_details_usd/rent_invoices_details_usd.py
@@ -1,501 +1,463 @@
# Copyright (c) 2013, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
-from time import strptime
-import calendar
-from datetime import date, timedelta, datetime
+
from collections import OrderedDict
-from frappe.utils import (
- getdate,
- date_diff,
- month_diff,
- get_last_day,
- get_first_day,
- add_months,
- floor,
- add_days,
- flt,
- cint,
-)
+from datetime import datetime, timedelta
+
+import frappe
from erpnext import get_company_currency, get_default_company
from erpnext.setup.utils import get_exchange_rate
+from frappe.utils import (
+ add_days,
+ add_months,
+ cint,
+ date_diff,
+ floor,
+ flt,
+ get_first_day,
+ get_last_day,
+ getdate,
+)
def execute(filters=None):
- filters["foreign_currency"] = "USD"
- data = get_data(filters)
- columns = get_columns(filters)
- return columns, data
+ filters["foreign_currency"] = "USD"
+ data = get_data(filters)
+ columns = get_columns(filters)
+ return columns, data
def get_data(filters):
- rows = []
- _from_date = "'{from_date}'".format(from_date=filters["from_date"])
- _to_date = "'{to_date}'".format(to_date=filters["to_date"])
- _company = "'{company}'".format(company=filters["company"])
- _items_grupe = filters.get("type_name")
- float_precision = cint(frappe.db.get_default("float_precision")) or 2
- if filters.get("company"):
- default_currency = get_company_currency(filters["company"])
- else:
- company = get_default_company()
- default_currency = get_company_currency(company)
-
- conditions = ""
- if not filters.get("extand"):
- conditions = "AND DATE(posting_date) BETWEEN {start} AND {end}".format(
- start=_from_date, end=_to_date
- )
-
- query = """
+ rows = []
+ _from_date = "'{from_date}'".format(from_date=filters["from_date"])
+ _to_date = "'{to_date}'".format(to_date=filters["to_date"])
+ _company = "'{company}'".format(company=filters["company"])
+ _items_grupe = filters.get("type_name")
+ float_precision = cint(frappe.db.get_default("float_precision")) or 2
+ if filters.get("company"):
+ default_currency = get_company_currency(filters["company"])
+ else:
+ company = get_default_company()
+ default_currency = get_company_currency(company)
+
+ conditions = ""
+ if not filters.get("extand"):
+ conditions = f"AND DATE(posting_date) BETWEEN {_from_date} AND {_to_date}"
+
+ query = f"""
SELECT
- name as invoice_id,
- customer,
- base_net_total as total,
+ name as invoice_id,
+ customer,
+ base_net_total as total,
net_total as foreign_total,
- currency,
- conversion_rate as exchange_rate,
- posting_date as date,
+ currency,
+ conversion_rate as exchange_rate,
+ posting_date as date,
lease
FROM
`tabSales Invoice`
WHERE
- docstatus = 1
- AND company = {company}
+ docstatus = 1
+ AND company = {_company}
AND lease != ""
AND from_date != ""
AND to_date != ""
{conditions}
ORDER BY lease DESC, posting_date ASC
- """.format(
- conditions=conditions, company=_company
- )
-
- sales_invoices = frappe.db.sql(query, as_dict=True)
-
- for invoice in sales_invoices:
- _items_rwos = []
- append = False
- property_name = frappe.db.get_value("Lease", invoice["lease"], "property")
- invoice["property_name"] = property_name
- if invoice.total == invoice.foreign_total:
- invoice.exchange_rate = get_exchange_rate(
- "USD", default_currency, invoice.posting_date
- )
- invoice.foreign_total = invoice.total / invoice.exchange_rate
-
- invoice_id = "'{invoice_id}'".format(invoice_id=invoice["invoice_id"])
-
- query_items = """
+ """
+
+ sales_invoices = frappe.db.sql(query, as_dict=True)
+
+ for invoice in sales_invoices:
+ _items_rwos = []
+ append = False
+ property_name = frappe.db.get_value("Lease", invoice["lease"], "property")
+ invoice["property_name"] = property_name
+ if invoice.total == invoice.foreign_total:
+ invoice.exchange_rate = get_exchange_rate("USD", default_currency, invoice.posting_date)
+ invoice.foreign_total = invoice.total / invoice.exchange_rate
+
+ invoice_id = "'{invoice_id}'".format(invoice_id=invoice["invoice_id"])
+
+ query_items = f"""
SELECT
- item_code,
- base_net_amount as item_total,
+ item_code,
+ base_net_amount as item_total,
net_amount as item_foreign_total,
- service_start_date as from_date,
- service_end_date as to_date,
- qty as quantity,
+ service_start_date as from_date,
+ service_end_date as to_date,
+ qty as quantity,
net_rate
FROM
`tabSales Invoice Item`
WHERE
parent = {invoice_id}
- """.format(
- invoice_id=invoice_id
- )
-
- items = frappe.db.sql(query_items, as_dict=True)
- for item in items:
- item_group = frappe.db.get_value("Item", item["item_code"], "item_group")
- item["item_group"] = item_group
- item.item_foreign_total = flt(item.item_foreign_total, float_precision)
- item.item_total = flt(item.item_total, float_precision)
- if item.item_foreign_total == item.item_total:
- item.item_foreign_total = item.item_total / invoice.exchange_rate
- months_obj = calculate_monthly_ammount(
- item.item_total,
- default_currency,
- item.from_date,
- item.to_date,
- item.item_foreign_total,
- filters.get("foreign_currency"),
- filters,
- )
- if months_obj:
- for key, value in months_obj.items():
- item[key] = value
- if _items_grupe == "All Item Groups":
- _items_rwos.append(item)
- append = True
- elif _items_grupe == item_group:
- _items_rwos.append(item)
- append = True
- if filters.get("foreign_currency"):
- item.item_total = flt(item.item_foreign_total, float_precision)
- else:
- item.item_total = flt(item.item_total, float_precision)
- if append:
- for item in _items_rwos:
- item.update(invoice)
- rows.append(item)
-
- return rows
+ """
+
+ items = frappe.db.sql(query_items, as_dict=True)
+ for item in items:
+ item_group = frappe.db.get_value("Item", item["item_code"], "item_group")
+ item["item_group"] = item_group
+ item.item_foreign_total = flt(item.item_foreign_total, float_precision)
+ item.item_total = flt(item.item_total, float_precision)
+ if item.item_foreign_total == item.item_total:
+ item.item_foreign_total = item.item_total / invoice.exchange_rate
+ months_obj = calculate_monthly_ammount(
+ item.item_total,
+ default_currency,
+ item.from_date,
+ item.to_date,
+ item.item_foreign_total,
+ filters.get("foreign_currency"),
+ filters,
+ )
+ if months_obj:
+ for key, value in months_obj.items():
+ item[key] = value
+ if _items_grupe == "All Item Groups":
+ _items_rwos.append(item)
+ append = True
+ elif _items_grupe == item_group:
+ _items_rwos.append(item)
+ append = True
+ if filters.get("foreign_currency"):
+ item.item_total = flt(item.item_foreign_total, float_precision)
+ else:
+ item.item_total = flt(item.item_total, float_precision)
+ if append:
+ for item in _items_rwos:
+ item.update(invoice)
+ rows.append(item)
+
+ return rows
def get_columns(filters):
- if filters.get("company"):
- currency = get_company_currency(filters["company"])
- else:
- company = get_default_company()
- currency = get_company_currency(company)
-
- if filters.get("foreign_currency"):
- _foreign_currency = filters["foreign_currency"]
- else:
- _foreign_currency = currency
-
- if _foreign_currency == currency:
- foreign_currency = "Foreign"
- else:
- foreign_currency = filters["foreign_currency"]
-
- columns = [
- {
- "label": "Property",
- "fieldname": "property_name",
- "fieldtype": "Link",
- "options": "Property",
- "width": 100,
- },
- {
- "label": "Customer",
- "fieldname": "customer",
- "fieldtype": "Link",
- "options": "Customer",
- "width": 100,
- },
- {
- "label": "Lease",
- "fieldname": "lease",
- "fieldtype": "Link",
- "options": "Lease",
- "width": 100,
- },
- {
- "label": "Advance Before {0}".format(_foreign_currency),
- "fieldname": "advance_before",
- "fieldtype": "Float",
- "width": 100,
- },
- {
- "label": "Invoice",
- "fieldname": "invoice_id",
- "fieldtype": "Link",
- "options": "Sales Invoice",
- "width": 150,
- },
- {
- "label": "Date",
- "fieldname": "date",
- "fieldtype": "date",
- "width": 100,
- },
- # {
- # "label": "Total {0}".format(currency),
- # "fieldname": "total",
- # "fieldtype": "Float",
- # "width": 100,
- # },
- {
- "label": "Exchange Rate",
- "fieldname": "exchange_rate",
- "fieldtype": "Float",
- "width": 100,
- },
- # {
- # "label": "Total {0}".format(foreign_currency or "Foreign"),
- # "fieldname": "foreign_total",
- # "fieldtype": "Float",
- # "width": 100,
- # },
- {
- "label": "Item",
- "fieldname": "item_code",
- "fieldtype": "Link",
- "options": "Item",
- "width": 100,
- },
- {
- "label": "Quantity",
- "fieldname": "quantity",
- "width": 75,
- },
- {
- "label": "Item Total {0}".format(_foreign_currency),
- "fieldname": "item_total",
- "fieldtype": "Float",
- "width": 100,
- },
- {
- "label": "From Date",
- "fieldname": "from_date",
- "fieldtype": "date",
- "width": 100,
- },
- {
- "label": "To Date",
- "fieldname": "to_date",
- "fieldtype": "date",
- "width": 100,
- },
- ]
-
- months_list = get_months(filters["from_date"], filters["to_date"])
-
- for month in months_list:
- columns.append(
- {
- "label": "{0} {1}".format(month, currency),
- "fieldname": "{0} {1}".format(month.lower(), currency),
- "fieldtype": "Float",
- "width": 100,
- }
- )
- if (
- filters.get("foreign_currency")
- and filters.get("foreign_currency") != currency
- ):
- columns.append(
- {
- "label": "{0} {1}".format(month, filters.get("foreign_currency")),
- "fieldname": "{0} {1}".format(
- month.lower(), filters.get("foreign_currency")
- ),
- "fieldtype": "Float",
- "width": 100,
- }
- )
-
- columns.append(
- {
- "label": "Advance After {0}".format(_foreign_currency),
- "fieldname": "advance_after",
- "fieldtype": "Float",
- "width": 100,
- }
- )
-
- return columns
+ if filters.get("company"):
+ currency = get_company_currency(filters["company"])
+ else:
+ company = get_default_company()
+ currency = get_company_currency(company)
+
+ if filters.get("foreign_currency"):
+ _foreign_currency = filters["foreign_currency"]
+ else:
+ _foreign_currency = currency
+
+ columns = [
+ {
+ "label": "Property",
+ "fieldname": "property_name",
+ "fieldtype": "Link",
+ "options": "Property",
+ "width": 100,
+ },
+ {
+ "label": "Customer",
+ "fieldname": "customer",
+ "fieldtype": "Link",
+ "options": "Customer",
+ "width": 100,
+ },
+ {
+ "label": "Lease",
+ "fieldname": "lease",
+ "fieldtype": "Link",
+ "options": "Lease",
+ "width": 100,
+ },
+ {
+ "label": f"Advance Before {_foreign_currency}",
+ "fieldname": "advance_before",
+ "fieldtype": "Float",
+ "width": 100,
+ },
+ {
+ "label": "Invoice",
+ "fieldname": "invoice_id",
+ "fieldtype": "Link",
+ "options": "Sales Invoice",
+ "width": 150,
+ },
+ {
+ "label": "Date",
+ "fieldname": "date",
+ "fieldtype": "date",
+ "width": 100,
+ },
+ # {
+ # "label": "Total {0}".format(currency),
+ # "fieldname": "total",
+ # "fieldtype": "Float",
+ # "width": 100,
+ # },
+ {
+ "label": "Exchange Rate",
+ "fieldname": "exchange_rate",
+ "fieldtype": "Float",
+ "width": 100,
+ },
+ # {
+ # "label": "Total {0}".format(foreign_currency or "Foreign"),
+ # "fieldname": "foreign_total",
+ # "fieldtype": "Float",
+ # "width": 100,
+ # },
+ {
+ "label": "Item",
+ "fieldname": "item_code",
+ "fieldtype": "Link",
+ "options": "Item",
+ "width": 100,
+ },
+ {
+ "label": "Quantity",
+ "fieldname": "quantity",
+ "width": 75,
+ },
+ {
+ "label": f"Item Total {_foreign_currency}",
+ "fieldname": "item_total",
+ "fieldtype": "Float",
+ "width": 100,
+ },
+ {
+ "label": "From Date",
+ "fieldname": "from_date",
+ "fieldtype": "date",
+ "width": 100,
+ },
+ {
+ "label": "To Date",
+ "fieldname": "to_date",
+ "fieldtype": "date",
+ "width": 100,
+ },
+ ]
+
+ months_list = get_months(filters["from_date"], filters["to_date"])
+
+ for month in months_list:
+ columns.append(
+ {
+ "label": f"{month} {currency}",
+ "fieldname": f"{month.lower()} {currency}",
+ "fieldtype": "Float",
+ "width": 100,
+ }
+ )
+ if filters.get("foreign_currency") and filters.get("foreign_currency") != currency:
+ columns.append(
+ {
+ "label": "{} {}".format(month, filters.get("foreign_currency")),
+ "fieldname": "{} {}".format(month.lower(), filters.get("foreign_currency")),
+ "fieldtype": "Float",
+ "width": 100,
+ }
+ )
+
+ columns.append(
+ {
+ "label": f"Advance After {_foreign_currency}",
+ "fieldname": "advance_after",
+ "fieldtype": "Float",
+ "width": 100,
+ }
+ )
+
+ return columns
def get_months(from_date, to_date):
- months_list = []
- dates = [from_date, to_date]
- start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
- months_obj = OrderedDict(
- ((start + timedelta(_)).strftime(r"%b-%y"), None)
- for _ in range((end - start).days)
- )
- for key, value in months_obj.items():
- months_list.append(key)
- return months_list
+ months_list = []
+ dates = [from_date, to_date]
+ start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
+ months_obj = OrderedDict(
+ ((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in range((end - start).days)
+ )
+ for key, _value in months_obj.items():
+ months_list.append(key)
+ return months_list
def check_full_month(from_date, to_date):
- month_start_day = get_first_day(from_date)
- month_end_day = get_last_day(from_date)
- if from_date == month_start_day and to_date == month_end_day:
- return True
- else:
- return False
+ month_start_day = get_first_day(from_date)
+ month_end_day = get_last_day(from_date)
+ if from_date == month_start_day and to_date == month_end_day:
+ return True
+ else:
+ return False
def calculate_monthly_ammount(
- ammount,
- default_currency,
- from_date,
- to_date,
- foreign_ammount,
- foreign_currency=None,
- filters=None,
+ ammount,
+ default_currency,
+ from_date,
+ to_date,
+ foreign_ammount,
+ foreign_currency=None,
+ filters=None,
):
- float_precision = cint(frappe.db.get_default("float_precision")) or 2
- months_report_list = []
- for month in get_months(filters["from_date"], filters["to_date"]):
- months_report_list.append("{0} {1}".format(month.lower(), default_currency))
- if (
- filters.get("foreign_currency")
- and filters.get("foreign_currency") != default_currency
- ):
- months_report_list.append(
- "{0} {1}".format(month.lower(), filters.get("foreign_currency"))
- )
- if ammount and from_date and to_date:
- monthly_ammount_obj = {}
- days = 0
- date = from_date
- end_date = to_date
- field_list = []
- field_list_foreign = []
- first_last = 0
- first_last_foreign = 0
- sub_ammount = 0
- sub_ammount_foreign = 0
- # days_list= []
-
- while date <= end_date:
- start_month = getdate(date).month
- end_month = getdate(to_date).month
-
- if start_month == end_month:
- last_day = end_date
- days_diff = date_diff(last_day, date) + 1
- if check_full_month(date, last_day):
- days_diff = 30
- days += days_diff
- # days_list.append(days_diff)
- if date == last_day:
- last_day = add_days(last_day, 1)
- month_filed = (get_months(str(date), str(last_day))[0]).lower()
- month_len = date_diff(get_last_day(date), get_first_day(date))
- field_list.append(
- {
- "days_diff": days_diff,
- "month_filed": "{0} {1}".format(month_filed, default_currency),
- "month_len": month_len,
- "foreign": False,
- }
- )
- if foreign_currency and foreign_currency != default_currency:
- field_list_foreign.append(
- {
- "days_diff": days_diff,
- "month_filed": "{0} {1}".format(
- month_filed, foreign_currency
- ),
- "month_len": month_len,
- "foreign": True,
- }
- )
- date = get_first_day(add_months(date, 1))
-
- else:
- last_day = get_last_day(date)
- days_diff = date_diff(last_day, date) + 1
- if check_full_month(date, last_day):
- days_diff = 30
- days += days_diff
- # days_list.append(days_diff)
- if date == last_day:
- last_day = add_days(last_day, 1)
- month_filed = (get_months(str(date), str(last_day))[0]).lower()
- month_len = date_diff(get_last_day(date), get_first_day(date))
- field_list.append(
- {
- "days_diff": days_diff,
- "month_filed": "{0} {1}".format(month_filed, default_currency),
- "month_len": month_len,
- "foreign": False,
- }
- )
- if foreign_currency and foreign_currency != default_currency:
- field_list_foreign.append(
- {
- "days_diff": days_diff,
- "month_filed": "{0} {1}".format(
- month_filed, foreign_currency
- ),
- "month_len": month_len,
- "foreign": True,
- }
- )
- date = get_first_day(add_months(date, 1))
-
- if floor(days / 30) != (days / 30) and (floor(days / 30) * 30 + 6) < days:
- days = (floor(days / 30) + 1) * 30
- elif floor(days / 30) != (days / 30) and floor(days / 30) * 30 < days:
- days = floor(days / 30) * 30
-
- daily_ammount = 0 if days == 0 else ammount / (days)
- daily_ammount_foreign = 0 if days == 0 else foreign_ammount / (days)
-
- m = 1
- for i in field_list:
- if m == 1 and i["days_diff"] < 30:
- first_last += i["days_diff"]
- elif m == len(field_list) and i["days_diff"] < 30:
- first_last += i["days_diff"]
- else:
- sub_ammount += i["days_diff"] * daily_ammount
- m += 1
-
- m = 1
- for i in field_list_foreign:
- if m == 1 and i["days_diff"] < 30:
- first_last_foreign += i["days_diff"]
- elif m == len(field_list) and i["days_diff"] < 30:
- first_last_foreign += i["days_diff"]
- else:
- sub_ammount_foreign += i["days_diff"] * daily_ammount_foreign
- m += 1
-
- n = 1
- for i in field_list_foreign:
- if n == 1 and i["days_diff"] < 30:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"]
- * ((foreign_ammount - sub_ammount_foreign) / first_last),
- float_precision,
- )
- elif n == len(field_list) and i["days_diff"] < 30:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"]
- * ((foreign_ammount - sub_ammount_foreign) / first_last),
- float_precision,
- )
- else:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"] * daily_ammount_foreign, float_precision
- )
- n += 1
-
- n = 1
- for i in field_list:
- if n == 1 and i["days_diff"] < 30:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"] * ((ammount - sub_ammount) / first_last),
- float_precision,
- )
- elif n == len(field_list) and i["days_diff"] < 30:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"] * ((ammount - sub_ammount) / first_last),
- float_precision,
- )
- else:
- monthly_ammount_obj[i["month_filed"]] = flt(
- i["days_diff"] * daily_ammount, float_precision
- )
- n += 1
- advance_before = 0
- advance_after = 0
- first_month = datetime.strptime(
- months_report_list[0][:6].replace("-", " 20"), "%b %Y"
- )
- last_month = datetime.strptime(
- months_report_list[-1][:6].replace("-", " 20"), "%b %Y"
- )
- for key, value in monthly_ammount_obj.items():
- key_currency = key[7:]
- currency = foreign_currency or default_currency
- if key not in months_report_list and key_currency == currency:
- mydate = datetime.strptime(str(key)[:6].replace("-", " 20"), "%b %Y")
- if mydate > last_month:
- advance_after += value
- elif mydate < first_month:
- advance_before += value
- if advance_after:
- monthly_ammount_obj["advance_after"] = advance_after
- if advance_before:
- monthly_ammount_obj["advance_before"] = advance_before
- return monthly_ammount_obj
+ float_precision = cint(frappe.db.get_default("float_precision")) or 2
+ months_report_list = []
+ for month in get_months(filters["from_date"], filters["to_date"]):
+ months_report_list.append(f"{month.lower()} {default_currency}")
+ if filters.get("foreign_currency") and filters.get("foreign_currency") != default_currency:
+ months_report_list.append("{} {}".format(month.lower(), filters.get("foreign_currency")))
+ if ammount and from_date and to_date:
+ monthly_ammount_obj = {}
+ days = 0
+ date = from_date
+ end_date = to_date
+ field_list = []
+ field_list_foreign = []
+ first_last = 0
+ first_last_foreign = 0
+ sub_ammount = 0
+ sub_ammount_foreign = 0
+ # days_list= []
+
+ while date <= end_date:
+ start_month = getdate(date).month
+ end_month = getdate(to_date).month
+
+ if start_month == end_month:
+ last_day = end_date
+ days_diff = date_diff(last_day, date) + 1
+ if check_full_month(date, last_day):
+ days_diff = 30
+ days += days_diff
+ # days_list.append(days_diff)
+ if date == last_day:
+ last_day = add_days(last_day, 1)
+ month_filed = (get_months(str(date), str(last_day))[0]).lower()
+ month_len = date_diff(get_last_day(date), get_first_day(date))
+ field_list.append(
+ {
+ "days_diff": days_diff,
+ "month_filed": f"{month_filed} {default_currency}",
+ "month_len": month_len,
+ "foreign": False,
+ }
+ )
+ if foreign_currency and foreign_currency != default_currency:
+ field_list_foreign.append(
+ {
+ "days_diff": days_diff,
+ "month_filed": f"{month_filed} {foreign_currency}",
+ "month_len": month_len,
+ "foreign": True,
+ }
+ )
+ date = get_first_day(add_months(date, 1))
+
+ else:
+ last_day = get_last_day(date)
+ days_diff = date_diff(last_day, date) + 1
+ if check_full_month(date, last_day):
+ days_diff = 30
+ days += days_diff
+ # days_list.append(days_diff)
+ if date == last_day:
+ last_day = add_days(last_day, 1)
+ month_filed = (get_months(str(date), str(last_day))[0]).lower()
+ month_len = date_diff(get_last_day(date), get_first_day(date))
+ field_list.append(
+ {
+ "days_diff": days_diff,
+ "month_filed": f"{month_filed} {default_currency}",
+ "month_len": month_len,
+ "foreign": False,
+ }
+ )
+ if foreign_currency and foreign_currency != default_currency:
+ field_list_foreign.append(
+ {
+ "days_diff": days_diff,
+ "month_filed": f"{month_filed} {foreign_currency}",
+ "month_len": month_len,
+ "foreign": True,
+ }
+ )
+ date = get_first_day(add_months(date, 1))
+
+ if floor(days / 30) != (days / 30) and (floor(days / 30) * 30 + 6) < days:
+ days = (floor(days / 30) + 1) * 30
+ elif floor(days / 30) != (days / 30) and floor(days / 30) * 30 < days:
+ days = floor(days / 30) * 30
+
+ daily_ammount = 0 if days == 0 else ammount / (days)
+ daily_ammount_foreign = 0 if days == 0 else foreign_ammount / (days)
+
+ m = 1
+ for i in field_list:
+ if m == 1 and i["days_diff"] < 30:
+ first_last += i["days_diff"]
+ elif m == len(field_list) and i["days_diff"] < 30:
+ first_last += i["days_diff"]
+ else:
+ sub_ammount += i["days_diff"] * daily_ammount
+ m += 1
+
+ m = 1
+ for i in field_list_foreign:
+ if m == 1 and i["days_diff"] < 30:
+ first_last_foreign += i["days_diff"]
+ elif m == len(field_list) and i["days_diff"] < 30:
+ first_last_foreign += i["days_diff"]
+ else:
+ sub_ammount_foreign += i["days_diff"] * daily_ammount_foreign
+ m += 1
+
+ n = 1
+ for i in field_list_foreign:
+ if n == 1 and i["days_diff"] < 30:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * ((foreign_ammount - sub_ammount_foreign) / first_last),
+ float_precision,
+ )
+ elif n == len(field_list) and i["days_diff"] < 30:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * ((foreign_ammount - sub_ammount_foreign) / first_last),
+ float_precision,
+ )
+ else:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * daily_ammount_foreign, float_precision
+ )
+ n += 1
+
+ n = 1
+ for i in field_list:
+ if n == 1 and i["days_diff"] < 30:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * ((ammount - sub_ammount) / first_last),
+ float_precision,
+ )
+ elif n == len(field_list) and i["days_diff"] < 30:
+ monthly_ammount_obj[i["month_filed"]] = flt(
+ i["days_diff"] * ((ammount - sub_ammount) / first_last),
+ float_precision,
+ )
+ else:
+ monthly_ammount_obj[i["month_filed"]] = flt(i["days_diff"] * daily_ammount, float_precision)
+ n += 1
+ advance_before = 0
+ advance_after = 0
+ first_month = datetime.strptime(months_report_list[0][:6].replace("-", " 20"), "%b %Y")
+ last_month = datetime.strptime(months_report_list[-1][:6].replace("-", " 20"), "%b %Y")
+ for key, value in monthly_ammount_obj.items():
+ key_currency = key[7:]
+ currency = foreign_currency or default_currency
+ if key not in months_report_list and key_currency == currency:
+ mydate = datetime.strptime(str(key)[:6].replace("-", " 20"), "%b %Y")
+ if mydate > last_month:
+ advance_after += value
+ elif mydate < first_month:
+ advance_before += value
+ if advance_after:
+ monthly_ammount_obj["advance_after"] = advance_after
+ if advance_before:
+ monthly_ammount_obj["advance_before"] = advance_before
+ return monthly_ammount_obj
diff --git a/propms/property_management_solution/report/security_attendance_report/security_attendance_report.html b/propms/property_management_solution/report/security_attendance_report/security_attendance_report.html
index b5eb3622..76cbf855 100755
--- a/propms/property_management_solution/report/security_attendance_report/security_attendance_report.html
+++ b/propms/property_management_solution/report/security_attendance_report/security_attendance_report.html
@@ -45,4 +45,4 @@
-
\ No newline at end of file
+
diff --git a/propms/property_management_solution/report/security_deposit/security_deposit.js b/propms/property_management_solution/report/security_deposit/security_deposit.js
index a2c3f683..c4f18372 100644
--- a/propms/property_management_solution/report/security_deposit/security_deposit.js
+++ b/propms/property_management_solution/report/security_deposit/security_deposit.js
@@ -10,7 +10,7 @@ frappe.query_reports["Security Deposit"] = {
"label": __("Account"),
"fieldtype": "Link",
"options": "Account",
- "default": "21401 - Security Deposit Commercial - VPL",
+ "default": "21401 - Security Deposit Commercial - VPL",
"get_query": function() {
return {
"query": "erpnext.controllers.queries.get_account_list",
@@ -22,4 +22,4 @@ frappe.query_reports["Security Deposit"] = {
}
},
]
-}
\ No newline at end of file
+}
diff --git a/propms/property_management_solution/report/subscription_service_report/subscription_service_report.js b/propms/property_management_solution/report/subscription_service_report/subscription_service_report.js
index 76e00b25..fe7e5e6b 100644
--- a/propms/property_management_solution/report/subscription_service_report/subscription_service_report.js
+++ b/propms/property_management_solution/report/subscription_service_report/subscription_service_report.js
@@ -18,7 +18,7 @@ frappe.query_reports["Subscription Service Report"] = {
}
}
}
- },
+ },
{
"fieldname":"to_date",
"label": __("To Date"),
diff --git a/propms/property_management_solution/report/utility_invoices/other_methods.py b/propms/property_management_solution/report/utility_invoices/other_methods.py
index b3edd4f5..8b3fd06e 100644
--- a/propms/property_management_solution/report/utility_invoices/other_methods.py
+++ b/propms/property_management_solution/report/utility_invoices/other_methods.py
@@ -1,205 +1,186 @@
-import frappe, calendar
-from frappe import _
+import calendar
from datetime import date, timedelta
+import frappe
+from frappe import _
+
def get_residential_columns(year):
- columns = [
- {
- "fieldname": "apartment_no",
- "label": _("Apartment No."),
- "fieldtype": "Data",
- "width": 150,
- },
- {
- "fieldname": "client",
- "label": _("Client"),
- "fieldtype": "Data",
- "width": 150,
- },
- {
- "fieldname": "advance_prev_year",
- "label": _("Advance RECD in 2019"),
- "fieldtype": "Data",
- "width": 150,
- },
- {
- "fieldname": "invoice_no",
- "label": _("Invoice No."),
- "fieldtype": "Link",
- "options": "Sales Invoice",
- "width": 150,
- },
- {"fieldname": "from", "label": _("From"), "fieldtype": "Data", "width": 150},
- {"fieldname": "to", "label": _("To"), "fieldtype": "Data", "width": 150},
- {
- "fieldname": "invoice_amount",
- "label": _("Invoice Amount"),
- "fieldtype": "Data",
- "width": 150,
- },
- ]
- months = months_array()
- for i in months:
- columns.append(
- {
- "fieldname": i.lower(),
- "label": i + " " + str(year),
- "fieldtype": "Data",
- "width": 150,
- }
- )
-
- return columns
+ columns = [
+ {
+ "fieldname": "apartment_no",
+ "label": _("Apartment No."),
+ "fieldtype": "Data",
+ "width": 150,
+ },
+ {
+ "fieldname": "client",
+ "label": _("Client"),
+ "fieldtype": "Data",
+ "width": 150,
+ },
+ {
+ "fieldname": "advance_prev_year",
+ "label": _("Advance RECD in 2019"),
+ "fieldtype": "Data",
+ "width": 150,
+ },
+ {
+ "fieldname": "invoice_no",
+ "label": _("Invoice No."),
+ "fieldtype": "Link",
+ "options": "Sales Invoice",
+ "width": 150,
+ },
+ {"fieldname": "from", "label": _("From"), "fieldtype": "Data", "width": 150},
+ {"fieldname": "to", "label": _("To"), "fieldtype": "Data", "width": 150},
+ {
+ "fieldname": "invoice_amount",
+ "label": _("Invoice Amount"),
+ "fieldtype": "Data",
+ "width": 150,
+ },
+ ]
+ months = months_array()
+ for i in months:
+ columns.append(
+ {
+ "fieldname": i.lower(),
+ "label": i + " " + str(year),
+ "fieldtype": "Data",
+ "width": 150,
+ }
+ )
+
+ return columns
def get_utility_sales_invoice(data, from_other=None, months=None):
- total = {}
- lease_item = "'Utility Charges' "
- query = """ SELECT * FROM `tabSales Invoice` AS SI WHERE EXISTS (SELECT * FROM `tabSales Invoice Item` AS SIT WHERE SIT.item_code = {0} and SIT.parent = SI.name )
+ total = {}
+ lease_item = "'Utility Charges' "
+ query = f""" SELECT * FROM `tabSales Invoice` AS SI WHERE EXISTS (SELECT * FROM `tabSales Invoice Item` AS SIT WHERE SIT.item_code = {lease_item} and SIT.parent = SI.name )
and SI.docstatus=%s
- ORDER by SI.customer,SI.from_date ASC""".format(
- lease_item
- ) % (
- 1
- )
-
- sales_invoices = frappe.db.sql(query, as_dict=True)
- previuos_customer = ""
- for i in sales_invoices:
- lease = frappe.get_value("Lease", i.lease, "property")
- obj = {
- "apartment_no": lease,
- "client": i.customer,
- "advance_prev_year": "",
- "invoice_no": i.name,
- "from": i.from_date if i.from_date else i.posting_date,
- "to": i.to_date - timedelta(days=1) if i.to_date else i.posting_date,
- "invoice_amount": i.total,
- }
- set_monthly_amount(
- i.from_date,
- i.to_date - timedelta(days=1) if i.to_date else "",
- obj,
- total,
- months,
- )
- if previuos_customer != i.customer:
- data.append({})
- previuos_customer = i.customer
- data.append(obj)
- if from_other:
- data.append(total)
+ ORDER by SI.customer,SI.from_date ASC""" % (1)
+
+ sales_invoices = frappe.db.sql(query, as_dict=True)
+ previuos_customer = ""
+ for i in sales_invoices:
+ lease = frappe.get_value("Lease", i.lease, "property")
+ obj = {
+ "apartment_no": lease,
+ "client": i.customer,
+ "advance_prev_year": "",
+ "invoice_no": i.name,
+ "from": i.from_date if i.from_date else i.posting_date,
+ "to": i.to_date - timedelta(days=1) if i.to_date else i.posting_date,
+ "invoice_amount": i.total,
+ }
+ set_monthly_amount(
+ i.from_date,
+ i.to_date - timedelta(days=1) if i.to_date else "",
+ obj,
+ total,
+ months,
+ )
+ if previuos_customer != i.customer:
+ data.append({})
+ previuos_customer = i.customer
+ data.append(obj)
+ if from_other:
+ data.append(total)
def set_monthly_amount(start_date, end_date, obj, total, months):
- rate = get_rate(obj["invoice_no"])
- if end_date and rate:
- check_dates(start_date, end_date, rate, obj, total, months)
+ rate = get_rate(obj["invoice_no"])
+ if end_date and rate:
+ check_dates(start_date, end_date, rate, obj, total, months)
def check_dates(start_date, end_date, rate, obj, total, months):
- start = start_date
- no_minus = 0
- while start < end_date:
- month_string = start.strftime("%b")
- month_no_of_days = calendar.monthrange(start.year, start.month)[1]
- last_date = date(start.year, start.month, month_no_of_days)
- if (last_date - start).days >= 29 or (
- month_string == "Feb" and (last_date - start).days >= 27
- ):
- if start.year == start_date.year:
- obj[month_string.lower()] = round(rate, 2)
- if months and month_string.lower() in months:
- total[month_string.lower()] = (
- round(rate, 2) + round(total[month_string.lower()], 2)
- if month_string.lower() in total
- else round(rate, 2)
- )
- else:
- if start.year == start_date.year:
- obj[month_string.lower()] = round(
- round(rate / month_no_of_days, 2)
- * (month_no_of_days - int(start.day)),
- 2,
- )
- if months and month_string.lower() in months:
- total[month_string.lower()] = (
- round(
- round(rate / month_no_of_days, 2)
- * (month_no_of_days - int(start.day)),
- 2,
- )
- + round(total[month_string.lower()], 2)
- if month_string.lower() in total
- else round(
- round(rate / month_no_of_days, 2)
- * (month_no_of_days - int(start.day)),
- 2,
- )
- )
- no_minus = month_no_of_days
- start += timedelta(days=month_no_of_days)
-
- start_last = start - timedelta(days=no_minus)
-
- if (end_date - start_last).days > 0:
- if start_last.year == start_date.year:
- month_string = end_date.strftime("%b")
- month_no_of_days = calendar.monthrange(end_date.year, end_date.month)[1]
- if int(end_date.day) >= 29 or (
- month_string == "Feb" and (end_date - start_last).days >= 27
- ):
- obj[month_string.lower()] = round(rate, 2)
- if months and month_string.lower() in months:
- total[month_string.lower()] = (
- round(rate, 2) + round(total[month_string.lower()], 2)
- if month_string.lower() in total
- else round(rate, 2)
- )
- else:
- obj[month_string.lower()] = round(
- round(rate / month_no_of_days, 2) * (int(end_date.day)), 2
- )
- if months and month_string.lower() in months:
- total[month_string.lower()] = (
- round(
- round(rate / month_no_of_days, 2) * (int(end_date.day)), 2
- )
- + round(total[month_string.lower()], 2)
- if month_string.lower() in total
- else round(
- round(rate / month_no_of_days, 2) * (int(end_date.day)), 2
- )
- )
+ start = start_date
+ no_minus = 0
+ while start < end_date:
+ month_string = start.strftime("%b")
+ month_no_of_days = calendar.monthrange(start.year, start.month)[1]
+ last_date = date(start.year, start.month, month_no_of_days)
+ if (last_date - start).days >= 29 or (month_string == "Feb" and (last_date - start).days >= 27):
+ if start.year == start_date.year:
+ obj[month_string.lower()] = round(rate, 2)
+ if months and month_string.lower() in months:
+ total[month_string.lower()] = (
+ round(rate, 2) + round(total[month_string.lower()], 2)
+ if month_string.lower() in total
+ else round(rate, 2)
+ )
+ else:
+ if start.year == start_date.year:
+ obj[month_string.lower()] = round(
+ round(rate / month_no_of_days, 2) * (month_no_of_days - int(start.day)),
+ 2,
+ )
+ if months and month_string.lower() in months:
+ total[month_string.lower()] = (
+ round(
+ round(rate / month_no_of_days, 2) * (month_no_of_days - int(start.day)),
+ 2,
+ )
+ + round(total[month_string.lower()], 2)
+ if month_string.lower() in total
+ else round(
+ round(rate / month_no_of_days, 2) * (month_no_of_days - int(start.day)),
+ 2,
+ )
+ )
+ no_minus = month_no_of_days
+ start += timedelta(days=month_no_of_days)
+
+ start_last = start - timedelta(days=no_minus)
+
+ if (end_date - start_last).days > 0:
+ if start_last.year == start_date.year:
+ month_string = end_date.strftime("%b")
+ month_no_of_days = calendar.monthrange(end_date.year, end_date.month)[1]
+ if int(end_date.day) >= 29 or (month_string == "Feb" and (end_date - start_last).days >= 27):
+ obj[month_string.lower()] = round(rate, 2)
+ if months and month_string.lower() in months:
+ total[month_string.lower()] = (
+ round(rate, 2) + round(total[month_string.lower()], 2)
+ if month_string.lower() in total
+ else round(rate, 2)
+ )
+ else:
+ obj[month_string.lower()] = round(round(rate / month_no_of_days, 2) * (int(end_date.day)), 2)
+ if months and month_string.lower() in months:
+ total[month_string.lower()] = (
+ round(round(rate / month_no_of_days, 2) * (int(end_date.day)), 2)
+ + round(total[month_string.lower()], 2)
+ if month_string.lower() in total
+ else round(round(rate / month_no_of_days, 2) * (int(end_date.day)), 2)
+ )
def months_array():
- return [
- "Jan",
- "Feb",
- "Mar",
- "Apr",
- "May",
- "Jun",
- "Jul",
- "Aug",
- "Sep",
- "Oct",
- "Nov",
- "Dec",
- ]
+ return [
+ "Jan",
+ "Feb",
+ "Mar",
+ "Apr",
+ "May",
+ "Jun",
+ "Jul",
+ "Aug",
+ "Sep",
+ "Oct",
+ "Nov",
+ "Dec",
+ ]
def get_rate(invoice_name):
- filters_value = "and item_code = 'Utility Charges'"
- query = """ SELECT rate FROM `tabSales Invoice Item` WHERE {0} {1}""".format(
- "parent = '" + invoice_name + "' ", filters_value
- )
- print(query)
-
- return (
- frappe.db.sql(query, as_dict=True)[0].rate
- if len(frappe.db.sql(query, as_dict=True)) > 0
- else ""
- )
+ filters_value = "and item_code = 'Utility Charges'"
+ query = """ SELECT rate FROM `tabSales Invoice Item` WHERE {} {}""".format(
+ "parent = '" + invoice_name + "' ", filters_value
+ )
+ print(query)
+
+ return frappe.db.sql(query, as_dict=True)[0].rate if len(frappe.db.sql(query, as_dict=True)) > 0 else ""
diff --git a/propms/property_management_solution/report/utility_invoices/utility_invoices.py b/propms/property_management_solution/report/utility_invoices/utility_invoices.py
index 141dff09..0f90f704 100644
--- a/propms/property_management_solution/report/utility_invoices/utility_invoices.py
+++ b/propms/property_management_solution/report/utility_invoices/utility_invoices.py
@@ -1,15 +1,14 @@
# Copyright (c) 2013, Aakvatech and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-from .other_methods import get_residential_columns
-from .other_methods import get_utility_sales_invoice
+
+from .other_methods import get_residential_columns, get_utility_sales_invoice
def execute(filters=None):
- columns, data = [], []
- if filters.get("year"):
- columns = get_residential_columns(filters.get("year"))
- get_utility_sales_invoice(data)
+ columns, data = [], []
+ if filters.get("year"):
+ columns = get_residential_columns(filters.get("year"))
+ get_utility_sales_invoice(data)
- return columns, data
+ return columns, data
diff --git a/propms/property_management_solution/report/withholding_tax_summary_on_sales_(properties)/withholding_tax_summary_on_sales_(properties).js b/propms/property_management_solution/report/withholding_tax_summary_on_sales_(properties)/withholding_tax_summary_on_sales_(properties).js
index 550d84d7..557a30d8 100644
--- a/propms/property_management_solution/report/withholding_tax_summary_on_sales_(properties)/withholding_tax_summary_on_sales_(properties).js
+++ b/propms/property_management_solution/report/withholding_tax_summary_on_sales_(properties)/withholding_tax_summary_on_sales_(properties).js
@@ -17,4 +17,4 @@ frappe.query_reports["Withholding Tax Summary on Sales (Properties)"] = {
"default": frappe.datetime.get_today(),
},
]
-};
\ No newline at end of file
+};
diff --git a/propms/property_management_solution/sales_invoice.js b/propms/property_management_solution/sales_invoice.js
index 911d804d..6735c05d 100644
--- a/propms/property_management_solution/sales_invoice.js
+++ b/propms/property_management_solution/sales_invoice.js
@@ -37,4 +37,4 @@ frappe.ui.form.on('Sales Invoice', {
frappe.model.set_value(cdt, cdn, "customer", "");
}
}
-})
\ No newline at end of file
+})
diff --git a/propms/propms-gitlab.sh b/propms/propms-gitlab.sh
index fcfe2b22..20b396ca 100755
--- a/propms/propms-gitlab.sh
+++ b/propms/propms-gitlab.sh
@@ -13,4 +13,3 @@ cd apps/propms
git add .
git commit -m "$1"
git push upstream master
-
diff --git a/propms/utils/create_custom_fields.py b/propms/utils/create_custom_fields.py
index dcbe1a01..16ac3ac4 100644
--- a/propms/utils/create_custom_fields.py
+++ b/propms/utils/create_custom_fields.py
@@ -8,71 +8,65 @@
def load_json(file):
- CURR_DIR = os.path.abspath(os.path.dirname(__file__))
- json_file_path = os.path.join(CURR_DIR, folder, file)
- # TODO do not load the file if already applied
- with open(json_file_path, "r") as file:
- data = json.load(file)
- return data
+ CURR_DIR = os.path.abspath(os.path.dirname(__file__))
+ json_file_path = os.path.join(CURR_DIR, folder, file)
+ # TODO do not load the file if already applied
+ with open(json_file_path) as file:
+ data = json.load(file)
+ return data
def create_fields_from_json(custom_fields_obj):
- disallowed_fields = [
- "name",
- "owner",
- "creation",
- "modified",
- "modified_by",
- "docstatus",
- "idx",
- "is_system_generated",
- "__last_sync_on",
- ]
- doctype_custom_fields_dict = {}
-
- for custom_field in custom_fields_obj:
- doctype = custom_field["dt"]
- all_fields = frappe.get_meta("Custom Field").get_valid_columns()
- field_list = set(all_fields).difference(disallowed_fields)
- custom_field_dict = {}
- for field_name in field_list:
- custom_field_dict[field_name] = custom_field.get(field_name)
-
- # Ensure the list for the doctype is initialized
- if doctype not in doctype_custom_fields_dict:
- doctype_custom_fields_dict[doctype] = []
-
- doctype_custom_fields_dict[doctype].append(custom_field_dict)
-
- create_custom_fields(doctype_custom_fields_dict, update=False)
+ disallowed_fields = [
+ "name",
+ "owner",
+ "creation",
+ "modified",
+ "modified_by",
+ "docstatus",
+ "idx",
+ "is_system_generated",
+ "__last_sync_on",
+ ]
+ doctype_custom_fields_dict = {}
+
+ for custom_field in custom_fields_obj:
+ doctype = custom_field["dt"]
+ all_fields = frappe.get_meta("Custom Field").get_valid_columns()
+ field_list = set(all_fields).difference(disallowed_fields)
+ custom_field_dict = {}
+ for field_name in field_list:
+ custom_field_dict[field_name] = custom_field.get(field_name)
+
+ # Ensure the list for the doctype is initialized
+ if doctype not in doctype_custom_fields_dict:
+ doctype_custom_fields_dict[doctype] = []
+
+ doctype_custom_fields_dict[doctype].append(custom_field_dict)
+
+ create_custom_fields(doctype_custom_fields_dict, update=False)
def execute():
- # read names of only json files in this folder and put it into files list
- files = list(
- filter(
- lambda x: x.endswith(".json"),
- os.listdir(
- os.path.join(os.path.abspath(os.path.dirname(__file__)), folder)
- ),
- )
- )
- for file in files:
- data = load_json(file)
- create_fields_from_json(data)
+ # read names of only json files in this folder and put it into files list
+ files = list(
+ filter(
+ lambda x: x.endswith(".json"),
+ os.listdir(os.path.join(os.path.abspath(os.path.dirname(__file__)), folder)),
+ )
+ )
+ for file in files:
+ data = load_json(file)
+ create_fields_from_json(data)
@frappe.whitelist()
def export_custom_fields(docnames):
- docnames = frappe.parse_json(docnames)
- custom_fields = []
-
- for docname in docnames:
- doc = frappe.get_doc("Custom Field", docname)
- custom_fields.append(
- doc.as_dict(
- convert_dates_to_str=True, no_default_fields=True, no_nulls=True
- )
- )
-
- return str(custom_fields)
\ No newline at end of file
+ docnames = frappe.parse_json(docnames)
+ custom_fields = []
+
+ for docname in docnames:
+ doc = frappe.get_doc("Custom Field", docname)
+ custom_fields.append(doc.as_dict(convert_dates_to_str=True, no_default_fields=True, no_nulls=True))
+
+ return str(custom_fields)
diff --git a/propms/utils/create_property_setter.py b/propms/utils/create_property_setter.py
index 6110fa57..fc214e49 100644
--- a/propms/utils/create_property_setter.py
+++ b/propms/utils/create_property_setter.py
@@ -8,88 +8,94 @@
def load_json(file):
- CURR_DIR = os.path.abspath(os.path.dirname(__file__))
- json_file_path = os.path.join(CURR_DIR, folder, file)
- with open(json_file_path, "r") as file:
- data = json.load(file)
- return data
+ CURR_DIR = os.path.abspath(os.path.dirname(__file__))
+ json_file_path = os.path.join(CURR_DIR, folder, file)
+ with open(json_file_path) as file:
+ data = json.load(file)
+ return data
def create_property_setter_from_json(property_setters_obj):
- disallowed_fields = [
- "name",
- "owner",
- "creation",
- "modified",
- "modified_by",
- "docstatus",
- "idx",
- "is_system_generated",
- "__last_sync_on",
- ]
-
- # Fetching existing setters using composite key (DocType, Field, Property)
- existing_data = frappe.db.get_all("Property Setter",
- fields=["name", "doc_type", "field_name", "property", "value", "property_type"],
- page_length=20000
- )
-
- # Create a mapping: {(doc_type, field_name, property): record}
- existing_map = {}
- for d in existing_data:
- key = (d.doc_type, d.field_name or "", d.property)
- existing_map[key] = d
-
- for property_setter in property_setters_obj:
- doc_type = property_setter.get('doc_type')
- field_name = property_setter.get('field_name')
- prop = property_setter.get('property')
- key = (doc_type, field_name or "", prop)
-
- name_in_json = property_setter.get('name')
-
- if key in existing_map:
- existing = existing_map[key]
-
- # Normalizing values for comparison
- old_val = str(existing.get('value') if existing.get('value') is not None else "")
- new_val = str(property_setter.get('value') if property_setter.get('value') is not None else "")
-
- old_prop_type = str(existing.get('property_type') if existing.get('property_type') is not None else "")
- new_prop_type = str(property_setter.get('property_type') if property_setter.get('property_type') is not None else "")
-
- if old_val.strip() == new_val.strip() and old_prop_type.strip() == new_prop_type.strip():
- continue
-
- if property_setter.get('doctype_or_field') == "DocType":
- for_doctype = True
- else:
- for_doctype = False
-
- all_fields = frappe.get_meta("Property Setter").get_valid_columns()
- field_list = set(all_fields).difference(disallowed_fields)
-
- property_setter_dict = {field: property_setter.get(field) for field in field_list if field in property_setter}
-
- make_property_setter(
- doctype=property_setter_dict['doc_type'],
- fieldname=property_setter_dict.get('field_name', None),
- property=property_setter_dict['property'],
- value=property_setter_dict['value'],
- property_type=property_setter_dict['property_type'],
- for_doctype=for_doctype
- )
+ disallowed_fields = [
+ "name",
+ "owner",
+ "creation",
+ "modified",
+ "modified_by",
+ "docstatus",
+ "idx",
+ "is_system_generated",
+ "__last_sync_on",
+ ]
+
+ # Fetching existing setters using composite key (DocType, Field, Property)
+ existing_data = frappe.db.get_all(
+ "Property Setter",
+ fields=["name", "doc_type", "field_name", "property", "value", "property_type"],
+ page_length=20000,
+ )
+
+ # Create a mapping: {(doc_type, field_name, property): record}
+ existing_map = {}
+ for d in existing_data:
+ key = (d.doc_type, d.field_name or "", d.property)
+ existing_map[key] = d
+
+ for property_setter in property_setters_obj:
+ doc_type = property_setter.get("doc_type")
+ field_name = property_setter.get("field_name")
+ prop = property_setter.get("property")
+ key = (doc_type, field_name or "", prop)
+
+ if key in existing_map:
+ existing = existing_map[key]
+
+ # Normalizing values for comparison
+ old_val = str(existing.get("value") if existing.get("value") is not None else "")
+ new_val = str(property_setter.get("value") if property_setter.get("value") is not None else "")
+
+ old_prop_type = str(
+ existing.get("property_type") if existing.get("property_type") is not None else ""
+ )
+ new_prop_type = str(
+ property_setter.get("property_type")
+ if property_setter.get("property_type") is not None
+ else ""
+ )
+
+ if old_val.strip() == new_val.strip() and old_prop_type.strip() == new_prop_type.strip():
+ continue
+
+ if property_setter.get("doctype_or_field") == "DocType":
+ for_doctype = True
+ else:
+ for_doctype = False
+
+ all_fields = frappe.get_meta("Property Setter").get_valid_columns()
+ field_list = set(all_fields).difference(disallowed_fields)
+
+ property_setter_dict = {
+ field: property_setter.get(field) for field in field_list if field in property_setter
+ }
+
+ make_property_setter(
+ doctype=property_setter_dict["doc_type"],
+ fieldname=property_setter_dict.get("field_name", None),
+ property=property_setter_dict["property"],
+ value=property_setter_dict["value"],
+ property_type=property_setter_dict["property_type"],
+ for_doctype=for_doctype,
+ )
+
def execute():
- # read names of only json files in this folder and put it into files list
- files = list(
- filter(
- lambda x: x.endswith(".json"),
- os.listdir(
- os.path.join(os.path.abspath(os.path.dirname(__file__)), folder)
- ),
- )
- )
- for file in files:
- data = load_json(file)
- create_property_setter_from_json(data)
\ No newline at end of file
+ # read names of only json files in this folder and put it into files list
+ files = list(
+ filter(
+ lambda x: x.endswith(".json"),
+ os.listdir(os.path.join(os.path.abspath(os.path.dirname(__file__)), folder)),
+ )
+ )
+ for file in files:
+ data = load_json(file)
+ create_property_setter_from_json(data)
diff --git a/pyproject.toml b/pyproject.toml
index 041d7671..64ee0d3d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,32 +9,32 @@ readme = "README.md"
dynamic = ["version"]
dependencies = []
+[project.optional-dependencies]
+dev = [
+ "pre-commit",
+]
+
[build-system]
requires = ["flit_core >=3.4,<4"]
build-backend = "flit_core.buildapi"
[tool.bench.frappe-dependencies]
-frappe = ">=15.0.0,<16.0.0"
-erpnext = ">=15.0.0,<16.0.0"
+frappe = ">=15.0.0,<17.0.0"
+erpnext = ">=15.0.0,<17.0.0"
[tool.ruff]
-line-length = 120
+line-length = 110
target-version = "py310"
+[tool.ruff.format]
+quote-style = "double"
+indent-style = "tab"
+docstring-code-format = true
+
[tool.ruff.lint]
-select = [
- "F",
- "E",
- "W",
- "I",
- "UP",
- "B",
-]
-ignore = [
- "E501",
- "E402",
- "E741",
-]
+select = ["F", "E", "W", "I", "UP", "B"]
+ignore = ["E101", "E402", "E501", "E741", "W191"]
-[tool.setuptools.dynamic]
-version = {attr = "propms.__version__"}
+# These dependencies are only installed when developer mode is enabled
+[tool.bench.dev-dependencies]
+# package_name = "~=1.1.0"
diff --git a/scripts/setup-git-hooks.sh b/scripts/setup-git-hooks.sh
new file mode 100755
index 00000000..58aabf1f
--- /dev/null
+++ b/scripts/setup-git-hooks.sh
@@ -0,0 +1,24 @@
+#!/usr/bin/env bash
+# Installs pre-commit and wires up the commit and push hooks for this clone.
+set -euo pipefail
+
+cd "$(git rev-parse --show-toplevel)"
+
+if ! command -v pre-commit >/dev/null 2>&1; then
+ echo "pre-commit not found, installing..."
+ if command -v uv >/dev/null 2>&1; then
+ uv tool install pre-commit
+ elif command -v pipx >/dev/null 2>&1; then
+ pipx install pre-commit
+ else
+ python3 -m pip install --user pre-commit
+ fi
+fi
+
+pre-commit install --install-hooks --overwrite
+
+echo
+echo "Hooks installed:"
+echo " pre-commit staged files only, fast"
+echo " commit-msg conventional commit message check"
+echo " pre-push pre-commit run --all-files, blocks the push on any failure"