diff --git a/package-lock.json b/package-lock.json
index 2e9a267ce..7e3f97cb9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,15 +10,19 @@
"daisyui": "^4.7.3",
"htmx.org": "^1.9.12",
"hyperscript.org": "^0.9.12",
- "jquery": "^3.7.1",
- "tailwindcss": "^3.4.1"
+ "jquery": "^3.7.1"
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^6.5.1",
+ "autoprefixer": "^10.4.20",
+ "css-loader": "^7.1.2",
"daisyui": "^4.9.0",
+ "mini-css-extract-plugin": "^2.9.2",
"npm-check-updates": "16.14.17",
- "tailwindcss": "^3.4.1",
- "webpack": "^5.94.0",
+ "postcss": "^8.4.49",
+ "postcss-loader": "^8.1.1",
+ "tailwindcss": "^3.4.17",
+ "webpack": "^5.97.1",
"webpack-bundle-tracker": "^3.1.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.3",
@@ -37,6 +41,29 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/@babel/code-frame": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
"node_modules/@colors/colors": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
@@ -221,39 +248,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/@npmcli/fs/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@npmcli/fs/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@npmcli/fs/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/@npmcli/git": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz",
@@ -282,33 +276,6 @@
"node": ">=12"
}
},
- "node_modules/@npmcli/git/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@npmcli/git/node_modules/semver/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/@npmcli/git/node_modules/which": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz",
@@ -324,12 +291,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/@npmcli/git/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/@npmcli/installed-package-contents": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz",
@@ -642,10 +603,30 @@
"@types/node": "*"
}
},
+ "node_modules/@types/eslint": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.7",
+ "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz",
+ "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
"node_modules/@types/estree": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
- "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true
},
"node_modules/@types/express": {
@@ -803,148 +784,148 @@
"integrity": "sha512-oJ4F3TnvpXaQwZJNF3ZK+kLPHKarDmJjJ6jyzVNDKH9md1dptjC7lWR//jrGuLdek/U6iltWxqAnYOu8gCiOvA=="
},
"node_modules/@webassemblyjs/ast": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz",
- "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz",
+ "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==",
"dev": true,
"dependencies": {
- "@webassemblyjs/helper-numbers": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6"
+ "@webassemblyjs/helper-numbers": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2"
}
},
"node_modules/@webassemblyjs/floating-point-hex-parser": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz",
- "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz",
+ "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==",
"dev": true
},
"node_modules/@webassemblyjs/helper-api-error": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz",
- "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz",
+ "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==",
"dev": true
},
"node_modules/@webassemblyjs/helper-buffer": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz",
- "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz",
+ "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==",
"dev": true
},
"node_modules/@webassemblyjs/helper-numbers": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz",
- "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz",
+ "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==",
"dev": true,
"dependencies": {
- "@webassemblyjs/floating-point-hex-parser": "1.11.6",
- "@webassemblyjs/helper-api-error": "1.11.6",
+ "@webassemblyjs/floating-point-hex-parser": "1.13.2",
+ "@webassemblyjs/helper-api-error": "1.13.2",
"@xtuc/long": "4.2.2"
}
},
"node_modules/@webassemblyjs/helper-wasm-bytecode": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz",
- "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz",
+ "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==",
"dev": true
},
"node_modules/@webassemblyjs/helper-wasm-section": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz",
- "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz",
+ "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==",
"dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.12.1",
- "@webassemblyjs/helper-buffer": "1.12.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/wasm-gen": "1.12.1"
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/wasm-gen": "1.14.1"
}
},
"node_modules/@webassemblyjs/ieee754": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz",
- "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz",
+ "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==",
"dev": true,
"dependencies": {
"@xtuc/ieee754": "^1.2.0"
}
},
"node_modules/@webassemblyjs/leb128": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz",
- "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz",
+ "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==",
"dev": true,
"dependencies": {
"@xtuc/long": "4.2.2"
}
},
"node_modules/@webassemblyjs/utf8": {
- "version": "1.11.6",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz",
- "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz",
+ "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==",
"dev": true
},
"node_modules/@webassemblyjs/wasm-edit": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz",
- "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz",
+ "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==",
"dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.12.1",
- "@webassemblyjs/helper-buffer": "1.12.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/helper-wasm-section": "1.12.1",
- "@webassemblyjs/wasm-gen": "1.12.1",
- "@webassemblyjs/wasm-opt": "1.12.1",
- "@webassemblyjs/wasm-parser": "1.12.1",
- "@webassemblyjs/wast-printer": "1.12.1"
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/helper-wasm-section": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-opt": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1",
+ "@webassemblyjs/wast-printer": "1.14.1"
}
},
"node_modules/@webassemblyjs/wasm-gen": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz",
- "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz",
+ "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==",
"dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.12.1",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/ieee754": "1.11.6",
- "@webassemblyjs/leb128": "1.11.6",
- "@webassemblyjs/utf8": "1.11.6"
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
}
},
"node_modules/@webassemblyjs/wasm-opt": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz",
- "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz",
+ "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==",
"dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.12.1",
- "@webassemblyjs/helper-buffer": "1.12.1",
- "@webassemblyjs/wasm-gen": "1.12.1",
- "@webassemblyjs/wasm-parser": "1.12.1"
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-buffer": "1.14.1",
+ "@webassemblyjs/wasm-gen": "1.14.1",
+ "@webassemblyjs/wasm-parser": "1.14.1"
}
},
"node_modules/@webassemblyjs/wasm-parser": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz",
- "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz",
+ "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==",
"dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.12.1",
- "@webassemblyjs/helper-api-error": "1.11.6",
- "@webassemblyjs/helper-wasm-bytecode": "1.11.6",
- "@webassemblyjs/ieee754": "1.11.6",
- "@webassemblyjs/leb128": "1.11.6",
- "@webassemblyjs/utf8": "1.11.6"
+ "@webassemblyjs/ast": "1.14.1",
+ "@webassemblyjs/helper-api-error": "1.13.2",
+ "@webassemblyjs/helper-wasm-bytecode": "1.13.2",
+ "@webassemblyjs/ieee754": "1.13.2",
+ "@webassemblyjs/leb128": "1.13.2",
+ "@webassemblyjs/utf8": "1.13.2"
}
},
"node_modules/@webassemblyjs/wast-printer": {
- "version": "1.12.1",
- "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz",
- "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz",
+ "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==",
"dev": true,
"dependencies": {
- "@webassemblyjs/ast": "1.12.1",
+ "@webassemblyjs/ast": "1.14.1",
"@xtuc/long": "4.2.2"
}
},
@@ -1024,9 +1005,9 @@
}
},
"node_modules/acorn": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
- "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
"bin": {
"acorn": "bin/acorn"
},
@@ -1034,15 +1015,6 @@
"node": ">=0.4.0"
}
},
- "node_modules/acorn-import-attributes": {
- "version": "1.9.5",
- "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
- "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
- "dev": true,
- "peerDependencies": {
- "acorn": "^8"
- }
- },
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
@@ -1302,6 +1274,43 @@
"node": ">=8"
}
},
+ "node_modules/autoprefixer": {
+ "version": "10.4.20",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
+ "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "browserslist": "^4.23.3",
+ "caniuse-lite": "^1.0.30001646",
+ "fraction.js": "^4.3.7",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1401,9 +1410,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.22.3",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.3.tgz",
- "integrity": "sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==",
+ "version": "4.24.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz",
+ "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==",
"dev": true,
"funding": [
{
@@ -1420,10 +1429,10 @@
}
],
"dependencies": {
- "caniuse-lite": "^1.0.30001580",
- "electron-to-chromium": "^1.4.648",
- "node-releases": "^2.0.14",
- "update-browserslist-db": "^1.0.13"
+ "caniuse-lite": "^1.0.30001688",
+ "electron-to-chromium": "^1.5.73",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.1"
},
"bin": {
"browserslist": "cli.js"
@@ -1446,39 +1455,6 @@
"semver": "^7.0.0"
}
},
- "node_modules/builtins/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/builtins/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/builtins/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/bundle-name": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
@@ -1563,6 +1539,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/camelcase-css": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
@@ -1573,9 +1558,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001636",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001636.tgz",
- "integrity": "sha512-bMg2vmr8XBsbL6Lr0UHXy/21m84FTxDLWn2FSqMd5PrlbMxwJlQnC2YWYxVgp66PZE+BBNF2jYQUBKCo1FDeZg==",
+ "version": "1.0.30001690",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001690.tgz",
+ "integrity": "sha512-5ExiE3qQN6oF8Clf8ifIDcMRCRE/dMGcETG/XGMD8/XiXm6HXQgQTh1yZYLXXpSOsEUlJm1Xr7kGULZTuGtP/w==",
"dev": true,
"funding": [
{
@@ -1824,6 +1809,32 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"dev": true
},
+ "node_modules/cosmiconfig": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+ "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+ "dev": true,
+ "dependencies": {
+ "env-paths": "^2.2.1",
+ "import-fresh": "^3.3.0",
+ "js-yaml": "^4.1.0",
+ "parse-json": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/d-fischer"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.9.5"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -1838,6 +1849,41 @@
"node": ">= 8"
}
},
+ "node_modules/css-loader": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz",
+ "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.1.0",
+ "postcss": "^8.4.33",
+ "postcss-modules-extract-imports": "^3.1.0",
+ "postcss-modules-local-by-default": "^4.0.5",
+ "postcss-modules-scope": "^3.2.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">= 18.12.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "@rspack/core": "0.x || 1.x",
+ "webpack": "^5.27.0"
+ },
+ "peerDependenciesMeta": {
+ "@rspack/core": {
+ "optional": true
+ },
+ "webpack": {
+ "optional": true
+ }
+ }
+ },
"node_modules/css-selector-tokenizer": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
@@ -2055,9 +2101,9 @@
"dev": true
},
"node_modules/electron-to-chromium": {
- "version": "1.4.661",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.661.tgz",
- "integrity": "sha512-AFg4wDHSOk5F+zA8aR+SVIOabu7m0e7BiJnigCvPXzIGy731XENw/lmNxTySpVFtkFEy+eyt4oHhh5FF3NjQNw==",
+ "version": "1.5.75",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.75.tgz",
+ "integrity": "sha512-Lf3++DumRE/QmweGjU+ZcKqQ+3bKkU/qjaKYhIJKEOhgIO9Xs6IiAQFkfFoj+RhgDk4LUeNsLo6plExHqSyu6Q==",
"dev": true
},
"node_modules/emoji-regex": {
@@ -2125,6 +2171,15 @@
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
"dev": true
},
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
@@ -2153,9 +2208,9 @@
"dev": true
},
"node_modules/escalade": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
- "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"engines": {
"node": ">=6"
@@ -2378,6 +2433,12 @@
"integrity": "sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==",
"dev": true
},
+ "node_modules/fast-uri": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz",
+ "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==",
+ "dev": true
+ },
"node_modules/fastest-levenshtein": {
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
@@ -2538,6 +2599,19 @@
"node": ">=10"
}
},
+ "node_modules/fraction.js": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
+ "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
"node_modules/fresh": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
@@ -3096,6 +3170,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/icss-utils": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
+ "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
+ "dev": true,
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
"node_modules/ignore": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz",
@@ -3117,6 +3203,31 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/import-local": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz",
@@ -3213,6 +3324,12 @@
"node": ">= 10"
}
},
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+ "dev": true
+ },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -3465,9 +3582,9 @@
}
},
"node_modules/jiti": {
- "version": "1.21.0",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz",
- "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==",
+ "version": "1.21.7",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
+ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
"dev": true,
"bin": {
"jiti": "bin/jiti.js"
@@ -3484,6 +3601,12 @@
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg=="
},
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
@@ -3581,6 +3704,24 @@
"shell-quote": "^1.8.1"
}
},
+ "node_modules/lilconfig": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
+ "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true
+ },
"node_modules/loader-runner": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@@ -3818,36 +3959,109 @@
"node": ">=6"
}
},
- "node_modules/minimalistic-assert": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
- "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
- "dev": true
- },
- "node_modules/minimatch": {
- "version": "9.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
- "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "node_modules/mini-css-extract-plugin": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz",
+ "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==",
"dev": true,
"dependencies": {
- "brace-expansion": "^2.0.1"
+ "schema-utils": "^4.0.0",
+ "tapable": "^2.2.1"
},
"engines": {
- "node": ">=16 || 14 >=14.17"
+ "node": ">= 12.13.0"
},
"funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
}
},
- "node_modules/minimatch/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "node_modules/mini-css-extract-plugin/node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz",
+ "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==",
+ "dev": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true
+ },
+ "node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.0.tgz",
+ "integrity": "sha512-Gf9qqc58SpCA/xdziiHz35F4GNIWYWZrEshUc/G/r5BnLph6xpKuLeoJoQuj5WfBIx/eQLf+hmVPYHaxJu7V2g==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.9.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.1.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "node_modules/minimatch": {
+ "version": "9.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+ "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimatch/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
@@ -4359,33 +4573,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/node-gyp/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/node-gyp/node_modules/semver/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/node-gyp/node_modules/ssri": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz",
@@ -4429,9 +4616,9 @@
"dev": true
},
"node_modules/node-releases": {
- "version": "2.0.14",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz",
- "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==",
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"dev": true
},
"node_modules/nopt": {
@@ -4485,39 +4672,6 @@
"node": ">=12"
}
},
- "node_modules/normalize-package-data/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/normalize-package-data/node_modules/semver/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/normalize-package-data/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -4527,6 +4681,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/npm-bundled": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz",
@@ -5044,18 +5207,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/npm-check-updates/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/npm-check-updates/node_modules/mimic-response": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
@@ -5194,21 +5345,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/npm-check-updates/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/npm-check-updates/node_modules/semver-diff": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-4.0.0.tgz",
@@ -5333,12 +5469,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/npm-check-updates/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/npm-install-checks": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz",
@@ -5351,39 +5481,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/npm-install-checks/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/npm-install-checks/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/npm-install-checks/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/npm-normalize-package-bin": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz",
@@ -5429,39 +5526,6 @@
"node": ">=12"
}
},
- "node_modules/npm-package-arg/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/npm-package-arg/node_modules/semver/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/npm-package-arg/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/npm-packlist": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz",
@@ -5489,39 +5553,6 @@
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
}
},
- "node_modules/npm-pick-manifest/node_modules/lru-cache": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
- "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
- "dependencies": {
- "yallist": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/npm-pick-manifest/node_modules/semver": {
- "version": "7.5.4",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
- "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
- "dependencies": {
- "lru-cache": "^6.0.0"
- },
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/npm-pick-manifest/node_modules/yallist": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
- },
"node_modules/npm-registry-fetch": {
"version": "14.0.5",
"resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz",
@@ -5775,6 +5806,18 @@
"node": ">=8"
}
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/parse-github-url": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz",
@@ -5787,6 +5830,30 @@
"node": ">=0.10.0"
}
},
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+ "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-json/node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -5870,9 +5937,9 @@
}
},
"node_modules/picocolors": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
- "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true
},
"node_modules/pirates": {
@@ -5897,9 +5964,9 @@
}
},
"node_modules/postcss": {
- "version": "8.4.32",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
- "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
+ "version": "8.4.49",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
+ "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
"dev": true,
"funding": [
{
@@ -5917,8 +5984,8 @@
],
"dependencies": {
"nanoid": "^3.3.7",
- "picocolors": "^1.0.0",
- "source-map-js": "^1.0.2"
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
@@ -5960,29 +6027,138 @@
"postcss": "^8.4.21"
}
},
- "node_modules/postcss-nested": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
- "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
+ "node_modules/postcss-loader": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz",
+ "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==",
"dev": true,
"dependencies": {
- "postcss-selector-parser": "^6.0.11"
+ "cosmiconfig": "^9.0.0",
+ "jiti": "^1.20.0",
+ "semver": "^7.5.4"
},
"engines": {
- "node": ">=12.0"
+ "node": ">= 18.12.0"
},
"funding": {
"type": "opencollective",
- "url": "https://opencollective.com/postcss/"
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "@rspack/core": "0.x || 1.x",
+ "postcss": "^7.0.0 || ^8.0.1",
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@rspack/core": {
+ "optional": true
+ },
+ "webpack": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-modules-extract-imports": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz",
+ "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==",
+ "dev": true,
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-local-by-default": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz",
+ "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^7.0.0",
+ "postcss-value-parser": "^4.1.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-scope": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz",
+ "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==",
+ "dev": true,
+ "dependencies": {
+ "postcss-selector-parser": "^7.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-values": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz",
+ "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==",
+ "dev": true,
+ "dependencies": {
+ "icss-utils": "^5.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
},
"peerDependencies": {
"postcss": "^8.2.14"
}
},
+ "node_modules/postcss-nested/node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/postcss-selector-parser": {
- "version": "6.0.13",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
- "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.0.0.tgz",
+ "integrity": "sha512-9RbEr1Y7FFfptd/1eEdntyjMwLeghW1bHX9GWjXo19vx4ytPQhANltvVxDggzJl7mnWM+dX28kb6cyS/4iQjlQ==",
"dev": true,
"dependencies": {
"cssesc": "^3.0.0",
@@ -6559,6 +6735,18 @@
"node": ">=10"
}
},
+ "node_modules/semver": {
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
+ "dev": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/semver-utils": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/semver-utils/-/semver-utils-1.1.4.tgz",
@@ -6907,9 +7095,9 @@
}
},
"node_modules/source-map-js": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
- "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
"dev": true,
"engines": {
"node": ">=0.10.0"
@@ -7189,14 +7377,14 @@
}
},
"node_modules/sucrase": {
- "version": "3.34.0",
- "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
- "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==",
+ "version": "3.35.0",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
+ "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
"dev": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.2",
"commander": "^4.0.0",
- "glob": "7.1.6",
+ "glob": "^10.3.10",
"lines-and-columns": "^1.1.6",
"mz": "^2.7.0",
"pirates": "^4.0.1",
@@ -7207,7 +7395,7 @@
"sucrase-node": "bin/sucrase-node"
},
"engines": {
- "node": ">=8"
+ "node": ">=16 || 14 >=14.17"
}
},
"node_modules/sucrase/node_modules/commander": {
@@ -7219,50 +7407,6 @@
"node": ">= 6"
}
},
- "node_modules/sucrase/node_modules/glob": {
- "version": "7.1.6",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
- "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
- "dev": true,
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/sucrase/node_modules/lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "dev": true
- },
- "node_modules/sucrase/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
- "dev": true,
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/sucrase/node_modules/ts-interface-checker": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
- "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
- "dev": true
- },
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@@ -7276,33 +7420,33 @@
}
},
"node_modules/tailwindcss": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
- "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==",
+ "version": "3.4.17",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
+ "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
"dev": true,
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
- "chokidar": "^3.5.3",
+ "chokidar": "^3.6.0",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
- "fast-glob": "^3.3.0",
+ "fast-glob": "^3.3.2",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
- "jiti": "^1.19.1",
- "lilconfig": "^2.1.0",
- "micromatch": "^4.0.5",
+ "jiti": "^1.21.6",
+ "lilconfig": "^3.1.3",
+ "micromatch": "^4.0.8",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
- "picocolors": "^1.0.0",
- "postcss": "^8.4.23",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.4.47",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
- "postcss-load-config": "^4.0.1",
- "postcss-nested": "^6.0.1",
- "postcss-selector-parser": "^6.0.11",
- "resolve": "^1.22.2",
- "sucrase": "^3.32.0"
+ "postcss-load-config": "^4.0.2",
+ "postcss-nested": "^6.2.0",
+ "postcss-selector-parser": "^6.1.2",
+ "resolve": "^1.22.8",
+ "sucrase": "^3.35.0"
},
"bin": {
"tailwind": "lib/cli.js",
@@ -7312,15 +7456,6 @@
"node": ">=14.0.0"
}
},
- "node_modules/tailwindcss/node_modules/lilconfig": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
- "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/tailwindcss/node_modules/postcss-load-config": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
@@ -7356,13 +7491,17 @@
}
}
},
- "node_modules/tailwindcss/node_modules/postcss-load-config/node_modules/lilconfig": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz",
- "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==",
+ "node_modules/tailwindcss/node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
"dev": true,
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
"engines": {
- "node": ">=14"
+ "node": ">=4"
}
},
"node_modules/tapable": {
@@ -7529,6 +7668,12 @@
"node": ">=0.6"
}
},
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true
+ },
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
@@ -7643,9 +7788,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.0.13",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz",
- "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
+ "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
"dev": true,
"funding": [
{
@@ -7662,8 +7807,8 @@
}
],
"dependencies": {
- "escalade": "^3.1.1",
- "picocolors": "^1.0.0"
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.0"
},
"bin": {
"update-browserslist-db": "cli.js"
@@ -7759,18 +7904,18 @@
}
},
"node_modules/webpack": {
- "version": "5.94.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz",
- "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==",
- "dev": true,
- "dependencies": {
- "@types/estree": "^1.0.5",
- "@webassemblyjs/ast": "^1.12.1",
- "@webassemblyjs/wasm-edit": "^1.12.1",
- "@webassemblyjs/wasm-parser": "^1.12.1",
- "acorn": "^8.7.1",
- "acorn-import-attributes": "^1.9.5",
- "browserslist": "^4.21.10",
+ "version": "5.97.1",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz",
+ "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==",
+ "dev": true,
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.7",
+ "@types/estree": "^1.0.6",
+ "@webassemblyjs/ast": "^1.14.1",
+ "@webassemblyjs/wasm-edit": "^1.14.1",
+ "@webassemblyjs/wasm-parser": "^1.14.1",
+ "acorn": "^8.14.0",
+ "browserslist": "^4.24.0",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
diff --git a/package.json b/package.json
index 6bee665b5..e8600574f 100644
--- a/package.json
+++ b/package.json
@@ -11,10 +11,15 @@
},
"devDependencies": {
"@fortawesome/fontawesome-free": "^6.5.1",
+ "autoprefixer": "^10.4.20",
+ "css-loader": "^7.1.2",
"daisyui": "^4.9.0",
+ "mini-css-extract-plugin": "^2.9.2",
"npm-check-updates": "16.14.17",
- "tailwindcss": "^3.4.1",
- "webpack": "^5.94.0",
+ "postcss": "^8.4.49",
+ "postcss-loader": "^8.1.1",
+ "tailwindcss": "^3.4.17",
+ "webpack": "^5.97.1",
"webpack-bundle-tracker": "^3.1.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.0.3",
@@ -25,7 +30,6 @@
"daisyui": "^4.7.3",
"htmx.org": "^1.9.12",
"hyperscript.org": "^0.9.12",
- "jquery": "^3.7.1",
- "tailwindcss": "^3.4.1"
+ "jquery": "^3.7.1"
}
}
diff --git a/poetry.lock b/poetry.lock
index e920c78e3..f08cbade9 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -81,19 +81,19 @@ files = [
[[package]]
name = "attrs"
-version = "24.2.0"
+version = "24.3.0"
description = "Classes Without Boilerplate"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"},
- {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"},
+ {file = "attrs-24.3.0-py3-none-any.whl", hash = "sha256:ac96cd038792094f438ad1f6ff80837353805ac950cd2aa0e0625ef19850c308"},
+ {file = "attrs-24.3.0.tar.gz", hash = "sha256:8f5c07333d543103541ba7be0e2ce16eeee8130cb0b3f9238ab904ce1e85baff"},
]
[package.extras]
benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
-dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
+dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"]
@@ -213,13 +213,13 @@ crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
[[package]]
name = "boto3-stubs"
-version = "1.35.76"
-description = "Type annotations for boto3 1.35.76 generated with mypy-boto3-builder 8.5.0"
+version = "1.35.85"
+description = "Type annotations for boto3 1.35.85 generated with mypy-boto3-builder 8.6.4"
optional = false
python-versions = ">=3.8"
files = [
- {file = "boto3_stubs-1.35.76-py3-none-any.whl", hash = "sha256:882f69f02cca48176fa3adf7f354fe64a65268423c9696871d0f4d098af35431"},
- {file = "boto3_stubs-1.35.76.tar.gz", hash = "sha256:32109b6a0c9720bf7c2e389655479c6dab4ee33c622e2cf2746c9e5ec527bae3"},
+ {file = "boto3_stubs-1.35.85-py3-none-any.whl", hash = "sha256:c3c1709603cb9d0fba4667b8408847f05b8f0b92bb74e88e0e97571cb6dd7745"},
+ {file = "boto3_stubs-1.35.85.tar.gz", hash = "sha256:c949abdba605dec649cfceab95f573c8fbce575ed23d8522e965b9eb6da4eeba"},
]
[package.dependencies]
@@ -238,7 +238,7 @@ accessanalyzer = ["mypy-boto3-accessanalyzer (>=1.35.0,<1.36.0)"]
account = ["mypy-boto3-account (>=1.35.0,<1.36.0)"]
acm = ["mypy-boto3-acm (>=1.35.0,<1.36.0)"]
acm-pca = ["mypy-boto3-acm-pca (>=1.35.0,<1.36.0)"]
-all = ["mypy-boto3-accessanalyzer (>=1.35.0,<1.36.0)", "mypy-boto3-account (>=1.35.0,<1.36.0)", "mypy-boto3-acm (>=1.35.0,<1.36.0)", "mypy-boto3-acm-pca (>=1.35.0,<1.36.0)", "mypy-boto3-amp (>=1.35.0,<1.36.0)", "mypy-boto3-amplify (>=1.35.0,<1.36.0)", "mypy-boto3-amplifybackend (>=1.35.0,<1.36.0)", "mypy-boto3-amplifyuibuilder (>=1.35.0,<1.36.0)", "mypy-boto3-apigateway (>=1.35.0,<1.36.0)", "mypy-boto3-apigatewaymanagementapi (>=1.35.0,<1.36.0)", "mypy-boto3-apigatewayv2 (>=1.35.0,<1.36.0)", "mypy-boto3-appconfig (>=1.35.0,<1.36.0)", "mypy-boto3-appconfigdata (>=1.35.0,<1.36.0)", "mypy-boto3-appfabric (>=1.35.0,<1.36.0)", "mypy-boto3-appflow (>=1.35.0,<1.36.0)", "mypy-boto3-appintegrations (>=1.35.0,<1.36.0)", "mypy-boto3-application-autoscaling (>=1.35.0,<1.36.0)", "mypy-boto3-application-insights (>=1.35.0,<1.36.0)", "mypy-boto3-application-signals (>=1.35.0,<1.36.0)", "mypy-boto3-applicationcostprofiler (>=1.35.0,<1.36.0)", "mypy-boto3-appmesh (>=1.35.0,<1.36.0)", "mypy-boto3-apprunner (>=1.35.0,<1.36.0)", "mypy-boto3-appstream (>=1.35.0,<1.36.0)", "mypy-boto3-appsync (>=1.35.0,<1.36.0)", "mypy-boto3-apptest (>=1.35.0,<1.36.0)", "mypy-boto3-arc-zonal-shift (>=1.35.0,<1.36.0)", "mypy-boto3-artifact (>=1.35.0,<1.36.0)", "mypy-boto3-athena (>=1.35.0,<1.36.0)", "mypy-boto3-auditmanager (>=1.35.0,<1.36.0)", "mypy-boto3-autoscaling (>=1.35.0,<1.36.0)", "mypy-boto3-autoscaling-plans (>=1.35.0,<1.36.0)", "mypy-boto3-b2bi (>=1.35.0,<1.36.0)", "mypy-boto3-backup (>=1.35.0,<1.36.0)", "mypy-boto3-backup-gateway (>=1.35.0,<1.36.0)", "mypy-boto3-batch (>=1.35.0,<1.36.0)", "mypy-boto3-bcm-data-exports (>=1.35.0,<1.36.0)", "mypy-boto3-bcm-pricing-calculator (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-agent (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-agent-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-data-automation (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-data-automation-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-billing (>=1.35.0,<1.36.0)", "mypy-boto3-billingconductor (>=1.35.0,<1.36.0)", "mypy-boto3-braket (>=1.35.0,<1.36.0)", "mypy-boto3-budgets (>=1.35.0,<1.36.0)", "mypy-boto3-ce (>=1.35.0,<1.36.0)", "mypy-boto3-chatbot (>=1.35.0,<1.36.0)", "mypy-boto3-chime (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-identity (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-meetings (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-messaging (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-voice (>=1.35.0,<1.36.0)", "mypy-boto3-cleanrooms (>=1.35.0,<1.36.0)", "mypy-boto3-cleanroomsml (>=1.35.0,<1.36.0)", "mypy-boto3-cloud9 (>=1.35.0,<1.36.0)", "mypy-boto3-cloudcontrol (>=1.35.0,<1.36.0)", "mypy-boto3-clouddirectory (>=1.35.0,<1.36.0)", "mypy-boto3-cloudformation (>=1.35.0,<1.36.0)", "mypy-boto3-cloudfront (>=1.35.0,<1.36.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.35.0,<1.36.0)", "mypy-boto3-cloudhsm (>=1.35.0,<1.36.0)", "mypy-boto3-cloudhsmv2 (>=1.35.0,<1.36.0)", "mypy-boto3-cloudsearch (>=1.35.0,<1.36.0)", "mypy-boto3-cloudsearchdomain (>=1.35.0,<1.36.0)", "mypy-boto3-cloudtrail (>=1.35.0,<1.36.0)", "mypy-boto3-cloudtrail-data (>=1.35.0,<1.36.0)", "mypy-boto3-cloudwatch (>=1.35.0,<1.36.0)", "mypy-boto3-codeartifact (>=1.35.0,<1.36.0)", "mypy-boto3-codebuild (>=1.35.0,<1.36.0)", "mypy-boto3-codecatalyst (>=1.35.0,<1.36.0)", "mypy-boto3-codecommit (>=1.35.0,<1.36.0)", "mypy-boto3-codeconnections (>=1.35.0,<1.36.0)", "mypy-boto3-codedeploy (>=1.35.0,<1.36.0)", "mypy-boto3-codeguru-reviewer (>=1.35.0,<1.36.0)", "mypy-boto3-codeguru-security (>=1.35.0,<1.36.0)", "mypy-boto3-codeguruprofiler (>=1.35.0,<1.36.0)", "mypy-boto3-codepipeline (>=1.35.0,<1.36.0)", "mypy-boto3-codestar-connections (>=1.35.0,<1.36.0)", "mypy-boto3-codestar-notifications (>=1.35.0,<1.36.0)", "mypy-boto3-cognito-identity (>=1.35.0,<1.36.0)", "mypy-boto3-cognito-idp (>=1.35.0,<1.36.0)", "mypy-boto3-cognito-sync (>=1.35.0,<1.36.0)", "mypy-boto3-comprehend (>=1.35.0,<1.36.0)", "mypy-boto3-comprehendmedical (>=1.35.0,<1.36.0)", "mypy-boto3-compute-optimizer (>=1.35.0,<1.36.0)", "mypy-boto3-config (>=1.35.0,<1.36.0)", "mypy-boto3-connect (>=1.35.0,<1.36.0)", "mypy-boto3-connect-contact-lens (>=1.35.0,<1.36.0)", "mypy-boto3-connectcampaigns (>=1.35.0,<1.36.0)", "mypy-boto3-connectcampaignsv2 (>=1.35.0,<1.36.0)", "mypy-boto3-connectcases (>=1.35.0,<1.36.0)", "mypy-boto3-connectparticipant (>=1.35.0,<1.36.0)", "mypy-boto3-controlcatalog (>=1.35.0,<1.36.0)", "mypy-boto3-controltower (>=1.35.0,<1.36.0)", "mypy-boto3-cost-optimization-hub (>=1.35.0,<1.36.0)", "mypy-boto3-cur (>=1.35.0,<1.36.0)", "mypy-boto3-customer-profiles (>=1.35.0,<1.36.0)", "mypy-boto3-databrew (>=1.35.0,<1.36.0)", "mypy-boto3-dataexchange (>=1.35.0,<1.36.0)", "mypy-boto3-datapipeline (>=1.35.0,<1.36.0)", "mypy-boto3-datasync (>=1.35.0,<1.36.0)", "mypy-boto3-datazone (>=1.35.0,<1.36.0)", "mypy-boto3-dax (>=1.35.0,<1.36.0)", "mypy-boto3-deadline (>=1.35.0,<1.36.0)", "mypy-boto3-detective (>=1.35.0,<1.36.0)", "mypy-boto3-devicefarm (>=1.35.0,<1.36.0)", "mypy-boto3-devops-guru (>=1.35.0,<1.36.0)", "mypy-boto3-directconnect (>=1.35.0,<1.36.0)", "mypy-boto3-discovery (>=1.35.0,<1.36.0)", "mypy-boto3-dlm (>=1.35.0,<1.36.0)", "mypy-boto3-dms (>=1.35.0,<1.36.0)", "mypy-boto3-docdb (>=1.35.0,<1.36.0)", "mypy-boto3-docdb-elastic (>=1.35.0,<1.36.0)", "mypy-boto3-drs (>=1.35.0,<1.36.0)", "mypy-boto3-ds (>=1.35.0,<1.36.0)", "mypy-boto3-ds-data (>=1.35.0,<1.36.0)", "mypy-boto3-dsql (>=1.35.0,<1.36.0)", "mypy-boto3-dynamodb (>=1.35.0,<1.36.0)", "mypy-boto3-dynamodbstreams (>=1.35.0,<1.36.0)", "mypy-boto3-ebs (>=1.35.0,<1.36.0)", "mypy-boto3-ec2 (>=1.35.0,<1.36.0)", "mypy-boto3-ec2-instance-connect (>=1.35.0,<1.36.0)", "mypy-boto3-ecr (>=1.35.0,<1.36.0)", "mypy-boto3-ecr-public (>=1.35.0,<1.36.0)", "mypy-boto3-ecs (>=1.35.0,<1.36.0)", "mypy-boto3-efs (>=1.35.0,<1.36.0)", "mypy-boto3-eks (>=1.35.0,<1.36.0)", "mypy-boto3-eks-auth (>=1.35.0,<1.36.0)", "mypy-boto3-elastic-inference (>=1.35.0,<1.36.0)", "mypy-boto3-elasticache (>=1.35.0,<1.36.0)", "mypy-boto3-elasticbeanstalk (>=1.35.0,<1.36.0)", "mypy-boto3-elastictranscoder (>=1.35.0,<1.36.0)", "mypy-boto3-elb (>=1.35.0,<1.36.0)", "mypy-boto3-elbv2 (>=1.35.0,<1.36.0)", "mypy-boto3-emr (>=1.35.0,<1.36.0)", "mypy-boto3-emr-containers (>=1.35.0,<1.36.0)", "mypy-boto3-emr-serverless (>=1.35.0,<1.36.0)", "mypy-boto3-entityresolution (>=1.35.0,<1.36.0)", "mypy-boto3-es (>=1.35.0,<1.36.0)", "mypy-boto3-events (>=1.35.0,<1.36.0)", "mypy-boto3-evidently (>=1.35.0,<1.36.0)", "mypy-boto3-finspace (>=1.35.0,<1.36.0)", "mypy-boto3-finspace-data (>=1.35.0,<1.36.0)", "mypy-boto3-firehose (>=1.35.0,<1.36.0)", "mypy-boto3-fis (>=1.35.0,<1.36.0)", "mypy-boto3-fms (>=1.35.0,<1.36.0)", "mypy-boto3-forecast (>=1.35.0,<1.36.0)", "mypy-boto3-forecastquery (>=1.35.0,<1.36.0)", "mypy-boto3-frauddetector (>=1.35.0,<1.36.0)", "mypy-boto3-freetier (>=1.35.0,<1.36.0)", "mypy-boto3-fsx (>=1.35.0,<1.36.0)", "mypy-boto3-gamelift (>=1.35.0,<1.36.0)", "mypy-boto3-geo-maps (>=1.35.0,<1.36.0)", "mypy-boto3-geo-places (>=1.35.0,<1.36.0)", "mypy-boto3-geo-routes (>=1.35.0,<1.36.0)", "mypy-boto3-glacier (>=1.35.0,<1.36.0)", "mypy-boto3-globalaccelerator (>=1.35.0,<1.36.0)", "mypy-boto3-glue (>=1.35.0,<1.36.0)", "mypy-boto3-grafana (>=1.35.0,<1.36.0)", "mypy-boto3-greengrass (>=1.35.0,<1.36.0)", "mypy-boto3-greengrassv2 (>=1.35.0,<1.36.0)", "mypy-boto3-groundstation (>=1.35.0,<1.36.0)", "mypy-boto3-guardduty (>=1.35.0,<1.36.0)", "mypy-boto3-health (>=1.35.0,<1.36.0)", "mypy-boto3-healthlake (>=1.35.0,<1.36.0)", "mypy-boto3-iam (>=1.35.0,<1.36.0)", "mypy-boto3-identitystore (>=1.35.0,<1.36.0)", "mypy-boto3-imagebuilder (>=1.35.0,<1.36.0)", "mypy-boto3-importexport (>=1.35.0,<1.36.0)", "mypy-boto3-inspector (>=1.35.0,<1.36.0)", "mypy-boto3-inspector-scan (>=1.35.0,<1.36.0)", "mypy-boto3-inspector2 (>=1.35.0,<1.36.0)", "mypy-boto3-internetmonitor (>=1.35.0,<1.36.0)", "mypy-boto3-invoicing (>=1.35.0,<1.36.0)", "mypy-boto3-iot (>=1.35.0,<1.36.0)", "mypy-boto3-iot-data (>=1.35.0,<1.36.0)", "mypy-boto3-iot-jobs-data (>=1.35.0,<1.36.0)", "mypy-boto3-iot1click-devices (>=1.35.0,<1.36.0)", "mypy-boto3-iot1click-projects (>=1.35.0,<1.36.0)", "mypy-boto3-iotanalytics (>=1.35.0,<1.36.0)", "mypy-boto3-iotdeviceadvisor (>=1.35.0,<1.36.0)", "mypy-boto3-iotevents (>=1.35.0,<1.36.0)", "mypy-boto3-iotevents-data (>=1.35.0,<1.36.0)", "mypy-boto3-iotfleethub (>=1.35.0,<1.36.0)", "mypy-boto3-iotfleetwise (>=1.35.0,<1.36.0)", "mypy-boto3-iotsecuretunneling (>=1.35.0,<1.36.0)", "mypy-boto3-iotsitewise (>=1.35.0,<1.36.0)", "mypy-boto3-iotthingsgraph (>=1.35.0,<1.36.0)", "mypy-boto3-iottwinmaker (>=1.35.0,<1.36.0)", "mypy-boto3-iotwireless (>=1.35.0,<1.36.0)", "mypy-boto3-ivs (>=1.35.0,<1.36.0)", "mypy-boto3-ivs-realtime (>=1.35.0,<1.36.0)", "mypy-boto3-ivschat (>=1.35.0,<1.36.0)", "mypy-boto3-kafka (>=1.35.0,<1.36.0)", "mypy-boto3-kafkaconnect (>=1.35.0,<1.36.0)", "mypy-boto3-kendra (>=1.35.0,<1.36.0)", "mypy-boto3-kendra-ranking (>=1.35.0,<1.36.0)", "mypy-boto3-keyspaces (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis-video-archived-media (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis-video-media (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis-video-signaling (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.35.0,<1.36.0)", "mypy-boto3-kinesisanalytics (>=1.35.0,<1.36.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.35.0,<1.36.0)", "mypy-boto3-kinesisvideo (>=1.35.0,<1.36.0)", "mypy-boto3-kms (>=1.35.0,<1.36.0)", "mypy-boto3-lakeformation (>=1.35.0,<1.36.0)", "mypy-boto3-lambda (>=1.35.0,<1.36.0)", "mypy-boto3-launch-wizard (>=1.35.0,<1.36.0)", "mypy-boto3-lex-models (>=1.35.0,<1.36.0)", "mypy-boto3-lex-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-lexv2-models (>=1.35.0,<1.36.0)", "mypy-boto3-lexv2-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-license-manager (>=1.35.0,<1.36.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.35.0,<1.36.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.35.0,<1.36.0)", "mypy-boto3-lightsail (>=1.35.0,<1.36.0)", "mypy-boto3-location (>=1.35.0,<1.36.0)", "mypy-boto3-logs (>=1.35.0,<1.36.0)", "mypy-boto3-lookoutequipment (>=1.35.0,<1.36.0)", "mypy-boto3-lookoutmetrics (>=1.35.0,<1.36.0)", "mypy-boto3-lookoutvision (>=1.35.0,<1.36.0)", "mypy-boto3-m2 (>=1.35.0,<1.36.0)", "mypy-boto3-machinelearning (>=1.35.0,<1.36.0)", "mypy-boto3-macie2 (>=1.35.0,<1.36.0)", "mypy-boto3-mailmanager (>=1.35.0,<1.36.0)", "mypy-boto3-managedblockchain (>=1.35.0,<1.36.0)", "mypy-boto3-managedblockchain-query (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-agreement (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-catalog (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-deployment (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-entitlement (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-reporting (>=1.35.0,<1.36.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.35.0,<1.36.0)", "mypy-boto3-mediaconnect (>=1.35.0,<1.36.0)", "mypy-boto3-mediaconvert (>=1.35.0,<1.36.0)", "mypy-boto3-medialive (>=1.35.0,<1.36.0)", "mypy-boto3-mediapackage (>=1.35.0,<1.36.0)", "mypy-boto3-mediapackage-vod (>=1.35.0,<1.36.0)", "mypy-boto3-mediapackagev2 (>=1.35.0,<1.36.0)", "mypy-boto3-mediastore (>=1.35.0,<1.36.0)", "mypy-boto3-mediastore-data (>=1.35.0,<1.36.0)", "mypy-boto3-mediatailor (>=1.35.0,<1.36.0)", "mypy-boto3-medical-imaging (>=1.35.0,<1.36.0)", "mypy-boto3-memorydb (>=1.35.0,<1.36.0)", "mypy-boto3-meteringmarketplace (>=1.35.0,<1.36.0)", "mypy-boto3-mgh (>=1.35.0,<1.36.0)", "mypy-boto3-mgn (>=1.35.0,<1.36.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.35.0,<1.36.0)", "mypy-boto3-migrationhub-config (>=1.35.0,<1.36.0)", "mypy-boto3-migrationhuborchestrator (>=1.35.0,<1.36.0)", "mypy-boto3-migrationhubstrategy (>=1.35.0,<1.36.0)", "mypy-boto3-mq (>=1.35.0,<1.36.0)", "mypy-boto3-mturk (>=1.35.0,<1.36.0)", "mypy-boto3-mwaa (>=1.35.0,<1.36.0)", "mypy-boto3-neptune (>=1.35.0,<1.36.0)", "mypy-boto3-neptune-graph (>=1.35.0,<1.36.0)", "mypy-boto3-neptunedata (>=1.35.0,<1.36.0)", "mypy-boto3-network-firewall (>=1.35.0,<1.36.0)", "mypy-boto3-networkflowmonitor (>=1.35.0,<1.36.0)", "mypy-boto3-networkmanager (>=1.35.0,<1.36.0)", "mypy-boto3-networkmonitor (>=1.35.0,<1.36.0)", "mypy-boto3-notifications (>=1.35.0,<1.36.0)", "mypy-boto3-notificationscontacts (>=1.35.0,<1.36.0)", "mypy-boto3-oam (>=1.35.0,<1.36.0)", "mypy-boto3-observabilityadmin (>=1.35.0,<1.36.0)", "mypy-boto3-omics (>=1.35.0,<1.36.0)", "mypy-boto3-opensearch (>=1.35.0,<1.36.0)", "mypy-boto3-opensearchserverless (>=1.35.0,<1.36.0)", "mypy-boto3-opsworks (>=1.35.0,<1.36.0)", "mypy-boto3-opsworkscm (>=1.35.0,<1.36.0)", "mypy-boto3-organizations (>=1.35.0,<1.36.0)", "mypy-boto3-osis (>=1.35.0,<1.36.0)", "mypy-boto3-outposts (>=1.35.0,<1.36.0)", "mypy-boto3-panorama (>=1.35.0,<1.36.0)", "mypy-boto3-partnercentral-selling (>=1.35.0,<1.36.0)", "mypy-boto3-payment-cryptography (>=1.35.0,<1.36.0)", "mypy-boto3-payment-cryptography-data (>=1.35.0,<1.36.0)", "mypy-boto3-pca-connector-ad (>=1.35.0,<1.36.0)", "mypy-boto3-pca-connector-scep (>=1.35.0,<1.36.0)", "mypy-boto3-pcs (>=1.35.0,<1.36.0)", "mypy-boto3-personalize (>=1.35.0,<1.36.0)", "mypy-boto3-personalize-events (>=1.35.0,<1.36.0)", "mypy-boto3-personalize-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-pi (>=1.35.0,<1.36.0)", "mypy-boto3-pinpoint (>=1.35.0,<1.36.0)", "mypy-boto3-pinpoint-email (>=1.35.0,<1.36.0)", "mypy-boto3-pinpoint-sms-voice (>=1.35.0,<1.36.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.35.0,<1.36.0)", "mypy-boto3-pipes (>=1.35.0,<1.36.0)", "mypy-boto3-polly (>=1.35.0,<1.36.0)", "mypy-boto3-pricing (>=1.35.0,<1.36.0)", "mypy-boto3-privatenetworks (>=1.35.0,<1.36.0)", "mypy-boto3-proton (>=1.35.0,<1.36.0)", "mypy-boto3-qapps (>=1.35.0,<1.36.0)", "mypy-boto3-qbusiness (>=1.35.0,<1.36.0)", "mypy-boto3-qconnect (>=1.35.0,<1.36.0)", "mypy-boto3-qldb (>=1.35.0,<1.36.0)", "mypy-boto3-qldb-session (>=1.35.0,<1.36.0)", "mypy-boto3-quicksight (>=1.35.0,<1.36.0)", "mypy-boto3-ram (>=1.35.0,<1.36.0)", "mypy-boto3-rbin (>=1.35.0,<1.36.0)", "mypy-boto3-rds (>=1.35.0,<1.36.0)", "mypy-boto3-rds-data (>=1.35.0,<1.36.0)", "mypy-boto3-redshift (>=1.35.0,<1.36.0)", "mypy-boto3-redshift-data (>=1.35.0,<1.36.0)", "mypy-boto3-redshift-serverless (>=1.35.0,<1.36.0)", "mypy-boto3-rekognition (>=1.35.0,<1.36.0)", "mypy-boto3-repostspace (>=1.35.0,<1.36.0)", "mypy-boto3-resiliencehub (>=1.35.0,<1.36.0)", "mypy-boto3-resource-explorer-2 (>=1.35.0,<1.36.0)", "mypy-boto3-resource-groups (>=1.35.0,<1.36.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.35.0,<1.36.0)", "mypy-boto3-robomaker (>=1.35.0,<1.36.0)", "mypy-boto3-rolesanywhere (>=1.35.0,<1.36.0)", "mypy-boto3-route53 (>=1.35.0,<1.36.0)", "mypy-boto3-route53-recovery-cluster (>=1.35.0,<1.36.0)", "mypy-boto3-route53-recovery-control-config (>=1.35.0,<1.36.0)", "mypy-boto3-route53-recovery-readiness (>=1.35.0,<1.36.0)", "mypy-boto3-route53domains (>=1.35.0,<1.36.0)", "mypy-boto3-route53profiles (>=1.35.0,<1.36.0)", "mypy-boto3-route53resolver (>=1.35.0,<1.36.0)", "mypy-boto3-rum (>=1.35.0,<1.36.0)", "mypy-boto3-s3 (>=1.35.0,<1.36.0)", "mypy-boto3-s3control (>=1.35.0,<1.36.0)", "mypy-boto3-s3outposts (>=1.35.0,<1.36.0)", "mypy-boto3-s3tables (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-edge (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-geospatial (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-metrics (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-savingsplans (>=1.35.0,<1.36.0)", "mypy-boto3-scheduler (>=1.35.0,<1.36.0)", "mypy-boto3-schemas (>=1.35.0,<1.36.0)", "mypy-boto3-sdb (>=1.35.0,<1.36.0)", "mypy-boto3-secretsmanager (>=1.35.0,<1.36.0)", "mypy-boto3-security-ir (>=1.35.0,<1.36.0)", "mypy-boto3-securityhub (>=1.35.0,<1.36.0)", "mypy-boto3-securitylake (>=1.35.0,<1.36.0)", "mypy-boto3-serverlessrepo (>=1.35.0,<1.36.0)", "mypy-boto3-service-quotas (>=1.35.0,<1.36.0)", "mypy-boto3-servicecatalog (>=1.35.0,<1.36.0)", "mypy-boto3-servicecatalog-appregistry (>=1.35.0,<1.36.0)", "mypy-boto3-servicediscovery (>=1.35.0,<1.36.0)", "mypy-boto3-ses (>=1.35.0,<1.36.0)", "mypy-boto3-sesv2 (>=1.35.0,<1.36.0)", "mypy-boto3-shield (>=1.35.0,<1.36.0)", "mypy-boto3-signer (>=1.35.0,<1.36.0)", "mypy-boto3-simspaceweaver (>=1.35.0,<1.36.0)", "mypy-boto3-sms (>=1.35.0,<1.36.0)", "mypy-boto3-sms-voice (>=1.35.0,<1.36.0)", "mypy-boto3-snow-device-management (>=1.35.0,<1.36.0)", "mypy-boto3-snowball (>=1.35.0,<1.36.0)", "mypy-boto3-sns (>=1.35.0,<1.36.0)", "mypy-boto3-socialmessaging (>=1.35.0,<1.36.0)", "mypy-boto3-sqs (>=1.35.0,<1.36.0)", "mypy-boto3-ssm (>=1.35.0,<1.36.0)", "mypy-boto3-ssm-contacts (>=1.35.0,<1.36.0)", "mypy-boto3-ssm-incidents (>=1.35.0,<1.36.0)", "mypy-boto3-ssm-quicksetup (>=1.35.0,<1.36.0)", "mypy-boto3-ssm-sap (>=1.35.0,<1.36.0)", "mypy-boto3-sso (>=1.35.0,<1.36.0)", "mypy-boto3-sso-admin (>=1.35.0,<1.36.0)", "mypy-boto3-sso-oidc (>=1.35.0,<1.36.0)", "mypy-boto3-stepfunctions (>=1.35.0,<1.36.0)", "mypy-boto3-storagegateway (>=1.35.0,<1.36.0)", "mypy-boto3-sts (>=1.35.0,<1.36.0)", "mypy-boto3-supplychain (>=1.35.0,<1.36.0)", "mypy-boto3-support (>=1.35.0,<1.36.0)", "mypy-boto3-support-app (>=1.35.0,<1.36.0)", "mypy-boto3-swf (>=1.35.0,<1.36.0)", "mypy-boto3-synthetics (>=1.35.0,<1.36.0)", "mypy-boto3-taxsettings (>=1.35.0,<1.36.0)", "mypy-boto3-textract (>=1.35.0,<1.36.0)", "mypy-boto3-timestream-influxdb (>=1.35.0,<1.36.0)", "mypy-boto3-timestream-query (>=1.35.0,<1.36.0)", "mypy-boto3-timestream-write (>=1.35.0,<1.36.0)", "mypy-boto3-tnb (>=1.35.0,<1.36.0)", "mypy-boto3-transcribe (>=1.35.0,<1.36.0)", "mypy-boto3-transfer (>=1.35.0,<1.36.0)", "mypy-boto3-translate (>=1.35.0,<1.36.0)", "mypy-boto3-trustedadvisor (>=1.35.0,<1.36.0)", "mypy-boto3-verifiedpermissions (>=1.35.0,<1.36.0)", "mypy-boto3-voice-id (>=1.35.0,<1.36.0)", "mypy-boto3-vpc-lattice (>=1.35.0,<1.36.0)", "mypy-boto3-waf (>=1.35.0,<1.36.0)", "mypy-boto3-waf-regional (>=1.35.0,<1.36.0)", "mypy-boto3-wafv2 (>=1.35.0,<1.36.0)", "mypy-boto3-wellarchitected (>=1.35.0,<1.36.0)", "mypy-boto3-wisdom (>=1.35.0,<1.36.0)", "mypy-boto3-workdocs (>=1.35.0,<1.36.0)", "mypy-boto3-workmail (>=1.35.0,<1.36.0)", "mypy-boto3-workmailmessageflow (>=1.35.0,<1.36.0)", "mypy-boto3-workspaces (>=1.35.0,<1.36.0)", "mypy-boto3-workspaces-thin-client (>=1.35.0,<1.36.0)", "mypy-boto3-workspaces-web (>=1.35.0,<1.36.0)", "mypy-boto3-xray (>=1.35.0,<1.36.0)"]
+all = ["mypy-boto3-accessanalyzer (>=1.35.0,<1.36.0)", "mypy-boto3-account (>=1.35.0,<1.36.0)", "mypy-boto3-acm (>=1.35.0,<1.36.0)", "mypy-boto3-acm-pca (>=1.35.0,<1.36.0)", "mypy-boto3-amp (>=1.35.0,<1.36.0)", "mypy-boto3-amplify (>=1.35.0,<1.36.0)", "mypy-boto3-amplifybackend (>=1.35.0,<1.36.0)", "mypy-boto3-amplifyuibuilder (>=1.35.0,<1.36.0)", "mypy-boto3-apigateway (>=1.35.0,<1.36.0)", "mypy-boto3-apigatewaymanagementapi (>=1.35.0,<1.36.0)", "mypy-boto3-apigatewayv2 (>=1.35.0,<1.36.0)", "mypy-boto3-appconfig (>=1.35.0,<1.36.0)", "mypy-boto3-appconfigdata (>=1.35.0,<1.36.0)", "mypy-boto3-appfabric (>=1.35.0,<1.36.0)", "mypy-boto3-appflow (>=1.35.0,<1.36.0)", "mypy-boto3-appintegrations (>=1.35.0,<1.36.0)", "mypy-boto3-application-autoscaling (>=1.35.0,<1.36.0)", "mypy-boto3-application-insights (>=1.35.0,<1.36.0)", "mypy-boto3-application-signals (>=1.35.0,<1.36.0)", "mypy-boto3-applicationcostprofiler (>=1.35.0,<1.36.0)", "mypy-boto3-appmesh (>=1.35.0,<1.36.0)", "mypy-boto3-apprunner (>=1.35.0,<1.36.0)", "mypy-boto3-appstream (>=1.35.0,<1.36.0)", "mypy-boto3-appsync (>=1.35.0,<1.36.0)", "mypy-boto3-apptest (>=1.35.0,<1.36.0)", "mypy-boto3-arc-zonal-shift (>=1.35.0,<1.36.0)", "mypy-boto3-artifact (>=1.35.0,<1.36.0)", "mypy-boto3-athena (>=1.35.0,<1.36.0)", "mypy-boto3-auditmanager (>=1.35.0,<1.36.0)", "mypy-boto3-autoscaling (>=1.35.0,<1.36.0)", "mypy-boto3-autoscaling-plans (>=1.35.0,<1.36.0)", "mypy-boto3-b2bi (>=1.35.0,<1.36.0)", "mypy-boto3-backup (>=1.35.0,<1.36.0)", "mypy-boto3-backup-gateway (>=1.35.0,<1.36.0)", "mypy-boto3-backupsearch (>=1.35.0,<1.36.0)", "mypy-boto3-batch (>=1.35.0,<1.36.0)", "mypy-boto3-bcm-data-exports (>=1.35.0,<1.36.0)", "mypy-boto3-bcm-pricing-calculator (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-agent (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-agent-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-data-automation (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-data-automation-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-bedrock-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-billing (>=1.35.0,<1.36.0)", "mypy-boto3-billingconductor (>=1.35.0,<1.36.0)", "mypy-boto3-braket (>=1.35.0,<1.36.0)", "mypy-boto3-budgets (>=1.35.0,<1.36.0)", "mypy-boto3-ce (>=1.35.0,<1.36.0)", "mypy-boto3-chatbot (>=1.35.0,<1.36.0)", "mypy-boto3-chime (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-identity (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-media-pipelines (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-meetings (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-messaging (>=1.35.0,<1.36.0)", "mypy-boto3-chime-sdk-voice (>=1.35.0,<1.36.0)", "mypy-boto3-cleanrooms (>=1.35.0,<1.36.0)", "mypy-boto3-cleanroomsml (>=1.35.0,<1.36.0)", "mypy-boto3-cloud9 (>=1.35.0,<1.36.0)", "mypy-boto3-cloudcontrol (>=1.35.0,<1.36.0)", "mypy-boto3-clouddirectory (>=1.35.0,<1.36.0)", "mypy-boto3-cloudformation (>=1.35.0,<1.36.0)", "mypy-boto3-cloudfront (>=1.35.0,<1.36.0)", "mypy-boto3-cloudfront-keyvaluestore (>=1.35.0,<1.36.0)", "mypy-boto3-cloudhsm (>=1.35.0,<1.36.0)", "mypy-boto3-cloudhsmv2 (>=1.35.0,<1.36.0)", "mypy-boto3-cloudsearch (>=1.35.0,<1.36.0)", "mypy-boto3-cloudsearchdomain (>=1.35.0,<1.36.0)", "mypy-boto3-cloudtrail (>=1.35.0,<1.36.0)", "mypy-boto3-cloudtrail-data (>=1.35.0,<1.36.0)", "mypy-boto3-cloudwatch (>=1.35.0,<1.36.0)", "mypy-boto3-codeartifact (>=1.35.0,<1.36.0)", "mypy-boto3-codebuild (>=1.35.0,<1.36.0)", "mypy-boto3-codecatalyst (>=1.35.0,<1.36.0)", "mypy-boto3-codecommit (>=1.35.0,<1.36.0)", "mypy-boto3-codeconnections (>=1.35.0,<1.36.0)", "mypy-boto3-codedeploy (>=1.35.0,<1.36.0)", "mypy-boto3-codeguru-reviewer (>=1.35.0,<1.36.0)", "mypy-boto3-codeguru-security (>=1.35.0,<1.36.0)", "mypy-boto3-codeguruprofiler (>=1.35.0,<1.36.0)", "mypy-boto3-codepipeline (>=1.35.0,<1.36.0)", "mypy-boto3-codestar-connections (>=1.35.0,<1.36.0)", "mypy-boto3-codestar-notifications (>=1.35.0,<1.36.0)", "mypy-boto3-cognito-identity (>=1.35.0,<1.36.0)", "mypy-boto3-cognito-idp (>=1.35.0,<1.36.0)", "mypy-boto3-cognito-sync (>=1.35.0,<1.36.0)", "mypy-boto3-comprehend (>=1.35.0,<1.36.0)", "mypy-boto3-comprehendmedical (>=1.35.0,<1.36.0)", "mypy-boto3-compute-optimizer (>=1.35.0,<1.36.0)", "mypy-boto3-config (>=1.35.0,<1.36.0)", "mypy-boto3-connect (>=1.35.0,<1.36.0)", "mypy-boto3-connect-contact-lens (>=1.35.0,<1.36.0)", "mypy-boto3-connectcampaigns (>=1.35.0,<1.36.0)", "mypy-boto3-connectcampaignsv2 (>=1.35.0,<1.36.0)", "mypy-boto3-connectcases (>=1.35.0,<1.36.0)", "mypy-boto3-connectparticipant (>=1.35.0,<1.36.0)", "mypy-boto3-controlcatalog (>=1.35.0,<1.36.0)", "mypy-boto3-controltower (>=1.35.0,<1.36.0)", "mypy-boto3-cost-optimization-hub (>=1.35.0,<1.36.0)", "mypy-boto3-cur (>=1.35.0,<1.36.0)", "mypy-boto3-customer-profiles (>=1.35.0,<1.36.0)", "mypy-boto3-databrew (>=1.35.0,<1.36.0)", "mypy-boto3-dataexchange (>=1.35.0,<1.36.0)", "mypy-boto3-datapipeline (>=1.35.0,<1.36.0)", "mypy-boto3-datasync (>=1.35.0,<1.36.0)", "mypy-boto3-datazone (>=1.35.0,<1.36.0)", "mypy-boto3-dax (>=1.35.0,<1.36.0)", "mypy-boto3-deadline (>=1.35.0,<1.36.0)", "mypy-boto3-detective (>=1.35.0,<1.36.0)", "mypy-boto3-devicefarm (>=1.35.0,<1.36.0)", "mypy-boto3-devops-guru (>=1.35.0,<1.36.0)", "mypy-boto3-directconnect (>=1.35.0,<1.36.0)", "mypy-boto3-discovery (>=1.35.0,<1.36.0)", "mypy-boto3-dlm (>=1.35.0,<1.36.0)", "mypy-boto3-dms (>=1.35.0,<1.36.0)", "mypy-boto3-docdb (>=1.35.0,<1.36.0)", "mypy-boto3-docdb-elastic (>=1.35.0,<1.36.0)", "mypy-boto3-drs (>=1.35.0,<1.36.0)", "mypy-boto3-ds (>=1.35.0,<1.36.0)", "mypy-boto3-ds-data (>=1.35.0,<1.36.0)", "mypy-boto3-dsql (>=1.35.0,<1.36.0)", "mypy-boto3-dynamodb (>=1.35.0,<1.36.0)", "mypy-boto3-dynamodbstreams (>=1.35.0,<1.36.0)", "mypy-boto3-ebs (>=1.35.0,<1.36.0)", "mypy-boto3-ec2 (>=1.35.0,<1.36.0)", "mypy-boto3-ec2-instance-connect (>=1.35.0,<1.36.0)", "mypy-boto3-ecr (>=1.35.0,<1.36.0)", "mypy-boto3-ecr-public (>=1.35.0,<1.36.0)", "mypy-boto3-ecs (>=1.35.0,<1.36.0)", "mypy-boto3-efs (>=1.35.0,<1.36.0)", "mypy-boto3-eks (>=1.35.0,<1.36.0)", "mypy-boto3-eks-auth (>=1.35.0,<1.36.0)", "mypy-boto3-elastic-inference (>=1.35.0,<1.36.0)", "mypy-boto3-elasticache (>=1.35.0,<1.36.0)", "mypy-boto3-elasticbeanstalk (>=1.35.0,<1.36.0)", "mypy-boto3-elastictranscoder (>=1.35.0,<1.36.0)", "mypy-boto3-elb (>=1.35.0,<1.36.0)", "mypy-boto3-elbv2 (>=1.35.0,<1.36.0)", "mypy-boto3-emr (>=1.35.0,<1.36.0)", "mypy-boto3-emr-containers (>=1.35.0,<1.36.0)", "mypy-boto3-emr-serverless (>=1.35.0,<1.36.0)", "mypy-boto3-entityresolution (>=1.35.0,<1.36.0)", "mypy-boto3-es (>=1.35.0,<1.36.0)", "mypy-boto3-events (>=1.35.0,<1.36.0)", "mypy-boto3-evidently (>=1.35.0,<1.36.0)", "mypy-boto3-finspace (>=1.35.0,<1.36.0)", "mypy-boto3-finspace-data (>=1.35.0,<1.36.0)", "mypy-boto3-firehose (>=1.35.0,<1.36.0)", "mypy-boto3-fis (>=1.35.0,<1.36.0)", "mypy-boto3-fms (>=1.35.0,<1.36.0)", "mypy-boto3-forecast (>=1.35.0,<1.36.0)", "mypy-boto3-forecastquery (>=1.35.0,<1.36.0)", "mypy-boto3-frauddetector (>=1.35.0,<1.36.0)", "mypy-boto3-freetier (>=1.35.0,<1.36.0)", "mypy-boto3-fsx (>=1.35.0,<1.36.0)", "mypy-boto3-gamelift (>=1.35.0,<1.36.0)", "mypy-boto3-geo-maps (>=1.35.0,<1.36.0)", "mypy-boto3-geo-places (>=1.35.0,<1.36.0)", "mypy-boto3-geo-routes (>=1.35.0,<1.36.0)", "mypy-boto3-glacier (>=1.35.0,<1.36.0)", "mypy-boto3-globalaccelerator (>=1.35.0,<1.36.0)", "mypy-boto3-glue (>=1.35.0,<1.36.0)", "mypy-boto3-grafana (>=1.35.0,<1.36.0)", "mypy-boto3-greengrass (>=1.35.0,<1.36.0)", "mypy-boto3-greengrassv2 (>=1.35.0,<1.36.0)", "mypy-boto3-groundstation (>=1.35.0,<1.36.0)", "mypy-boto3-guardduty (>=1.35.0,<1.36.0)", "mypy-boto3-health (>=1.35.0,<1.36.0)", "mypy-boto3-healthlake (>=1.35.0,<1.36.0)", "mypy-boto3-iam (>=1.35.0,<1.36.0)", "mypy-boto3-identitystore (>=1.35.0,<1.36.0)", "mypy-boto3-imagebuilder (>=1.35.0,<1.36.0)", "mypy-boto3-importexport (>=1.35.0,<1.36.0)", "mypy-boto3-inspector (>=1.35.0,<1.36.0)", "mypy-boto3-inspector-scan (>=1.35.0,<1.36.0)", "mypy-boto3-inspector2 (>=1.35.0,<1.36.0)", "mypy-boto3-internetmonitor (>=1.35.0,<1.36.0)", "mypy-boto3-invoicing (>=1.35.0,<1.36.0)", "mypy-boto3-iot (>=1.35.0,<1.36.0)", "mypy-boto3-iot-data (>=1.35.0,<1.36.0)", "mypy-boto3-iot-jobs-data (>=1.35.0,<1.36.0)", "mypy-boto3-iot1click-devices (>=1.35.0,<1.36.0)", "mypy-boto3-iot1click-projects (>=1.35.0,<1.36.0)", "mypy-boto3-iotanalytics (>=1.35.0,<1.36.0)", "mypy-boto3-iotdeviceadvisor (>=1.35.0,<1.36.0)", "mypy-boto3-iotevents (>=1.35.0,<1.36.0)", "mypy-boto3-iotevents-data (>=1.35.0,<1.36.0)", "mypy-boto3-iotfleethub (>=1.35.0,<1.36.0)", "mypy-boto3-iotfleetwise (>=1.35.0,<1.36.0)", "mypy-boto3-iotsecuretunneling (>=1.35.0,<1.36.0)", "mypy-boto3-iotsitewise (>=1.35.0,<1.36.0)", "mypy-boto3-iotthingsgraph (>=1.35.0,<1.36.0)", "mypy-boto3-iottwinmaker (>=1.35.0,<1.36.0)", "mypy-boto3-iotwireless (>=1.35.0,<1.36.0)", "mypy-boto3-ivs (>=1.35.0,<1.36.0)", "mypy-boto3-ivs-realtime (>=1.35.0,<1.36.0)", "mypy-boto3-ivschat (>=1.35.0,<1.36.0)", "mypy-boto3-kafka (>=1.35.0,<1.36.0)", "mypy-boto3-kafkaconnect (>=1.35.0,<1.36.0)", "mypy-boto3-kendra (>=1.35.0,<1.36.0)", "mypy-boto3-kendra-ranking (>=1.35.0,<1.36.0)", "mypy-boto3-keyspaces (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis-video-archived-media (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis-video-media (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis-video-signaling (>=1.35.0,<1.36.0)", "mypy-boto3-kinesis-video-webrtc-storage (>=1.35.0,<1.36.0)", "mypy-boto3-kinesisanalytics (>=1.35.0,<1.36.0)", "mypy-boto3-kinesisanalyticsv2 (>=1.35.0,<1.36.0)", "mypy-boto3-kinesisvideo (>=1.35.0,<1.36.0)", "mypy-boto3-kms (>=1.35.0,<1.36.0)", "mypy-boto3-lakeformation (>=1.35.0,<1.36.0)", "mypy-boto3-lambda (>=1.35.0,<1.36.0)", "mypy-boto3-launch-wizard (>=1.35.0,<1.36.0)", "mypy-boto3-lex-models (>=1.35.0,<1.36.0)", "mypy-boto3-lex-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-lexv2-models (>=1.35.0,<1.36.0)", "mypy-boto3-lexv2-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-license-manager (>=1.35.0,<1.36.0)", "mypy-boto3-license-manager-linux-subscriptions (>=1.35.0,<1.36.0)", "mypy-boto3-license-manager-user-subscriptions (>=1.35.0,<1.36.0)", "mypy-boto3-lightsail (>=1.35.0,<1.36.0)", "mypy-boto3-location (>=1.35.0,<1.36.0)", "mypy-boto3-logs (>=1.35.0,<1.36.0)", "mypy-boto3-lookoutequipment (>=1.35.0,<1.36.0)", "mypy-boto3-lookoutmetrics (>=1.35.0,<1.36.0)", "mypy-boto3-lookoutvision (>=1.35.0,<1.36.0)", "mypy-boto3-m2 (>=1.35.0,<1.36.0)", "mypy-boto3-machinelearning (>=1.35.0,<1.36.0)", "mypy-boto3-macie2 (>=1.35.0,<1.36.0)", "mypy-boto3-mailmanager (>=1.35.0,<1.36.0)", "mypy-boto3-managedblockchain (>=1.35.0,<1.36.0)", "mypy-boto3-managedblockchain-query (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-agreement (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-catalog (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-deployment (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-entitlement (>=1.35.0,<1.36.0)", "mypy-boto3-marketplace-reporting (>=1.35.0,<1.36.0)", "mypy-boto3-marketplacecommerceanalytics (>=1.35.0,<1.36.0)", "mypy-boto3-mediaconnect (>=1.35.0,<1.36.0)", "mypy-boto3-mediaconvert (>=1.35.0,<1.36.0)", "mypy-boto3-medialive (>=1.35.0,<1.36.0)", "mypy-boto3-mediapackage (>=1.35.0,<1.36.0)", "mypy-boto3-mediapackage-vod (>=1.35.0,<1.36.0)", "mypy-boto3-mediapackagev2 (>=1.35.0,<1.36.0)", "mypy-boto3-mediastore (>=1.35.0,<1.36.0)", "mypy-boto3-mediastore-data (>=1.35.0,<1.36.0)", "mypy-boto3-mediatailor (>=1.35.0,<1.36.0)", "mypy-boto3-medical-imaging (>=1.35.0,<1.36.0)", "mypy-boto3-memorydb (>=1.35.0,<1.36.0)", "mypy-boto3-meteringmarketplace (>=1.35.0,<1.36.0)", "mypy-boto3-mgh (>=1.35.0,<1.36.0)", "mypy-boto3-mgn (>=1.35.0,<1.36.0)", "mypy-boto3-migration-hub-refactor-spaces (>=1.35.0,<1.36.0)", "mypy-boto3-migrationhub-config (>=1.35.0,<1.36.0)", "mypy-boto3-migrationhuborchestrator (>=1.35.0,<1.36.0)", "mypy-boto3-migrationhubstrategy (>=1.35.0,<1.36.0)", "mypy-boto3-mq (>=1.35.0,<1.36.0)", "mypy-boto3-mturk (>=1.35.0,<1.36.0)", "mypy-boto3-mwaa (>=1.35.0,<1.36.0)", "mypy-boto3-neptune (>=1.35.0,<1.36.0)", "mypy-boto3-neptune-graph (>=1.35.0,<1.36.0)", "mypy-boto3-neptunedata (>=1.35.0,<1.36.0)", "mypy-boto3-network-firewall (>=1.35.0,<1.36.0)", "mypy-boto3-networkflowmonitor (>=1.35.0,<1.36.0)", "mypy-boto3-networkmanager (>=1.35.0,<1.36.0)", "mypy-boto3-networkmonitor (>=1.35.0,<1.36.0)", "mypy-boto3-notifications (>=1.35.0,<1.36.0)", "mypy-boto3-notificationscontacts (>=1.35.0,<1.36.0)", "mypy-boto3-oam (>=1.35.0,<1.36.0)", "mypy-boto3-observabilityadmin (>=1.35.0,<1.36.0)", "mypy-boto3-omics (>=1.35.0,<1.36.0)", "mypy-boto3-opensearch (>=1.35.0,<1.36.0)", "mypy-boto3-opensearchserverless (>=1.35.0,<1.36.0)", "mypy-boto3-opsworks (>=1.35.0,<1.36.0)", "mypy-boto3-opsworkscm (>=1.35.0,<1.36.0)", "mypy-boto3-organizations (>=1.35.0,<1.36.0)", "mypy-boto3-osis (>=1.35.0,<1.36.0)", "mypy-boto3-outposts (>=1.35.0,<1.36.0)", "mypy-boto3-panorama (>=1.35.0,<1.36.0)", "mypy-boto3-partnercentral-selling (>=1.35.0,<1.36.0)", "mypy-boto3-payment-cryptography (>=1.35.0,<1.36.0)", "mypy-boto3-payment-cryptography-data (>=1.35.0,<1.36.0)", "mypy-boto3-pca-connector-ad (>=1.35.0,<1.36.0)", "mypy-boto3-pca-connector-scep (>=1.35.0,<1.36.0)", "mypy-boto3-pcs (>=1.35.0,<1.36.0)", "mypy-boto3-personalize (>=1.35.0,<1.36.0)", "mypy-boto3-personalize-events (>=1.35.0,<1.36.0)", "mypy-boto3-personalize-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-pi (>=1.35.0,<1.36.0)", "mypy-boto3-pinpoint (>=1.35.0,<1.36.0)", "mypy-boto3-pinpoint-email (>=1.35.0,<1.36.0)", "mypy-boto3-pinpoint-sms-voice (>=1.35.0,<1.36.0)", "mypy-boto3-pinpoint-sms-voice-v2 (>=1.35.0,<1.36.0)", "mypy-boto3-pipes (>=1.35.0,<1.36.0)", "mypy-boto3-polly (>=1.35.0,<1.36.0)", "mypy-boto3-pricing (>=1.35.0,<1.36.0)", "mypy-boto3-privatenetworks (>=1.35.0,<1.36.0)", "mypy-boto3-proton (>=1.35.0,<1.36.0)", "mypy-boto3-qapps (>=1.35.0,<1.36.0)", "mypy-boto3-qbusiness (>=1.35.0,<1.36.0)", "mypy-boto3-qconnect (>=1.35.0,<1.36.0)", "mypy-boto3-qldb (>=1.35.0,<1.36.0)", "mypy-boto3-qldb-session (>=1.35.0,<1.36.0)", "mypy-boto3-quicksight (>=1.35.0,<1.36.0)", "mypy-boto3-ram (>=1.35.0,<1.36.0)", "mypy-boto3-rbin (>=1.35.0,<1.36.0)", "mypy-boto3-rds (>=1.35.0,<1.36.0)", "mypy-boto3-rds-data (>=1.35.0,<1.36.0)", "mypy-boto3-redshift (>=1.35.0,<1.36.0)", "mypy-boto3-redshift-data (>=1.35.0,<1.36.0)", "mypy-boto3-redshift-serverless (>=1.35.0,<1.36.0)", "mypy-boto3-rekognition (>=1.35.0,<1.36.0)", "mypy-boto3-repostspace (>=1.35.0,<1.36.0)", "mypy-boto3-resiliencehub (>=1.35.0,<1.36.0)", "mypy-boto3-resource-explorer-2 (>=1.35.0,<1.36.0)", "mypy-boto3-resource-groups (>=1.35.0,<1.36.0)", "mypy-boto3-resourcegroupstaggingapi (>=1.35.0,<1.36.0)", "mypy-boto3-robomaker (>=1.35.0,<1.36.0)", "mypy-boto3-rolesanywhere (>=1.35.0,<1.36.0)", "mypy-boto3-route53 (>=1.35.0,<1.36.0)", "mypy-boto3-route53-recovery-cluster (>=1.35.0,<1.36.0)", "mypy-boto3-route53-recovery-control-config (>=1.35.0,<1.36.0)", "mypy-boto3-route53-recovery-readiness (>=1.35.0,<1.36.0)", "mypy-boto3-route53domains (>=1.35.0,<1.36.0)", "mypy-boto3-route53profiles (>=1.35.0,<1.36.0)", "mypy-boto3-route53resolver (>=1.35.0,<1.36.0)", "mypy-boto3-rum (>=1.35.0,<1.36.0)", "mypy-boto3-s3 (>=1.35.0,<1.36.0)", "mypy-boto3-s3control (>=1.35.0,<1.36.0)", "mypy-boto3-s3outposts (>=1.35.0,<1.36.0)", "mypy-boto3-s3tables (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-a2i-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-edge (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-featurestore-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-geospatial (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-metrics (>=1.35.0,<1.36.0)", "mypy-boto3-sagemaker-runtime (>=1.35.0,<1.36.0)", "mypy-boto3-savingsplans (>=1.35.0,<1.36.0)", "mypy-boto3-scheduler (>=1.35.0,<1.36.0)", "mypy-boto3-schemas (>=1.35.0,<1.36.0)", "mypy-boto3-sdb (>=1.35.0,<1.36.0)", "mypy-boto3-secretsmanager (>=1.35.0,<1.36.0)", "mypy-boto3-security-ir (>=1.35.0,<1.36.0)", "mypy-boto3-securityhub (>=1.35.0,<1.36.0)", "mypy-boto3-securitylake (>=1.35.0,<1.36.0)", "mypy-boto3-serverlessrepo (>=1.35.0,<1.36.0)", "mypy-boto3-service-quotas (>=1.35.0,<1.36.0)", "mypy-boto3-servicecatalog (>=1.35.0,<1.36.0)", "mypy-boto3-servicecatalog-appregistry (>=1.35.0,<1.36.0)", "mypy-boto3-servicediscovery (>=1.35.0,<1.36.0)", "mypy-boto3-ses (>=1.35.0,<1.36.0)", "mypy-boto3-sesv2 (>=1.35.0,<1.36.0)", "mypy-boto3-shield (>=1.35.0,<1.36.0)", "mypy-boto3-signer (>=1.35.0,<1.36.0)", "mypy-boto3-simspaceweaver (>=1.35.0,<1.36.0)", "mypy-boto3-sms (>=1.35.0,<1.36.0)", "mypy-boto3-sms-voice (>=1.35.0,<1.36.0)", "mypy-boto3-snow-device-management (>=1.35.0,<1.36.0)", "mypy-boto3-snowball (>=1.35.0,<1.36.0)", "mypy-boto3-sns (>=1.35.0,<1.36.0)", "mypy-boto3-socialmessaging (>=1.35.0,<1.36.0)", "mypy-boto3-sqs (>=1.35.0,<1.36.0)", "mypy-boto3-ssm (>=1.35.0,<1.36.0)", "mypy-boto3-ssm-contacts (>=1.35.0,<1.36.0)", "mypy-boto3-ssm-incidents (>=1.35.0,<1.36.0)", "mypy-boto3-ssm-quicksetup (>=1.35.0,<1.36.0)", "mypy-boto3-ssm-sap (>=1.35.0,<1.36.0)", "mypy-boto3-sso (>=1.35.0,<1.36.0)", "mypy-boto3-sso-admin (>=1.35.0,<1.36.0)", "mypy-boto3-sso-oidc (>=1.35.0,<1.36.0)", "mypy-boto3-stepfunctions (>=1.35.0,<1.36.0)", "mypy-boto3-storagegateway (>=1.35.0,<1.36.0)", "mypy-boto3-sts (>=1.35.0,<1.36.0)", "mypy-boto3-supplychain (>=1.35.0,<1.36.0)", "mypy-boto3-support (>=1.35.0,<1.36.0)", "mypy-boto3-support-app (>=1.35.0,<1.36.0)", "mypy-boto3-swf (>=1.35.0,<1.36.0)", "mypy-boto3-synthetics (>=1.35.0,<1.36.0)", "mypy-boto3-taxsettings (>=1.35.0,<1.36.0)", "mypy-boto3-textract (>=1.35.0,<1.36.0)", "mypy-boto3-timestream-influxdb (>=1.35.0,<1.36.0)", "mypy-boto3-timestream-query (>=1.35.0,<1.36.0)", "mypy-boto3-timestream-write (>=1.35.0,<1.36.0)", "mypy-boto3-tnb (>=1.35.0,<1.36.0)", "mypy-boto3-transcribe (>=1.35.0,<1.36.0)", "mypy-boto3-transfer (>=1.35.0,<1.36.0)", "mypy-boto3-translate (>=1.35.0,<1.36.0)", "mypy-boto3-trustedadvisor (>=1.35.0,<1.36.0)", "mypy-boto3-verifiedpermissions (>=1.35.0,<1.36.0)", "mypy-boto3-voice-id (>=1.35.0,<1.36.0)", "mypy-boto3-vpc-lattice (>=1.35.0,<1.36.0)", "mypy-boto3-waf (>=1.35.0,<1.36.0)", "mypy-boto3-waf-regional (>=1.35.0,<1.36.0)", "mypy-boto3-wafv2 (>=1.35.0,<1.36.0)", "mypy-boto3-wellarchitected (>=1.35.0,<1.36.0)", "mypy-boto3-wisdom (>=1.35.0,<1.36.0)", "mypy-boto3-workdocs (>=1.35.0,<1.36.0)", "mypy-boto3-workmail (>=1.35.0,<1.36.0)", "mypy-boto3-workmailmessageflow (>=1.35.0,<1.36.0)", "mypy-boto3-workspaces (>=1.35.0,<1.36.0)", "mypy-boto3-workspaces-thin-client (>=1.35.0,<1.36.0)", "mypy-boto3-workspaces-web (>=1.35.0,<1.36.0)", "mypy-boto3-xray (>=1.35.0,<1.36.0)"]
amp = ["mypy-boto3-amp (>=1.35.0,<1.36.0)"]
amplify = ["mypy-boto3-amplify (>=1.35.0,<1.36.0)"]
amplifybackend = ["mypy-boto3-amplifybackend (>=1.35.0,<1.36.0)"]
@@ -269,6 +269,7 @@ autoscaling-plans = ["mypy-boto3-autoscaling-plans (>=1.35.0,<1.36.0)"]
b2bi = ["mypy-boto3-b2bi (>=1.35.0,<1.36.0)"]
backup = ["mypy-boto3-backup (>=1.35.0,<1.36.0)"]
backup-gateway = ["mypy-boto3-backup-gateway (>=1.35.0,<1.36.0)"]
+backupsearch = ["mypy-boto3-backupsearch (>=1.35.0,<1.36.0)"]
batch = ["mypy-boto3-batch (>=1.35.0,<1.36.0)"]
bcm-data-exports = ["mypy-boto3-bcm-data-exports (>=1.35.0,<1.36.0)"]
bcm-pricing-calculator = ["mypy-boto3-bcm-pricing-calculator (>=1.35.0,<1.36.0)"]
@@ -280,7 +281,7 @@ bedrock-data-automation-runtime = ["mypy-boto3-bedrock-data-automation-runtime (
bedrock-runtime = ["mypy-boto3-bedrock-runtime (>=1.35.0,<1.36.0)"]
billing = ["mypy-boto3-billing (>=1.35.0,<1.36.0)"]
billingconductor = ["mypy-boto3-billingconductor (>=1.35.0,<1.36.0)"]
-boto3 = ["boto3 (==1.35.76)", "botocore (==1.35.76)"]
+boto3 = ["boto3 (==1.35.85)"]
braket = ["mypy-boto3-braket (>=1.35.0,<1.36.0)"]
budgets = ["mypy-boto3-budgets (>=1.35.0,<1.36.0)"]
ce = ["mypy-boto3-ce (>=1.35.0,<1.36.0)"]
@@ -391,7 +392,7 @@ forecastquery = ["mypy-boto3-forecastquery (>=1.35.0,<1.36.0)"]
frauddetector = ["mypy-boto3-frauddetector (>=1.35.0,<1.36.0)"]
freetier = ["mypy-boto3-freetier (>=1.35.0,<1.36.0)"]
fsx = ["mypy-boto3-fsx (>=1.35.0,<1.36.0)"]
-full = ["boto3-stubs-full"]
+full = ["boto3-stubs-full (>=1.35.0,<1.36.0)"]
gamelift = ["mypy-boto3-gamelift (>=1.35.0,<1.36.0)"]
geo-maps = ["mypy-boto3-geo-maps (>=1.35.0,<1.36.0)"]
geo-places = ["mypy-boto3-geo-places (>=1.35.0,<1.36.0)"]
@@ -664,13 +665,13 @@ crt = ["awscrt (==0.21.2)"]
[[package]]
name = "botocore-stubs"
-version = "1.35.76"
+version = "1.35.84.post1"
description = "Type annotations and code completion for botocore"
optional = false
python-versions = ">=3.8"
files = [
- {file = "botocore_stubs-1.35.76-py3-none-any.whl", hash = "sha256:617508d023e0bc98901e0189b794c4b3f289c1747c7cc410173ad698c819a716"},
- {file = "botocore_stubs-1.35.76.tar.gz", hash = "sha256:c977a049481d50a14bf2db0ef15020b76734ff628d4b8e0e77b8d1c65318369e"},
+ {file = "botocore_stubs-1.35.84.post1-py3-none-any.whl", hash = "sha256:bb0d6fb74161944ab61a66ae2d891b780c163e89336f24462d0e8aed5b9ff4b9"},
+ {file = "botocore_stubs-1.35.84.post1.tar.gz", hash = "sha256:ead1b807a3baa77962bc89ab701616425374ba5d31219fb64c2ca090f33090b3"},
]
[package.dependencies]
@@ -706,13 +707,13 @@ test = ["coverage", "pre-commit", "pytest", "pytest-cov", "pytest-mock"]
[[package]]
name = "certifi"
-version = "2024.8.30"
+version = "2024.12.14"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
files = [
- {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"},
- {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"},
+ {file = "certifi-2024.12.14-py3-none-any.whl", hash = "sha256:1275f7a45be9464efc1173084eaa30f866fe2e47d389406136d332ed4967ec56"},
+ {file = "certifi-2024.12.14.tar.gz", hash = "sha256:b650d30f370c2b724812bee08008be0c4163b163ddaec3f2546c1caf65f191db"},
]
[[package]]
@@ -957,73 +958,73 @@ files = [
[[package]]
name = "coverage"
-version = "7.6.8"
+version = "7.6.9"
description = "Code coverage measurement for Python"
optional = false
python-versions = ">=3.9"
files = [
- {file = "coverage-7.6.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b39e6011cd06822eb964d038d5dff5da5d98652b81f5ecd439277b32361a3a50"},
- {file = "coverage-7.6.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:63c19702db10ad79151a059d2d6336fe0c470f2e18d0d4d1a57f7f9713875dcf"},
- {file = "coverage-7.6.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3985b9be361d8fb6b2d1adc9924d01dec575a1d7453a14cccd73225cb79243ee"},
- {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644ec81edec0f4ad17d51c838a7d01e42811054543b76d4ba2c5d6af741ce2a6"},
- {file = "coverage-7.6.8-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f188a2402f8359cf0c4b1fe89eea40dc13b52e7b4fd4812450da9fcd210181d"},
- {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e19122296822deafce89a0c5e8685704c067ae65d45e79718c92df7b3ec3d331"},
- {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:13618bed0c38acc418896005732e565b317aa9e98d855a0e9f211a7ffc2d6638"},
- {file = "coverage-7.6.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:193e3bffca48ad74b8c764fb4492dd875038a2f9925530cb094db92bb5e47bed"},
- {file = "coverage-7.6.8-cp310-cp310-win32.whl", hash = "sha256:3988665ee376abce49613701336544041f2117de7b7fbfe91b93d8ff8b151c8e"},
- {file = "coverage-7.6.8-cp310-cp310-win_amd64.whl", hash = "sha256:f56f49b2553d7dd85fd86e029515a221e5c1f8cb3d9c38b470bc38bde7b8445a"},
- {file = "coverage-7.6.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:86cffe9c6dfcfe22e28027069725c7f57f4b868a3f86e81d1c62462764dc46d4"},
- {file = "coverage-7.6.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d82ab6816c3277dc962cfcdc85b1efa0e5f50fb2c449432deaf2398a2928ab94"},
- {file = "coverage-7.6.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13690e923a3932e4fad4c0ebfb9cb5988e03d9dcb4c5150b5fcbf58fd8bddfc4"},
- {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4be32da0c3827ac9132bb488d331cb32e8d9638dd41a0557c5569d57cf22c9c1"},
- {file = "coverage-7.6.8-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44e6c85bbdc809383b509d732b06419fb4544dca29ebe18480379633623baafb"},
- {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:768939f7c4353c0fac2f7c37897e10b1414b571fd85dd9fc49e6a87e37a2e0d8"},
- {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e44961e36cb13c495806d4cac67640ac2866cb99044e210895b506c26ee63d3a"},
- {file = "coverage-7.6.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3ea8bb1ab9558374c0ab591783808511d135a833c3ca64a18ec927f20c4030f0"},
- {file = "coverage-7.6.8-cp311-cp311-win32.whl", hash = "sha256:629a1ba2115dce8bf75a5cce9f2486ae483cb89c0145795603d6554bdc83e801"},
- {file = "coverage-7.6.8-cp311-cp311-win_amd64.whl", hash = "sha256:fb9fc32399dca861584d96eccd6c980b69bbcd7c228d06fb74fe53e007aa8ef9"},
- {file = "coverage-7.6.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:e683e6ecc587643f8cde8f5da6768e9d165cd31edf39ee90ed7034f9ca0eefee"},
- {file = "coverage-7.6.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1defe91d41ce1bd44b40fabf071e6a01a5aa14de4a31b986aa9dfd1b3e3e414a"},
- {file = "coverage-7.6.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7ad66e8e50225ebf4236368cc43c37f59d5e6728f15f6e258c8639fa0dd8e6d"},
- {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fe47da3e4fda5f1abb5709c156eca207eacf8007304ce3019eb001e7a7204cb"},
- {file = "coverage-7.6.8-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:202a2d645c5a46b84992f55b0a3affe4f0ba6b4c611abec32ee88358db4bb649"},
- {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4674f0daa1823c295845b6a740d98a840d7a1c11df00d1fd62614545c1583787"},
- {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:74610105ebd6f33d7c10f8907afed696e79c59e3043c5f20eaa3a46fddf33b4c"},
- {file = "coverage-7.6.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37cda8712145917105e07aab96388ae76e787270ec04bcb9d5cc786d7cbb8443"},
- {file = "coverage-7.6.8-cp312-cp312-win32.whl", hash = "sha256:9e89d5c8509fbd6c03d0dd1972925b22f50db0792ce06324ba069f10787429ad"},
- {file = "coverage-7.6.8-cp312-cp312-win_amd64.whl", hash = "sha256:379c111d3558272a2cae3d8e57e6b6e6f4fe652905692d54bad5ea0ca37c5ad4"},
- {file = "coverage-7.6.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0b0c69f4f724c64dfbfe79f5dfb503b42fe6127b8d479b2677f2b227478db2eb"},
- {file = "coverage-7.6.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c15b32a7aca8038ed7644f854bf17b663bc38e1671b5d6f43f9a2b2bd0c46f63"},
- {file = "coverage-7.6.8-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63068a11171e4276f6ece913bde059e77c713b48c3a848814a6537f35afb8365"},
- {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f4548c5ead23ad13fb7a2c8ea541357474ec13c2b736feb02e19a3085fac002"},
- {file = "coverage-7.6.8-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b4b4299dd0d2c67caaaf286d58aef5e75b125b95615dda4542561a5a566a1e3"},
- {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c9ebfb2507751f7196995142f057d1324afdab56db1d9743aab7f50289abd022"},
- {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c1b4474beee02ede1eef86c25ad4600a424fe36cff01a6103cb4533c6bf0169e"},
- {file = "coverage-7.6.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d9fd2547e6decdbf985d579cf3fc78e4c1d662b9b0ff7cc7862baaab71c9cc5b"},
- {file = "coverage-7.6.8-cp313-cp313-win32.whl", hash = "sha256:8aae5aea53cbfe024919715eca696b1a3201886ce83790537d1c3668459c7146"},
- {file = "coverage-7.6.8-cp313-cp313-win_amd64.whl", hash = "sha256:ae270e79f7e169ccfe23284ff5ea2d52a6f401dc01b337efb54b3783e2ce3f28"},
- {file = "coverage-7.6.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:de38add67a0af869b0d79c525d3e4588ac1ffa92f39116dbe0ed9753f26eba7d"},
- {file = "coverage-7.6.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:b07c25d52b1c16ce5de088046cd2432b30f9ad5e224ff17c8f496d9cb7d1d451"},
- {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62a66ff235e4c2e37ed3b6104d8b478d767ff73838d1222132a7a026aa548764"},
- {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09b9f848b28081e7b975a3626e9081574a7b9196cde26604540582da60235fdf"},
- {file = "coverage-7.6.8-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:093896e530c38c8e9c996901858ac63f3d4171268db2c9c8b373a228f459bbc5"},
- {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:9a7b8ac36fd688c8361cbc7bf1cb5866977ece6e0b17c34aa0df58bda4fa18a4"},
- {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:38c51297b35b3ed91670e1e4efb702b790002e3245a28c76e627478aa3c10d83"},
- {file = "coverage-7.6.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2e4e0f60cb4bd7396108823548e82fdab72d4d8a65e58e2c19bbbc2f1e2bfa4b"},
- {file = "coverage-7.6.8-cp313-cp313t-win32.whl", hash = "sha256:6535d996f6537ecb298b4e287a855f37deaf64ff007162ec0afb9ab8ba3b8b71"},
- {file = "coverage-7.6.8-cp313-cp313t-win_amd64.whl", hash = "sha256:c79c0685f142ca53256722a384540832420dff4ab15fec1863d7e5bc8691bdcc"},
- {file = "coverage-7.6.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3ac47fa29d8d41059ea3df65bd3ade92f97ee4910ed638e87075b8e8ce69599e"},
- {file = "coverage-7.6.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:24eda3a24a38157eee639ca9afe45eefa8d2420d49468819ac5f88b10de84f4c"},
- {file = "coverage-7.6.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4c81ed2820b9023a9a90717020315e63b17b18c274a332e3b6437d7ff70abe0"},
- {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd55f8fc8fa494958772a2a7302b0354ab16e0b9272b3c3d83cdb5bec5bd1779"},
- {file = "coverage-7.6.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f39e2f3530ed1626c66e7493be7a8423b023ca852aacdc91fb30162c350d2a92"},
- {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:716a78a342679cd1177bc8c2fe957e0ab91405bd43a17094324845200b2fddf4"},
- {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:177f01eeaa3aee4a5ffb0d1439c5952b53d5010f86e9d2667963e632e30082cc"},
- {file = "coverage-7.6.8-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:912e95017ff51dc3d7b6e2be158dedc889d9a5cc3382445589ce554f1a34c0ea"},
- {file = "coverage-7.6.8-cp39-cp39-win32.whl", hash = "sha256:4db3ed6a907b555e57cc2e6f14dc3a4c2458cdad8919e40b5357ab9b6db6c43e"},
- {file = "coverage-7.6.8-cp39-cp39-win_amd64.whl", hash = "sha256:428ac484592f780e8cd7b6b14eb568f7c85460c92e2a37cb0c0e5186e1a0d076"},
- {file = "coverage-7.6.8-pp39.pp310-none-any.whl", hash = "sha256:5c52a036535d12590c32c49209e79cabaad9f9ad8aa4cbd875b68c4d67a9cbce"},
- {file = "coverage-7.6.8.tar.gz", hash = "sha256:8b2b8503edb06822c86d82fa64a4a5cb0760bb8f31f26e138ec743f422f37cfc"},
+ {file = "coverage-7.6.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85d9636f72e8991a1706b2b55b06c27545448baf9f6dbf51c4004609aacd7dcb"},
+ {file = "coverage-7.6.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:608a7fd78c67bee8936378299a6cb9f5149bb80238c7a566fc3e6717a4e68710"},
+ {file = "coverage-7.6.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96d636c77af18b5cb664ddf12dab9b15a0cfe9c0bde715da38698c8cea748bfa"},
+ {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d75cded8a3cff93da9edc31446872d2997e327921d8eed86641efafd350e1df1"},
+ {file = "coverage-7.6.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7b15f589593110ae767ce997775d645b47e5cbbf54fd322f8ebea6277466cec"},
+ {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:44349150f6811b44b25574839b39ae35291f6496eb795b7366fef3bd3cf112d3"},
+ {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d891c136b5b310d0e702e186d70cd16d1119ea8927347045124cb286b29297e5"},
+ {file = "coverage-7.6.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:db1dab894cc139f67822a92910466531de5ea6034ddfd2b11c0d4c6257168073"},
+ {file = "coverage-7.6.9-cp310-cp310-win32.whl", hash = "sha256:41ff7b0da5af71a51b53f501a3bac65fb0ec311ebed1632e58fc6107f03b9198"},
+ {file = "coverage-7.6.9-cp310-cp310-win_amd64.whl", hash = "sha256:35371f8438028fdccfaf3570b31d98e8d9eda8bb1d6ab9473f5a390969e98717"},
+ {file = "coverage-7.6.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:932fc826442132dde42ee52cf66d941f581c685a6313feebed358411238f60f9"},
+ {file = "coverage-7.6.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:085161be5f3b30fd9b3e7b9a8c301f935c8313dcf928a07b116324abea2c1c2c"},
+ {file = "coverage-7.6.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ccc660a77e1c2bf24ddbce969af9447a9474790160cfb23de6be4fa88e3951c7"},
+ {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c69e42c892c018cd3c8d90da61d845f50a8243062b19d228189b0224150018a9"},
+ {file = "coverage-7.6.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0824a28ec542a0be22f60c6ac36d679e0e262e5353203bea81d44ee81fe9c6d4"},
+ {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4401ae5fc52ad8d26d2a5d8a7428b0f0c72431683f8e63e42e70606374c311a1"},
+ {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:98caba4476a6c8d59ec1eb00c7dd862ba9beca34085642d46ed503cc2d440d4b"},
+ {file = "coverage-7.6.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee5defd1733fd6ec08b168bd4f5387d5b322f45ca9e0e6c817ea6c4cd36313e3"},
+ {file = "coverage-7.6.9-cp311-cp311-win32.whl", hash = "sha256:f2d1ec60d6d256bdf298cb86b78dd715980828f50c46701abc3b0a2b3f8a0dc0"},
+ {file = "coverage-7.6.9-cp311-cp311-win_amd64.whl", hash = "sha256:0d59fd927b1f04de57a2ba0137166d31c1a6dd9e764ad4af552912d70428c92b"},
+ {file = "coverage-7.6.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:99e266ae0b5d15f1ca8d278a668df6f51cc4b854513daab5cae695ed7b721cf8"},
+ {file = "coverage-7.6.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9901d36492009a0a9b94b20e52ebfc8453bf49bb2b27bca2c9706f8b4f5a554a"},
+ {file = "coverage-7.6.9-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abd3e72dd5b97e3af4246cdada7738ef0e608168de952b837b8dd7e90341f015"},
+ {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff74026a461eb0660366fb01c650c1d00f833a086b336bdad7ab00cc952072b3"},
+ {file = "coverage-7.6.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65dad5a248823a4996724a88eb51d4b31587aa7aa428562dbe459c684e5787ae"},
+ {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:22be16571504c9ccea919fcedb459d5ab20d41172056206eb2994e2ff06118a4"},
+ {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f957943bc718b87144ecaee70762bc2bc3f1a7a53c7b861103546d3a403f0a6"},
+ {file = "coverage-7.6.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0ae1387db4aecb1f485fb70a6c0148c6cdaebb6038f1d40089b1fc84a5db556f"},
+ {file = "coverage-7.6.9-cp312-cp312-win32.whl", hash = "sha256:1a330812d9cc7ac2182586f6d41b4d0fadf9be9049f350e0efb275c8ee8eb692"},
+ {file = "coverage-7.6.9-cp312-cp312-win_amd64.whl", hash = "sha256:b12c6b18269ca471eedd41c1b6a1065b2f7827508edb9a7ed5555e9a56dcfc97"},
+ {file = "coverage-7.6.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:899b8cd4781c400454f2f64f7776a5d87bbd7b3e7f7bda0cb18f857bb1334664"},
+ {file = "coverage-7.6.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:61f70dc68bd36810972e55bbbe83674ea073dd1dcc121040a08cdf3416c5349c"},
+ {file = "coverage-7.6.9-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a289d23d4c46f1a82d5db4abeb40b9b5be91731ee19a379d15790e53031c014"},
+ {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e216d8044a356fc0337c7a2a0536d6de07888d7bcda76febcb8adc50bdbbd00"},
+ {file = "coverage-7.6.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c026eb44f744acaa2bda7493dad903aa5bf5fc4f2554293a798d5606710055d"},
+ {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e77363e8425325384f9d49272c54045bbed2f478e9dd698dbc65dbc37860eb0a"},
+ {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:777abfab476cf83b5177b84d7486497e034eb9eaea0d746ce0c1268c71652077"},
+ {file = "coverage-7.6.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:447af20e25fdbe16f26e84eb714ba21d98868705cb138252d28bc400381f6ffb"},
+ {file = "coverage-7.6.9-cp313-cp313-win32.whl", hash = "sha256:d872ec5aeb086cbea771c573600d47944eea2dcba8be5f3ee649bfe3cb8dc9ba"},
+ {file = "coverage-7.6.9-cp313-cp313-win_amd64.whl", hash = "sha256:fd1213c86e48dfdc5a0cc676551db467495a95a662d2396ecd58e719191446e1"},
+ {file = "coverage-7.6.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ba9e7484d286cd5a43744e5f47b0b3fb457865baf07bafc6bee91896364e1419"},
+ {file = "coverage-7.6.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e5ea1cf0872ee455c03e5674b5bca5e3e68e159379c1af0903e89f5eba9ccc3a"},
+ {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d10e07aa2b91835d6abec555ec8b2733347956991901eea6ffac295f83a30e4"},
+ {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13a9e2d3ee855db3dd6ea1ba5203316a1b1fd8eaeffc37c5b54987e61e4194ae"},
+ {file = "coverage-7.6.9-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c38bf15a40ccf5619fa2fe8f26106c7e8e080d7760aeccb3722664c8656b030"},
+ {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d5275455b3e4627c8e7154feaf7ee0743c2e7af82f6e3b561967b1cca755a0be"},
+ {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8f8770dfc6e2c6a2d4569f411015c8d751c980d17a14b0530da2d7f27ffdd88e"},
+ {file = "coverage-7.6.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8d2dfa71665a29b153a9681edb1c8d9c1ea50dfc2375fb4dac99ea7e21a0bcd9"},
+ {file = "coverage-7.6.9-cp313-cp313t-win32.whl", hash = "sha256:5e6b86b5847a016d0fbd31ffe1001b63355ed309651851295315031ea7eb5a9b"},
+ {file = "coverage-7.6.9-cp313-cp313t-win_amd64.whl", hash = "sha256:97ddc94d46088304772d21b060041c97fc16bdda13c6c7f9d8fcd8d5ae0d8611"},
+ {file = "coverage-7.6.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:adb697c0bd35100dc690de83154627fbab1f4f3c0386df266dded865fc50a902"},
+ {file = "coverage-7.6.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:be57b6d56e49c2739cdf776839a92330e933dd5e5d929966fbbd380c77f060be"},
+ {file = "coverage-7.6.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1592791f8204ae9166de22ba7e6705fa4ebd02936c09436a1bb85aabca3e599"},
+ {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4e12ae8cc979cf83d258acb5e1f1cf2f3f83524d1564a49d20b8bec14b637f08"},
+ {file = "coverage-7.6.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb5555cff66c4d3d6213a296b360f9e1a8e323e74e0426b6c10ed7f4d021e464"},
+ {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:b9389a429e0e5142e69d5bf4a435dd688c14478a19bb901735cdf75e57b13845"},
+ {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:592ac539812e9b46046620341498caf09ca21023c41c893e1eb9dbda00a70cbf"},
+ {file = "coverage-7.6.9-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a27801adef24cc30871da98a105f77995e13a25a505a0161911f6aafbd66e678"},
+ {file = "coverage-7.6.9-cp39-cp39-win32.whl", hash = "sha256:8e3c3e38930cfb729cb8137d7f055e5a473ddaf1217966aa6238c88bd9fd50e6"},
+ {file = "coverage-7.6.9-cp39-cp39-win_amd64.whl", hash = "sha256:e28bf44afa2b187cc9f41749138a64435bf340adfcacb5b2290c070ce99839d4"},
+ {file = "coverage-7.6.9-pp39.pp310-none-any.whl", hash = "sha256:f3ca78518bc6bc92828cd11867b121891d75cae4ea9e908d72030609b996db1b"},
+ {file = "coverage-7.6.9.tar.gz", hash = "sha256:4a8d8977b0c6ef5aeadcb644da9e69ae0dcfe66ec7f368c89c72e058bd71164d"},
]
[package.extras]
@@ -1116,37 +1117,37 @@ test = ["flake8", "isort", "pytest"]
[[package]]
name = "debugpy"
-version = "1.8.9"
+version = "1.8.11"
description = "An implementation of the Debug Adapter Protocol for Python"
optional = false
python-versions = ">=3.8"
files = [
- {file = "debugpy-1.8.9-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:cfe1e6c6ad7178265f74981edf1154ffce97b69005212fbc90ca22ddfe3d017e"},
- {file = "debugpy-1.8.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada7fb65102a4d2c9ab62e8908e9e9f12aed9d76ef44880367bc9308ebe49a0f"},
- {file = "debugpy-1.8.9-cp310-cp310-win32.whl", hash = "sha256:c36856343cbaa448171cba62a721531e10e7ffb0abff838004701454149bc037"},
- {file = "debugpy-1.8.9-cp310-cp310-win_amd64.whl", hash = "sha256:17c5e0297678442511cf00a745c9709e928ea4ca263d764e90d233208889a19e"},
- {file = "debugpy-1.8.9-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:b74a49753e21e33e7cf030883a92fa607bddc4ede1aa4145172debc637780040"},
- {file = "debugpy-1.8.9-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62d22dacdb0e296966d7d74a7141aaab4bec123fa43d1a35ddcb39bf9fd29d70"},
- {file = "debugpy-1.8.9-cp311-cp311-win32.whl", hash = "sha256:8138efff315cd09b8dcd14226a21afda4ca582284bf4215126d87342bba1cc66"},
- {file = "debugpy-1.8.9-cp311-cp311-win_amd64.whl", hash = "sha256:ff54ef77ad9f5c425398efb150239f6fe8e20c53ae2f68367eba7ece1e96226d"},
- {file = "debugpy-1.8.9-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:957363d9a7a6612a37458d9a15e72d03a635047f946e5fceee74b50d52a9c8e2"},
- {file = "debugpy-1.8.9-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e565fc54b680292b418bb809f1386f17081d1346dca9a871bf69a8ac4071afe"},
- {file = "debugpy-1.8.9-cp312-cp312-win32.whl", hash = "sha256:3e59842d6c4569c65ceb3751075ff8d7e6a6ada209ceca6308c9bde932bcef11"},
- {file = "debugpy-1.8.9-cp312-cp312-win_amd64.whl", hash = "sha256:66eeae42f3137eb428ea3a86d4a55f28da9bd5a4a3d369ba95ecc3a92c1bba53"},
- {file = "debugpy-1.8.9-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:957ecffff80d47cafa9b6545de9e016ae8c9547c98a538ee96ab5947115fb3dd"},
- {file = "debugpy-1.8.9-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1efbb3ff61487e2c16b3e033bc8595aea578222c08aaf3c4bf0f93fadbd662ee"},
- {file = "debugpy-1.8.9-cp313-cp313-win32.whl", hash = "sha256:7c4d65d03bee875bcb211c76c1d8f10f600c305dbd734beaed4077e902606fee"},
- {file = "debugpy-1.8.9-cp313-cp313-win_amd64.whl", hash = "sha256:e46b420dc1bea64e5bbedd678148be512442bc589b0111bd799367cde051e71a"},
- {file = "debugpy-1.8.9-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:472a3994999fe6c0756945ffa359e9e7e2d690fb55d251639d07208dbc37caea"},
- {file = "debugpy-1.8.9-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365e556a4772d7d0d151d7eb0e77ec4db03bcd95f26b67b15742b88cacff88e9"},
- {file = "debugpy-1.8.9-cp38-cp38-win32.whl", hash = "sha256:54a7e6d3014c408eb37b0b06021366ee985f1539e12fe49ca2ee0d392d9ceca5"},
- {file = "debugpy-1.8.9-cp38-cp38-win_amd64.whl", hash = "sha256:8e99c0b1cc7bf86d83fb95d5ccdc4ad0586d4432d489d1f54e4055bcc795f693"},
- {file = "debugpy-1.8.9-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:7e8b079323a56f719977fde9d8115590cb5e7a1cba2fcee0986ef8817116e7c1"},
- {file = "debugpy-1.8.9-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6953b335b804a41f16a192fa2e7851bdcfd92173cbb2f9f777bb934f49baab65"},
- {file = "debugpy-1.8.9-cp39-cp39-win32.whl", hash = "sha256:7e646e62d4602bb8956db88b1e72fe63172148c1e25c041e03b103a25f36673c"},
- {file = "debugpy-1.8.9-cp39-cp39-win_amd64.whl", hash = "sha256:3d9755e77a2d680ce3d2c5394a444cf42be4a592caaf246dbfbdd100ffcf7ae5"},
- {file = "debugpy-1.8.9-py2.py3-none-any.whl", hash = "sha256:cc37a6c9987ad743d9c3a14fa1b1a14b7e4e6041f9dd0c8abf8895fe7a97b899"},
- {file = "debugpy-1.8.9.zip", hash = "sha256:1339e14c7d980407248f09824d1b25ff5c5616651689f1e0f0e51bdead3ea13e"},
+ {file = "debugpy-1.8.11-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2b26fefc4e31ff85593d68b9022e35e8925714a10ab4858fb1b577a8a48cb8cd"},
+ {file = "debugpy-1.8.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61bc8b3b265e6949855300e84dc93d02d7a3a637f2aec6d382afd4ceb9120c9f"},
+ {file = "debugpy-1.8.11-cp310-cp310-win32.whl", hash = "sha256:c928bbf47f65288574b78518449edaa46c82572d340e2750889bbf8cd92f3737"},
+ {file = "debugpy-1.8.11-cp310-cp310-win_amd64.whl", hash = "sha256:8da1db4ca4f22583e834dcabdc7832e56fe16275253ee53ba66627b86e304da1"},
+ {file = "debugpy-1.8.11-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:85de8474ad53ad546ff1c7c7c89230db215b9b8a02754d41cb5a76f70d0be296"},
+ {file = "debugpy-1.8.11-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffc382e4afa4aee367bf413f55ed17bd91b191dcaf979890af239dda435f2a1"},
+ {file = "debugpy-1.8.11-cp311-cp311-win32.whl", hash = "sha256:40499a9979c55f72f4eb2fc38695419546b62594f8af194b879d2a18439c97a9"},
+ {file = "debugpy-1.8.11-cp311-cp311-win_amd64.whl", hash = "sha256:987bce16e86efa86f747d5151c54e91b3c1e36acc03ce1ddb50f9d09d16ded0e"},
+ {file = "debugpy-1.8.11-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:84e511a7545d11683d32cdb8f809ef63fc17ea2a00455cc62d0a4dbb4ed1c308"},
+ {file = "debugpy-1.8.11-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce291a5aca4985d82875d6779f61375e959208cdf09fcec40001e65fb0a54768"},
+ {file = "debugpy-1.8.11-cp312-cp312-win32.whl", hash = "sha256:28e45b3f827d3bf2592f3cf7ae63282e859f3259db44ed2b129093ca0ac7940b"},
+ {file = "debugpy-1.8.11-cp312-cp312-win_amd64.whl", hash = "sha256:44b1b8e6253bceada11f714acf4309ffb98bfa9ac55e4fce14f9e5d4484287a1"},
+ {file = "debugpy-1.8.11-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:8988f7163e4381b0da7696f37eec7aca19deb02e500245df68a7159739bbd0d3"},
+ {file = "debugpy-1.8.11-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c1f6a173d1140e557347419767d2b14ac1c9cd847e0b4c5444c7f3144697e4e"},
+ {file = "debugpy-1.8.11-cp313-cp313-win32.whl", hash = "sha256:bb3b15e25891f38da3ca0740271e63ab9db61f41d4d8541745cfc1824252cb28"},
+ {file = "debugpy-1.8.11-cp313-cp313-win_amd64.whl", hash = "sha256:d8768edcbeb34da9e11bcb8b5c2e0958d25218df7a6e56adf415ef262cd7b6d1"},
+ {file = "debugpy-1.8.11-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:ad7efe588c8f5cf940f40c3de0cd683cc5b76819446abaa50dc0829a30c094db"},
+ {file = "debugpy-1.8.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:189058d03a40103a57144752652b3ab08ff02b7595d0ce1f651b9acc3a3a35a0"},
+ {file = "debugpy-1.8.11-cp38-cp38-win32.whl", hash = "sha256:32db46ba45849daed7ccf3f2e26f7a386867b077f39b2a974bb5c4c2c3b0a280"},
+ {file = "debugpy-1.8.11-cp38-cp38-win_amd64.whl", hash = "sha256:116bf8342062246ca749013df4f6ea106f23bc159305843491f64672a55af2e5"},
+ {file = "debugpy-1.8.11-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:654130ca6ad5de73d978057eaf9e582244ff72d4574b3e106fb8d3d2a0d32458"},
+ {file = "debugpy-1.8.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23dc34c5e03b0212fa3c49a874df2b8b1b8fda95160bd79c01eb3ab51ea8d851"},
+ {file = "debugpy-1.8.11-cp39-cp39-win32.whl", hash = "sha256:52d8a3166c9f2815bfae05f386114b0b2d274456980d41f320299a8d9a5615a7"},
+ {file = "debugpy-1.8.11-cp39-cp39-win_amd64.whl", hash = "sha256:52c3cf9ecda273a19cc092961ee34eb9ba8687d67ba34cc7b79a521c1c64c4c0"},
+ {file = "debugpy-1.8.11-py2.py3-none-any.whl", hash = "sha256:0e22f846f4211383e6a416d04b4c13ed174d24cc5d43f5fd52e7821d0ebc8920"},
+ {file = "debugpy-1.8.11.tar.gz", hash = "sha256:6ad2688b69235c43b020e04fecccdf6a96c8943ca9c2fb340b8adc103c655e57"},
]
[[package]]
@@ -1315,13 +1316,13 @@ files = [
[[package]]
name = "django-silk"
-version = "5.3.1"
+version = "5.3.2"
description = "Silky smooth profiling for the Django Framework"
optional = false
python-versions = ">=3.9"
files = [
- {file = "django_silk-5.3.1-py3-none-any.whl", hash = "sha256:7834580fabea5d9e8a32eabb0d9cb061cc37f0ec057d2933b4da761a53ae1bed"},
- {file = "django_silk-5.3.1.tar.gz", hash = "sha256:aa4ae73a90fcbd5159a810f81e15a0ec010619beab37c82957eb4012fd0016f0"},
+ {file = "django_silk-5.3.2-py3-none-any.whl", hash = "sha256:49f1caebfda28b1707f0cfef524e0476beb82b8c5e40f5ccff7f73a6b4f6d3ac"},
+ {file = "django_silk-5.3.2.tar.gz", hash = "sha256:b0db54eebedb8d16f572321bd6daccac0bd3f547ae2618bb45d96fe8fc02229d"},
]
[package.dependencies]
@@ -1527,12 +1528,13 @@ validation = ["swagger-spec-validator (>=2.1.0)"]
[[package]]
name = "editorconfig"
-version = "0.12.4"
+version = "0.17.0"
description = "EditorConfig File Locator and Interpreter for Python"
optional = false
python-versions = "*"
files = [
- {file = "EditorConfig-0.12.4.tar.gz", hash = "sha256:24857fa1793917dd9ccf0c7810a07e05404ce9b823521c7dce22a4fb5d125f80"},
+ {file = "EditorConfig-0.17.0-py3-none-any.whl", hash = "sha256:fe491719c5f65959ec00b167d07740e7ffec9a3f362038c72b289330b9991dfc"},
+ {file = "editorconfig-0.17.0.tar.gz", hash = "sha256:8739052279699840065d3a9f5c125d7d5a98daeefe53b0e5274261d77cb49aa2"},
]
[[package]]
@@ -2305,13 +2307,13 @@ requests = "*"
[[package]]
name = "mkdocs-material"
-version = "9.5.47"
+version = "9.5.49"
description = "Documentation that simply works"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mkdocs_material-9.5.47-py3-none-any.whl", hash = "sha256:53fb9c9624e7865da6ec807d116cd7be24b3cb36ab31b1d1d1a9af58c56009a2"},
- {file = "mkdocs_material-9.5.47.tar.gz", hash = "sha256:fc3b7a8e00ad896660bd3a5cc12ca0cb28bdc2bcbe2a946b5714c23ac91b0ede"},
+ {file = "mkdocs_material-9.5.49-py3-none-any.whl", hash = "sha256:c3c2d8176b18198435d3a3e119011922f3e11424074645c24019c2dcf08a360e"},
+ {file = "mkdocs_material-9.5.49.tar.gz", hash = "sha256:3671bb282b4f53a1c72e08adbe04d2481a98f85fed392530051f80ff94a9621d"},
]
[package.dependencies]
@@ -2466,13 +2468,13 @@ typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.12\""}
[[package]]
name = "mypy-boto3-sesv2"
-version = "1.35.53"
-description = "Type annotations for boto3.SESV2 1.35.53 service generated with mypy-boto3-builder 8.1.4"
+version = "1.35.79"
+description = "Type annotations for boto3 SESV2 1.35.79 service generated with mypy-boto3-builder 8.6.3"
optional = false
python-versions = ">=3.8"
files = [
- {file = "mypy_boto3_sesv2-1.35.53-py3-none-any.whl", hash = "sha256:7fdb2cf32dad7e5283d83c75e5a330c1eca3e76a2bab5ded0bea3e5c0d334178"},
- {file = "mypy_boto3_sesv2-1.35.53.tar.gz", hash = "sha256:8c8c4515c5d8260c311fbf209cc1f121a416785f3c0d3eed1f5a6e9141290dce"},
+ {file = "mypy_boto3_sesv2-1.35.79-py3-none-any.whl", hash = "sha256:b155fe18884e67d96957717f9772821a8b0bf95e59c0d55268a4327dc20d4036"},
+ {file = "mypy_boto3_sesv2-1.35.79.tar.gz", hash = "sha256:27bb00bc0c09f6abd7a9752ab004eda82a0fc2c6268fdbc920448ad1083f46c1"},
]
[package.dependencies]
@@ -2796,12 +2798,12 @@ files = [
[[package]]
name = "pulumi"
-version = "3.142.0"
+version = "3.143.0"
description = "Pulumi's Python SDK"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pulumi-3.142.0-py3-none-any.whl", hash = "sha256:659a58a26aa36f585046750f01c3f20113c71fa065ee5a4487c775a9ef98b9b2"},
+ {file = "pulumi-3.143.0-py3-none-any.whl", hash = "sha256:04d20d019b8ff2e3f9492c7bf1ca11262e6dd68c4395d2c0ed669cfa7860f1d2"},
]
[package.dependencies]
@@ -2815,13 +2817,13 @@ six = ">=1.12,<2.0"
[[package]]
name = "pulumi-aws"
-version = "6.63.0"
+version = "6.65.0"
description = "A Pulumi package for creating and managing Amazon Web Services (AWS) cloud resources."
optional = false
python-versions = ">=3.8"
files = [
- {file = "pulumi_aws-6.63.0-py3-none-any.whl", hash = "sha256:5d4f3b6034cc3fc2ca2b0137d4cb668a52116b37215ab79d5872a66ffae0e664"},
- {file = "pulumi_aws-6.63.0.tar.gz", hash = "sha256:51b36a36c2bf9876f088e4ed7283b01f56e2d5a38d5a809e61992a2617030ff2"},
+ {file = "pulumi_aws-6.65.0-py3-none-any.whl", hash = "sha256:1472b33a92504fa9758a02db35cfd3cfb5d34e4913fd0edc4dac9f8cf5c44938"},
+ {file = "pulumi_aws-6.65.0.tar.gz", hash = "sha256:2cdd5750a2827b1e887aad921391f7914b3e493c2fe5d12fbd1f835254ac17ed"},
]
[package.dependencies]
@@ -2854,18 +2856,18 @@ files = [
[[package]]
name = "pydantic"
-version = "2.10.3"
+version = "2.10.4"
description = "Data validation using Python type hints"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pydantic-2.10.3-py3-none-any.whl", hash = "sha256:be04d85bbc7b65651c5f8e6b9976ed9c6f41782a55524cef079a34a0bb82144d"},
- {file = "pydantic-2.10.3.tar.gz", hash = "sha256:cb5ac360ce894ceacd69c403187900a02c4b20b693a9dd1d643e1effab9eadf9"},
+ {file = "pydantic-2.10.4-py3-none-any.whl", hash = "sha256:597e135ea68be3a37552fb524bc7d0d66dcf93d395acd93a00682f1efcb8ee3d"},
+ {file = "pydantic-2.10.4.tar.gz", hash = "sha256:82f12e9723da6de4fe2ba888b5971157b3be7ad914267dea8f05f82b28254f06"},
]
[package.dependencies]
annotated-types = ">=0.6.0"
-pydantic-core = "2.27.1"
+pydantic-core = "2.27.2"
typing-extensions = ">=4.12.2"
[package.extras]
@@ -2874,111 +2876,111 @@ timezone = ["tzdata"]
[[package]]
name = "pydantic-core"
-version = "2.27.1"
+version = "2.27.2"
description = "Core functionality for Pydantic validation and serialization"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pydantic_core-2.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71a5e35c75c021aaf400ac048dacc855f000bdfed91614b4a726f7432f1f3d6a"},
- {file = "pydantic_core-2.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f82d068a2d6ecfc6e054726080af69a6764a10015467d7d7b9f66d6ed5afa23b"},
- {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:121ceb0e822f79163dd4699e4c54f5ad38b157084d97b34de8b232bcaad70278"},
- {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4603137322c18eaf2e06a4495f426aa8d8388940f3c457e7548145011bb68e05"},
- {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a33cd6ad9017bbeaa9ed78a2e0752c5e250eafb9534f308e7a5f7849b0b1bfb4"},
- {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15cc53a3179ba0fcefe1e3ae50beb2784dede4003ad2dfd24f81bba4b23a454f"},
- {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45d9c5eb9273aa50999ad6adc6be5e0ecea7e09dbd0d31bd0c65a55a2592ca08"},
- {file = "pydantic_core-2.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8bf7b66ce12a2ac52d16f776b31d16d91033150266eb796967a7e4621707e4f6"},
- {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:655d7dd86f26cb15ce8a431036f66ce0318648f8853d709b4167786ec2fa4807"},
- {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:5556470f1a2157031e676f776c2bc20acd34c1990ca5f7e56f1ebf938b9ab57c"},
- {file = "pydantic_core-2.27.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f69ed81ab24d5a3bd93861c8c4436f54afdf8e8cc421562b0c7504cf3be58206"},
- {file = "pydantic_core-2.27.1-cp310-none-win32.whl", hash = "sha256:f5a823165e6d04ccea61a9f0576f345f8ce40ed533013580e087bd4d7442b52c"},
- {file = "pydantic_core-2.27.1-cp310-none-win_amd64.whl", hash = "sha256:57866a76e0b3823e0b56692d1a0bf722bffb324839bb5b7226a7dbd6c9a40b17"},
- {file = "pydantic_core-2.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac3b20653bdbe160febbea8aa6c079d3df19310d50ac314911ed8cc4eb7f8cb8"},
- {file = "pydantic_core-2.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a5a8e19d7c707c4cadb8c18f5f60c843052ae83c20fa7d44f41594c644a1d330"},
- {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f7059ca8d64fea7f238994c97d91f75965216bcbe5f695bb44f354893f11d52"},
- {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bed0f8a0eeea9fb72937ba118f9db0cb7e90773462af7962d382445f3005e5a4"},
- {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a3cb37038123447cf0f3ea4c74751f6a9d7afef0eb71aa07bf5f652b5e6a132c"},
- {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:84286494f6c5d05243456e04223d5a9417d7f443c3b76065e75001beb26f88de"},
- {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acc07b2cfc5b835444b44a9956846b578d27beeacd4b52e45489e93276241025"},
- {file = "pydantic_core-2.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fefee876e07a6e9aad7a8c8c9f85b0cdbe7df52b8a9552307b09050f7512c7e"},
- {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:258c57abf1188926c774a4c94dd29237e77eda19462e5bb901d88adcab6af919"},
- {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:35c14ac45fcfdf7167ca76cc80b2001205a8d5d16d80524e13508371fb8cdd9c"},
- {file = "pydantic_core-2.27.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d1b26e1dff225c31897696cab7d4f0a315d4c0d9e8666dbffdb28216f3b17fdc"},
- {file = "pydantic_core-2.27.1-cp311-none-win32.whl", hash = "sha256:2cdf7d86886bc6982354862204ae3b2f7f96f21a3eb0ba5ca0ac42c7b38598b9"},
- {file = "pydantic_core-2.27.1-cp311-none-win_amd64.whl", hash = "sha256:3af385b0cee8df3746c3f406f38bcbfdc9041b5c2d5ce3e5fc6637256e60bbc5"},
- {file = "pydantic_core-2.27.1-cp311-none-win_arm64.whl", hash = "sha256:81f2ec23ddc1b476ff96563f2e8d723830b06dceae348ce02914a37cb4e74b89"},
- {file = "pydantic_core-2.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9cbd94fc661d2bab2bc702cddd2d3370bbdcc4cd0f8f57488a81bcce90c7a54f"},
- {file = "pydantic_core-2.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5f8c4718cd44ec1580e180cb739713ecda2bdee1341084c1467802a417fe0f02"},
- {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15aae984e46de8d376df515f00450d1522077254ef6b7ce189b38ecee7c9677c"},
- {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ba5e3963344ff25fc8c40da90f44b0afca8cfd89d12964feb79ac1411a260ac"},
- {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:992cea5f4f3b29d6b4f7f1726ed8ee46c8331c6b4eed6db5b40134c6fe1768bb"},
- {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0325336f348dbee6550d129b1627cb8f5351a9dc91aad141ffb96d4937bd9529"},
- {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7597c07fbd11515f654d6ece3d0e4e5093edc30a436c63142d9a4b8e22f19c35"},
- {file = "pydantic_core-2.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3bbd5d8cc692616d5ef6fbbbd50dbec142c7e6ad9beb66b78a96e9c16729b089"},
- {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:dc61505e73298a84a2f317255fcc72b710b72980f3a1f670447a21efc88f8381"},
- {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:e1f735dc43da318cad19b4173dd1ffce1d84aafd6c9b782b3abc04a0d5a6f5bb"},
- {file = "pydantic_core-2.27.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f4e5658dbffe8843a0f12366a4c2d1c316dbe09bb4dfbdc9d2d9cd6031de8aae"},
- {file = "pydantic_core-2.27.1-cp312-none-win32.whl", hash = "sha256:672ebbe820bb37988c4d136eca2652ee114992d5d41c7e4858cdd90ea94ffe5c"},
- {file = "pydantic_core-2.27.1-cp312-none-win_amd64.whl", hash = "sha256:66ff044fd0bb1768688aecbe28b6190f6e799349221fb0de0e6f4048eca14c16"},
- {file = "pydantic_core-2.27.1-cp312-none-win_arm64.whl", hash = "sha256:9a3b0793b1bbfd4146304e23d90045f2a9b5fd5823aa682665fbdaf2a6c28f3e"},
- {file = "pydantic_core-2.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f216dbce0e60e4d03e0c4353c7023b202d95cbaeff12e5fd2e82ea0a66905073"},
- {file = "pydantic_core-2.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a2e02889071850bbfd36b56fd6bc98945e23670773bc7a76657e90e6b6603c08"},
- {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42b0e23f119b2b456d07ca91b307ae167cc3f6c846a7b169fca5326e32fdc6cf"},
- {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:764be71193f87d460a03f1f7385a82e226639732214b402f9aa61f0d025f0737"},
- {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1c00666a3bd2f84920a4e94434f5974d7bbc57e461318d6bb34ce9cdbbc1f6b2"},
- {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ccaa88b24eebc0f849ce0a4d09e8a408ec5a94afff395eb69baf868f5183107"},
- {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c65af9088ac534313e1963443d0ec360bb2b9cba6c2909478d22c2e363d98a51"},
- {file = "pydantic_core-2.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206b5cf6f0c513baffaeae7bd817717140770c74528f3e4c3e1cec7871ddd61a"},
- {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:062f60e512fc7fff8b8a9d680ff0ddaaef0193dba9fa83e679c0c5f5fbd018bc"},
- {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:a0697803ed7d4af5e4c1adf1670af078f8fcab7a86350e969f454daf598c4960"},
- {file = "pydantic_core-2.27.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:58ca98a950171f3151c603aeea9303ef6c235f692fe555e883591103da709b23"},
- {file = "pydantic_core-2.27.1-cp313-none-win32.whl", hash = "sha256:8065914ff79f7eab1599bd80406681f0ad08f8e47c880f17b416c9f8f7a26d05"},
- {file = "pydantic_core-2.27.1-cp313-none-win_amd64.whl", hash = "sha256:ba630d5e3db74c79300d9a5bdaaf6200172b107f263c98a0539eeecb857b2337"},
- {file = "pydantic_core-2.27.1-cp313-none-win_arm64.whl", hash = "sha256:45cf8588c066860b623cd11c4ba687f8d7175d5f7ef65f7129df8a394c502de5"},
- {file = "pydantic_core-2.27.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:5897bec80a09b4084aee23f9b73a9477a46c3304ad1d2d07acca19723fb1de62"},
- {file = "pydantic_core-2.27.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d0165ab2914379bd56908c02294ed8405c252250668ebcb438a55494c69f44ab"},
- {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b9af86e1d8e4cfc82c2022bfaa6f459381a50b94a29e95dcdda8442d6d83864"},
- {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f6c8a66741c5f5447e047ab0ba7a1c61d1e95580d64bce852e3df1f895c4067"},
- {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a42d6a8156ff78981f8aa56eb6394114e0dedb217cf8b729f438f643608cbcd"},
- {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64c65f40b4cd8b0e049a8edde07e38b476da7e3aaebe63287c899d2cff253fa5"},
- {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdcf339322a3fae5cbd504edcefddd5a50d9ee00d968696846f089b4432cf78"},
- {file = "pydantic_core-2.27.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bf99c8404f008750c846cb4ac4667b798a9f7de673ff719d705d9b2d6de49c5f"},
- {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8f1edcea27918d748c7e5e4d917297b2a0ab80cad10f86631e488b7cddf76a36"},
- {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:159cac0a3d096f79ab6a44d77a961917219707e2a130739c64d4dd46281f5c2a"},
- {file = "pydantic_core-2.27.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:029d9757eb621cc6e1848fa0b0310310de7301057f623985698ed7ebb014391b"},
- {file = "pydantic_core-2.27.1-cp38-none-win32.whl", hash = "sha256:a28af0695a45f7060e6f9b7092558a928a28553366519f64083c63a44f70e618"},
- {file = "pydantic_core-2.27.1-cp38-none-win_amd64.whl", hash = "sha256:2d4567c850905d5eaaed2f7a404e61012a51caf288292e016360aa2b96ff38d4"},
- {file = "pydantic_core-2.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e9386266798d64eeb19dd3677051f5705bf873e98e15897ddb7d76f477131967"},
- {file = "pydantic_core-2.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4228b5b646caa73f119b1ae756216b59cc6e2267201c27d3912b592c5e323b60"},
- {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b3dfe500de26c52abe0477dde16192ac39c98f05bf2d80e76102d394bd13854"},
- {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:aee66be87825cdf72ac64cb03ad4c15ffef4143dbf5c113f64a5ff4f81477bf9"},
- {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b748c44bb9f53031c8cbc99a8a061bc181c1000c60a30f55393b6e9c45cc5bd"},
- {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ca038c7f6a0afd0b2448941b6ef9d5e1949e999f9e5517692eb6da58e9d44be"},
- {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e0bd57539da59a3e4671b90a502da9a28c72322a4f17866ba3ac63a82c4498e"},
- {file = "pydantic_core-2.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ac6c2c45c847bbf8f91930d88716a0fb924b51e0c6dad329b793d670ec5db792"},
- {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b94d4ba43739bbe8b0ce4262bcc3b7b9f31459ad120fb595627eaeb7f9b9ca01"},
- {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:00e6424f4b26fe82d44577b4c842d7df97c20be6439e8e685d0d715feceb9fb9"},
- {file = "pydantic_core-2.27.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:38de0a70160dd97540335b7ad3a74571b24f1dc3ed33f815f0880682e6880131"},
- {file = "pydantic_core-2.27.1-cp39-none-win32.whl", hash = "sha256:7ccebf51efc61634f6c2344da73e366c75e735960b5654b63d7e6f69a5885fa3"},
- {file = "pydantic_core-2.27.1-cp39-none-win_amd64.whl", hash = "sha256:a57847b090d7892f123726202b7daa20df6694cbd583b67a592e856bff603d6c"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3fa80ac2bd5856580e242dbc202db873c60a01b20309c8319b5c5986fbe53ce6"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d950caa237bb1954f1b8c9227b5065ba6875ac9771bb8ec790d956a699b78676"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e4216e64d203e39c62df627aa882f02a2438d18a5f21d7f721621f7a5d3611d"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02a3d637bd387c41d46b002f0e49c52642281edacd2740e5a42f7017feea3f2c"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:161c27ccce13b6b0c8689418da3885d3220ed2eae2ea5e9b2f7f3d48f1d52c27"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19910754e4cc9c63bc1c7f6d73aa1cfee82f42007e407c0f413695c2f7ed777f"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:e173486019cc283dc9778315fa29a363579372fe67045e971e89b6365cc035ed"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:af52d26579b308921b73b956153066481f064875140ccd1dfd4e77db89dbb12f"},
- {file = "pydantic_core-2.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:981fb88516bd1ae8b0cbbd2034678a39dedc98752f264ac9bc5839d3923fa04c"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5fde892e6c697ce3e30c61b239330fc5d569a71fefd4eb6512fc6caec9dd9e2f"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:816f5aa087094099fff7edabb5e01cc370eb21aa1a1d44fe2d2aefdfb5599b31"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c10c309e18e443ddb108f0ef64e8729363adbfd92d6d57beec680f6261556f3"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98476c98b02c8e9b2eec76ac4156fd006628b1b2d0ef27e548ffa978393fd154"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c3027001c28434e7ca5a6e1e527487051136aa81803ac812be51802150d880dd"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7699b1df36a48169cdebda7ab5a2bac265204003f153b4bd17276153d997670a"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1c39b07d90be6b48968ddc8c19e7585052088fd7ec8d568bb31ff64c70ae3c97"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:46ccfe3032b3915586e469d4972973f893c0a2bb65669194a5bdea9bacc088c2"},
- {file = "pydantic_core-2.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:62ba45e21cf6571d7f716d903b5b7b6d2617e2d5d67c0923dc47b9d41369f840"},
- {file = "pydantic_core-2.27.1.tar.gz", hash = "sha256:62a763352879b84aa31058fc931884055fd75089cccbd9d58bb6afd01141b235"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-win32.whl", hash = "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4"},
+ {file = "pydantic_core-2.27.2-cp310-cp310-win_amd64.whl", hash = "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-win32.whl", hash = "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-win_amd64.whl", hash = "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc"},
+ {file = "pydantic_core-2.27.2-cp311-cp311-win_arm64.whl", hash = "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9"},
+ {file = "pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee"},
+ {file = "pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_armv7l.whl", hash = "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-win32.whl", hash = "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b"},
+ {file = "pydantic_core-2.27.2-cp38-cp38-win_amd64.whl", hash = "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-win32.whl", hash = "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e"},
+ {file = "pydantic_core-2.27.2-cp39-cp39-win_amd64.whl", hash = "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9"},
+ {file = "pydantic_core-2.27.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2"},
+ {file = "pydantic_core-2.27.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35"},
+ {file = "pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39"},
]
[package.dependencies]
@@ -2986,13 +2988,13 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]]
name = "pydantic-settings"
-version = "2.6.1"
+version = "2.7.0"
description = "Settings management using Pydantic"
optional = false
python-versions = ">=3.8"
files = [
- {file = "pydantic_settings-2.6.1-py3-none-any.whl", hash = "sha256:7fb0637c786a558d3103436278a7c4f1cfd29ba8973238a50c5bb9a55387da87"},
- {file = "pydantic_settings-2.6.1.tar.gz", hash = "sha256:e0f92546d8a9923cb8941689abf85d6601a8c19a23e97a34b2964a2e3f813ca0"},
+ {file = "pydantic_settings-2.7.0-py3-none-any.whl", hash = "sha256:e00c05d5fa6cbbb227c84bd7487c5c1065084119b750df7c8c1a554aed236eb5"},
+ {file = "pydantic_settings-2.7.0.tar.gz", hash = "sha256:ac4bfd4a36831a48dbf8b2d9325425b549a0a6f18cea118436d728eb4f1c4d66"},
]
[package.dependencies]
@@ -3445,13 +3447,13 @@ prompt_toolkit = ">=2.0,<=3.0.36"
[[package]]
name = "redis"
-version = "5.2.0"
+version = "5.2.1"
description = "Python client for Redis database and key-value store"
optional = false
python-versions = ">=3.8"
files = [
- {file = "redis-5.2.0-py3-none-any.whl", hash = "sha256:ae174f2bb3b1bf2b09d54bf3e51fbc1469cf6c10aa03e21141f51969801a7897"},
- {file = "redis-5.2.0.tar.gz", hash = "sha256:0b1087665a771b1ff2e003aa5bdd354f15a70c9e25d5a7dbf9c722c16528a7b0"},
+ {file = "redis-5.2.1-py3-none-any.whl", hash = "sha256:ee7e1056b9aea0f04c6c2ed59452947f34c4940ee025f5dd83e6a6418b6989e4"},
+ {file = "redis-5.2.1.tar.gz", hash = "sha256:16f2e22dff21d5125e8481515e386711a34cbec50f0e44413dd7d9c060a54e0f"},
]
[package.dependencies]
@@ -3782,13 +3784,13 @@ saml = ["python3-saml (>=1.5.0)"]
[[package]]
name = "sqlparse"
-version = "0.5.2"
+version = "0.5.3"
description = "A non-validating SQL parser."
optional = false
python-versions = ">=3.8"
files = [
- {file = "sqlparse-0.5.2-py3-none-any.whl", hash = "sha256:e99bc85c78160918c3e1d9230834ab8d80fc06c59d03f8db2618f65f65dda55e"},
- {file = "sqlparse-0.5.2.tar.gz", hash = "sha256:9e37b35e16d1cc652a2545f0997c1deb23ea28fa1f3eefe609eee3063c3b105f"},
+ {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"},
+ {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"},
]
[package.extras]
@@ -3798,7 +3800,7 @@ doc = ["sphinx"]
[[package]]
name = "strelix-core"
version = "0.0.1"
-description = "github.com/Strelix/Core"
+description = "github.com/Strelix/core"
optional = false
python-versions = ">=3.10,<4.0"
files = []
@@ -3949,13 +3951,13 @@ telegram = ["requests"]
[[package]]
name = "types-awscrt"
-version = "0.23.3"
+version = "0.23.6"
description = "Type annotations and code completion for awscrt"
optional = false
python-versions = ">=3.8"
files = [
- {file = "types_awscrt-0.23.3-py3-none-any.whl", hash = "sha256:cc0057885cb7ce1e66856123a4c2861b051e9f0716b1767ad72bfe4ca26bbcd4"},
- {file = "types_awscrt-0.23.3.tar.gz", hash = "sha256:043c0ae0fe5d272618294cbeaf1c349a654a9f7c00121be64d27486933ac4a26"},
+ {file = "types_awscrt-0.23.6-py3-none-any.whl", hash = "sha256:fbf9c221af5607b24bf17f8431217ce8b9a27917139edbc984891eb63fd5a593"},
+ {file = "types_awscrt-0.23.6.tar.gz", hash = "sha256:405bce8c281f9e7c6c92a229225cc0bf10d30729a6a601123213389bd524b8b1"},
]
[[package]]
@@ -3996,13 +3998,13 @@ files = [
[[package]]
name = "types-protobuf"
-version = "5.28.3.20241203"
+version = "5.29.1.20241207"
description = "Typing stubs for protobuf"
optional = false
python-versions = ">=3.8"
files = [
- {file = "types_protobuf-5.28.3.20241203-py3-none-any.whl", hash = "sha256:5367632a4785394b0504e7c1e4d54a2dceeae9cd4f73a705d0f6499fc99cecb1"},
- {file = "types_protobuf-5.28.3.20241203.tar.gz", hash = "sha256:2e1c962bdf76c576506b5fc0678d28efa6652a54c04ae46562a6209e777bd789"},
+ {file = "types_protobuf-5.29.1.20241207-py3-none-any.whl", hash = "sha256:92893c42083e9b718c678badc0af7a9a1307b92afe1599e5cba5f3d35b668b2f"},
+ {file = "types_protobuf-5.29.1.20241207.tar.gz", hash = "sha256:2ebcadb8ab3ef2e3e2f067e0882906d64ba0dc65fc5b0fd7a8b692315b4a0be9"},
]
[[package]]
@@ -4018,13 +4020,13 @@ files = [
[[package]]
name = "types-pycurl"
-version = "7.45.3.20240421"
+version = "7.45.4.20241216"
description = "Typing stubs for pycurl"
optional = false
python-versions = ">=3.8"
files = [
- {file = "types-pycurl-7.45.3.20240421.tar.gz", hash = "sha256:0d5b3b9243e3940e26b782c1cdc0b2438d244ee2e8dcde09cf523635a42ff406"},
- {file = "types_pycurl-7.45.3.20240421-py3-none-any.whl", hash = "sha256:c20b56426cd67e4a80682d054391f6bab725d5fc31baae0c3eef9bbec00629c3"},
+ {file = "types_pycurl-7.45.4.20241216-py3-none-any.whl", hash = "sha256:d50eb912216ae2bab4f581bf44cd013f24002f001077a38fcc662a25b525342c"},
+ {file = "types_pycurl-7.45.4.20241216.tar.gz", hash = "sha256:ce5ad0978e09ef43ee97b54ac68c183eda27ee0c981f70ec3f1b867ff07611f4"},
]
[[package]]
@@ -4044,13 +4046,13 @@ types-setuptools = "*"
[[package]]
name = "types-python-dateutil"
-version = "2.9.0.20241003"
+version = "2.9.0.20241206"
description = "Typing stubs for python-dateutil"
optional = false
python-versions = ">=3.8"
files = [
- {file = "types-python-dateutil-2.9.0.20241003.tar.gz", hash = "sha256:58cb85449b2a56d6684e41aeefb4c4280631246a0da1a719bdbe6f3fb0317446"},
- {file = "types_python_dateutil-2.9.0.20241003-py3-none-any.whl", hash = "sha256:250e1d8e80e7bbc3a6c99b907762711d1a1cdd00e978ad39cb5940f6f0a87f3d"},
+ {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"},
+ {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"},
]
[[package]]
@@ -4135,21 +4137,21 @@ files = [
[[package]]
name = "typos"
-version = "1.28.2"
+version = "1.28.4"
description = "Source Code Spelling Correction"
optional = false
python-versions = ">=3.7"
files = [
- {file = "typos-1.28.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:2ce911fabe95b4900aa063c159aee3bb883c7412c058b161a7f15d3ae293dcc8"},
- {file = "typos-1.28.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:8643a81aec9d36a719b421350e7ab14c3db2441b4527a1ab146b7a1e98f7da27"},
- {file = "typos-1.28.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8299d3b89d44c0b544dc37edd419c2424ce743d6aaaa1df7bccac4f98fb1383"},
- {file = "typos-1.28.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b3441c505f31baff0fd8ed3938c6d26102b8842a3321b2eddd139e6563f77a5"},
- {file = "typos-1.28.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3cadf5c68d4818cedb0dde7ab8a07a7f02c4f184859a869871ec14ab4d710ad1"},
- {file = "typos-1.28.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0d1151b202ab11c0a75bd986a0e2afa28e516a7d6180e474a642ddc5d8437091"},
- {file = "typos-1.28.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:91eb18d188f9f62dba5c98d4d8d799b7160a98f2dd09d15153be8c993d17d1a9"},
- {file = "typos-1.28.2-py3-none-win32.whl", hash = "sha256:8af5d31dd79c3f84c161f8f0245224ed8523fee67107a13651a2257a7fc95ce8"},
- {file = "typos-1.28.2-py3-none-win_amd64.whl", hash = "sha256:74948f6c264181ee2881336dbb944dcbf2d168bee661d8d133fa9946f74a5098"},
- {file = "typos-1.28.2.tar.gz", hash = "sha256:c9adf7d20605fd59c27852050a4cb483dc2004fb61bca3f09768ca368d0308d6"},
+ {file = "typos-1.28.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:0bb4945868432848bdb378137411f0c5d6f7f763e4da64b84b037ad2392b45f8"},
+ {file = "typos-1.28.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54d507db5439e65ebb36a15551ba0fd23d317ed6d4212d0b866a5310a14d8841"},
+ {file = "typos-1.28.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bacfb01a2ab60b146f1412252f327e58e32a430613a761d76dbcc6e275ecffe3"},
+ {file = "typos-1.28.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3bc312a10df63211b4f8730d551bc086f71ec5fb7a0a587a50f16c3902edf76"},
+ {file = "typos-1.28.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f605b3bb8c928cc0a0d46b29335d400630d43da0a9977bc890987a6cc175420a"},
+ {file = "typos-1.28.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:d854c5f854304efb959d7fe56fef5720163738687a6db6232bbd951ee2190167"},
+ {file = "typos-1.28.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:ce87ddde847c535354dbe036691473fc6029f2c895f47340167874185b14bb29"},
+ {file = "typos-1.28.4-py3-none-win32.whl", hash = "sha256:92a1a2eaa88f682f9f23152d3f20c932d64e18390819e69b2d006c3e83afe3e3"},
+ {file = "typos-1.28.4-py3-none-win_amd64.whl", hash = "sha256:23dbb854917e4d8eaba6ff364d4849d09fc70faeb0f88bc35c814b622b6d045a"},
+ {file = "typos-1.28.4.tar.gz", hash = "sha256:7afd8ad79ab8b84f7adb12350d5630abc5e061c8a76802ddbc29eea256689600"},
]
[[package]]
@@ -4335,4 +4337,4 @@ test = ["coverage (>=5.3)", "tomli (>=2.0.1)", "tox"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<4.0"
-content-hash = "a41b52ec2a03574be2144fdf7e433f5202f60749c3694dab85677b6fd6a2beca"
+content-hash = "d9dc57b78282a853554f645aa31b18f7537a2ef7e6fb3f915d679a4e5d38c718"
diff --git a/pyproject.toml b/pyproject.toml
index f26ca8305..8c25d98fb 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -6,7 +6,6 @@ authors = ["TreyWW"]
readme = "README.md"
[tool.poetry.dependencies]
-strelix-core = { path = "../core/", develop = true }
python = ">=3.10,<4.0"
bleach = "6.1.0" # used for HTML sanitation
boto3 = "1.34.76" # AWS
@@ -24,6 +23,7 @@ drf-yasg = "^1.21.7"
setuptools = "^70.1.1"
xhtml2pdf = "^0.2.16"
stripe = "^10.8.0"
+strelix-core = {path = "E:/WebDev/core", develop = true}
[tool.poetry.group.mypy.dependencies]
mypy = "1.7.1"
django-stubs = { version = "4.2.7" }
diff --git a/backend/core/service/boto3/scheduler/__init__.py b/settings/__init__.py
similarity index 100%
rename from backend/core/service/boto3/scheduler/__init__.py
rename to settings/__init__.py
diff --git a/settings/helpers.py b/settings/helpers.py
index 2b225ad80..bedadfd2f 100644
--- a/settings/helpers.py
+++ b/settings/helpers.py
@@ -15,7 +15,7 @@
SendBulkEmailResponseTypeDef,
)
-from backend.core.types.emails import (
+from core.types.emails import (
SingleEmailInput,
BulkTemplatedEmailInput,
SingleTemplatedEmailContent,
diff --git a/settings/local_settings.py b/settings/local_settings.py
index 203805b82..cc995ea61 100644
--- a/settings/local_settings.py
+++ b/settings/local_settings.py
@@ -36,13 +36,14 @@
print(f"[BACKEND] Using {DB_TYPE} database: {os.environ.get('DATABASE_NAME')}")
else:
+ SQLITE_PATH = os.environ.get("SQLITE_PATH", BASE_DIR / "db.sqlite3")
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
- "NAME": BASE_DIR / "db.sqlite3",
+ "NAME": SQLITE_PATH,
}
}
- print("[BACKEND] Using sqlite3 database", flush=True)
+ print(f"[BACKEND] Using sqlite3 database with path {SQLITE_PATH}", flush=True)
local_allowed_host = os.environ.get("LOCAL_ALLOWED_HOST")
local_allowed_hosts = [host for host in [local_allowed_host] if host is not None]
diff --git a/settings/settings.py b/settings/settings.py
index bd163237b..681d3dc88 100644
--- a/settings/settings.py
+++ b/settings/settings.py
@@ -75,7 +75,7 @@
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": [
# "rest_framework.authentication.TokenAuthentication",
- "backend.core.api.public.authentication.CustomBearerAuthentication" # also adds custom model
+ "core.api.public.authentication.CustomBearerAuthentication" # also adds custom model
],
"DEFAULT_PERMISSION_CLASSES": [
"rest_framework.permissions.IsAuthenticated",
@@ -89,7 +89,7 @@
SWAGGER_SETTINGS = {
"USE_SESSION_AUTH": False,
- "DEFAULT_INFO": "backend.core.api.public.swagger_ui.INFO",
+ "DEFAULT_INFO": "core.api.public.swagger_ui.INFO",
"SECURITY_DEFINITIONS": {"Bearer": {"type": "apiKey", "name": "Authorization", "in": "header"}},
}
@@ -287,7 +287,7 @@
}
}
-AUTH_USER_MODEL = "backend.User"
+AUTH_USER_MODEL = "core.User"
LANGUAGE_CODE = "en-us"
@@ -323,7 +323,7 @@
# SOCIAL_AUTH_LOGIN_URL = "/login/external/"
# SOCIAL_AUTH_NEW_USER_REDIRECT_URL = "/login/external/new_user/"
# SOCIAL_AUTH_LOGIN_REDIRECT_URL = "/"
-SOCIAL_AUTH_USER_MODEL = "backend.User"
+SOCIAL_AUTH_USER_MODEL = "core.User"
AWS_TAGS_APP_NAME = get_var("AWS_TAGS_APP_NAME", default="myfinances")
diff --git a/tests/api/test_account_settings.py b/tests/api/test_account_settings.py
index 1a272ea1c..ebf11615d 100644
--- a/tests/api/test_account_settings.py
+++ b/tests/api/test_account_settings.py
@@ -10,7 +10,7 @@ def setUp(self):
super().setUp()
self.url_path = "/api/settings/account_preferences/"
self.url_name = "api:settings:account_preferences"
- self.view_function_path = "backend.core.api.settings.preferences.update_account_preferences"
+ self.view_function_path = "core.api.settings.preferences.update_account_preferences"
# Test that the URL resolves to the correct view function
def test_url_matches_api(self):
diff --git a/tests/handler.py b/tests/handler.py
index de5ea3d2a..5af52fdfd 100644
--- a/tests/handler.py
+++ b/tests/handler.py
@@ -17,7 +17,7 @@ def assert_url_matches_view(url_path, url_name, view_function_path):
Args:
url_path (str): The full URL path of the view. (e.g. api//clients/fetch/)
url_name (str): The name of the URL to reverse. (e.g. api:clients:fetch)
- view_function_path (str): The expected path of the view function (e.g., "backend.core.api.clients.fetch.fetch_all_clients").
+ view_function_path (str): The expected path of the view function (e.g., "core.api.clients.fetch.fetch_all_clients").
"""
resolved_func = resolve(url_path).func
resolved_func_name = f"{resolved_func.__module__}.{resolved_func.__name__}"
diff --git a/tests/views/test_other.py b/tests/views/test_other.py
deleted file mode 100644
index 0e9417715..000000000
--- a/tests/views/test_other.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from django.urls import reverse, resolve
-
-from backend.core.views.auth.login import logout_view
-from tests.handler import ViewTestCase
-
-
-class OtherItemsTestCase(ViewTestCase):
- def test_logout_from_url_not_logged_in_redirects(self):
- response = self.client.get(reverse("auth:logout"))
- self.assertEqual(response.status_code, 302)
- self.assertEqual(resolve(reverse("auth:logout")).func, logout_view)
-
- def test_logout_from_url_logged_in(self):
- self.login_user()
- response = self.client.get(reverse("auth:logout"))
- self.assertNotIn("_auth_user_id", self.client.session)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, reverse("auth:login"))
-
- def testlogin_view_matches_with_urls_view(self):
- # func = resolve("/login/").func
- # func_name = f"{func.__module__}.{func.__name__}"
- self.assertEqual(
- "/auth/login/",
- reverse("auth:login"),
- )
From fda48b3aeba28078f117e3e4c3447fe23d872a8c Mon Sep 17 00:00:00 2001
From: Trey <73353716+TreyWW@users.noreply.github.com>
Date: Sun, 22 Dec 2024 00:15:43 +0000
Subject: [PATCH 03/21] Removed migrations for a fresh start, added extended
templates, made a start to modal revamp
Signed-off-by: Trey <73353716+TreyWW@users.noreply.github.com>
---
backend/context_processors.py | 4 +-
backend/migrations/0001_initial.py | 749 +++++++++++-------
.../0002_alter_receipt_date_uploaded.py | 17 -
backend/migrations/0002_initial.py | 420 ++++++++++
...client_company_client_is_representative.py | 22 -
.../0004_invoice_client_is_representative.py | 17 -
backend/migrations/0005_invoiceproduct.py | 42 -
...erchant_store_receipt_purchase_category.py | 25 -
...7_alter_receipt_merchant_store_and_more.py | 22 -
.../migrations/0008_receiptdownloadtoken.py | 47 --
.../0009_alter_invoice_sort_code.py | 17 -
.../migrations/0010_user_logged_in_as_team.py | 23 -
backend/migrations/0011_alter_team_leader.py | 24 -
...rganization_alter_receipt_user_and_more.py | 44 -
...ganization_client_organization_and_more.py | 82 --
...ion_extra_type_notification_extra_value.py | 23 -
...alter_notification_user_alter_team_name.py | 29 -
...voice_logo_alter_receipt_image_and_more.py | 43 -
backend/migrations/0017_featureflags.py | 30 -
backend/migrations/0018_user_role.py | 27 -
...019_alter_featureflags_options_and_more.py | 70 --
...lter_verificationcodes_options_and_more.py | 32 -
...alter_verificationcodes_expiry_and_more.py | 25 -
...alter_verificationcodes_expiry_and_more.py | 30 -
.../0023_apikey_invoiceonetimeschedule.py | 64 --
...pire_invoiceurl_system_created_and_more.py | 30 -
...oiceonetimeschedule_stored_schedule_arn.py | 18 -
...ount_amount_invoice_discount_percentage.py | 26 -
backend/migrations/0027_invoice_currency.py | 30 -
...increaserequest_quotaoverrides_and_more.py | 111 ---
...rganization_alter_invoice_user_and_more.py | 30 -
.../migrations/0030_alter_invoice_items.py | 18 -
...ags_description_alter_featureflags_name.py | 25 -
...fied_alter_client_organization_and_more.py | 98 ---
.../0033_alter_auditlog_organization.py | 19 -
...il_quotaincreaserequest_reason_and_more.py | 66 --
.../migrations/0035_client_contact_method.py | 18 -
...036_alter_client_address_clientdefaults.py | 61 --
backend/migrations/0036_apiauthtoken.py | 23 -
.../migrations/0037_merge_20240619_2223.py | 13 -
.../0038_alter_apiauthtoken_options.py | 17 -
...ctive_apiauthtoken_description_and_more.py | 59 --
...token_scopes_apiauthtoken_team_and_more.py | 54 --
.../0041_alter_apiauthtoken_user.py | 20 -
...piauthtoken_key_apiauthtoken_hashed_key.py | 39 -
...ation_remove_apiauthtoken_team_and_more.py | 137 ----
...ltvalues_delete_clientdefaults_and_more.py | 88 --
.../0045_usersettings_disabled_features.py | 18 -
...ereminder_boto_schedule_status_and_more.py | 197 -----
...0047_defaultvalues_default_invoice_logo.py | 21 -
...lter_defaultvalues_default_invoice_logo.py | 21 -
backend/migrations/0049_filestoragefile.py | 60 --
backend/migrations/0050_multifileupload.py | 48 --
...p_subscriptionplan_planfeature_and_more.py | 173 ----
.../0052_filestoragefile_file_uri_path.py | 19 -
...ance_id_alter_planfeature_name_and_more.py | 46 --
.../0054_transferusage_storageusage.py | 57 --
.../0055_remove_planfeature_group_and_more.py | 89 ---
.../0056_user_stripe_customer_id.py | 18 -
backend/migrations/0057_user_entitlements.py | 18 -
...0058_organization_entitlements_and_more.py | 23 -
...voicerecurringprofile_managers_and_more.py | 89 ---
.../0060_user_require_change_password.py | 18 -
...ultvalues_invoice_from_address_and_more.py | 43 -
...es_invoice_account_holder_name_and_more.py | 28 -
...ing_invoices_invoice_cancelled_and_more.py | 36 -
...e_invoice_payment_status_invoice_status.py | 24 -
...r_expire_passwordsecret_active_and_more.py | 42 -
...emove_verificationcodes_expiry_and_more.py | 30 -
...67_remove_apiauthtoken_expired_and_more.py | 27 -
...d_at_invoice_status_updated_at_and_more.py | 36 -
.../migrations/0069_alter_auditlog_action.py | 18 -
...0070_remove_invoice_invoice_id_and_more.py | 34 -
backend/modals.py | 58 ++
backend/urls.py | 2 +-
frontend/templates/base/+left_drawer.html | 103 ++-
frontend/templates/base/_head.html | 71 +-
frontend/templates/base/auth.html | 38 +-
frontend/templates/base/base.html | 59 +-
frontend/templates/base/htmx.html | 17 +-
frontend/templates/base/topbar/_topbar.html | 117 +--
frontend/templates/pages/dashboard.html | 2 +-
settings/settings.py | 2 +-
83 files changed, 1032 insertions(+), 3598 deletions(-)
delete mode 100644 backend/migrations/0002_alter_receipt_date_uploaded.py
create mode 100644 backend/migrations/0002_initial.py
delete mode 100644 backend/migrations/0003_client_company_client_is_representative.py
delete mode 100644 backend/migrations/0004_invoice_client_is_representative.py
delete mode 100644 backend/migrations/0005_invoiceproduct.py
delete mode 100644 backend/migrations/0006_receipt_merchant_store_receipt_purchase_category.py
delete mode 100644 backend/migrations/0007_alter_receipt_merchant_store_and_more.py
delete mode 100644 backend/migrations/0008_receiptdownloadtoken.py
delete mode 100644 backend/migrations/0009_alter_invoice_sort_code.py
delete mode 100644 backend/migrations/0010_user_logged_in_as_team.py
delete mode 100644 backend/migrations/0011_alter_team_leader.py
delete mode 100644 backend/migrations/0012_receipt_organization_alter_receipt_user_and_more.py
delete mode 100644 backend/migrations/0013_auditlog_organization_client_organization_and_more.py
delete mode 100644 backend/migrations/0014_notification_extra_type_notification_extra_value.py
delete mode 100644 backend/migrations/0015_alter_notification_user_alter_team_name.py
delete mode 100644 backend/migrations/0016_alter_invoice_logo_alter_receipt_image_and_more.py
delete mode 100644 backend/migrations/0017_featureflags.py
delete mode 100644 backend/migrations/0018_user_role.py
delete mode 100644 backend/migrations/0019_alter_featureflags_options_and_more.py
delete mode 100644 backend/migrations/0020_alter_verificationcodes_options_and_more.py
delete mode 100644 backend/migrations/0021_alter_verificationcodes_expiry_and_more.py
delete mode 100644 backend/migrations/0022_loginlog_service_alter_verificationcodes_expiry_and_more.py
delete mode 100644 backend/migrations/0023_apikey_invoiceonetimeschedule.py
delete mode 100644 backend/migrations/0024_invoiceurl_never_expire_invoiceurl_system_created_and_more.py
delete mode 100644 backend/migrations/0025_alter_invoiceonetimeschedule_stored_schedule_arn.py
delete mode 100644 backend/migrations/0026_invoice_discount_amount_invoice_discount_percentage.py
delete mode 100644 backend/migrations/0027_invoice_currency.py
delete mode 100644 backend/migrations/0028_quotalimit_quotaincreaserequest_quotaoverrides_and_more.py
delete mode 100644 backend/migrations/0029_alter_invoice_organization_alter_invoice_user_and_more.py
delete mode 100644 backend/migrations/0030_alter_invoice_items.py
delete mode 100644 backend/migrations/0031_featureflags_description_alter_featureflags_name.py
delete mode 100644 backend/migrations/0032_client_email_verified_alter_client_organization_and_more.py
delete mode 100644 backend/migrations/0033_alter_auditlog_organization.py
delete mode 100644 backend/migrations/0034_invoice_client_email_quotaincreaserequest_reason_and_more.py
delete mode 100644 backend/migrations/0035_client_contact_method.py
delete mode 100644 backend/migrations/0036_alter_client_address_clientdefaults.py
delete mode 100644 backend/migrations/0036_apiauthtoken.py
delete mode 100644 backend/migrations/0037_merge_20240619_2223.py
delete mode 100644 backend/migrations/0038_alter_apiauthtoken_options.py
delete mode 100644 backend/migrations/0039_apiauthtoken_active_apiauthtoken_description_and_more.py
delete mode 100644 backend/migrations/0040_apiauthtoken_scopes_apiauthtoken_team_and_more.py
delete mode 100644 backend/migrations/0041_alter_apiauthtoken_user.py
delete mode 100644 backend/migrations/0042_remove_apiauthtoken_key_apiauthtoken_hashed_key.py
delete mode 100644 backend/migrations/0043_rename_team_organization_remove_apiauthtoken_team_and_more.py
delete mode 100644 backend/migrations/0044_defaultvalues_delete_clientdefaults_and_more.py
delete mode 100644 backend/migrations/0045_usersettings_disabled_features.py
delete mode 100644 backend/migrations/0046_rename_status_invoicereminder_boto_schedule_status_and_more.py
delete mode 100644 backend/migrations/0047_defaultvalues_default_invoice_logo.py
delete mode 100644 backend/migrations/0048_alter_defaultvalues_default_invoice_logo.py
delete mode 100644 backend/migrations/0049_filestoragefile.py
delete mode 100644 backend/migrations/0050_multifileupload.py
delete mode 100644 backend/migrations/0051_planfeaturegroup_subscriptionplan_planfeature_and_more.py
delete mode 100644 backend/migrations/0052_filestoragefile_file_uri_path.py
delete mode 100644 backend/migrations/0053_usage_instance_id_alter_planfeature_name_and_more.py
delete mode 100644 backend/migrations/0054_transferusage_storageusage.py
delete mode 100644 backend/migrations/0055_remove_planfeature_group_and_more.py
delete mode 100644 backend/migrations/0056_user_stripe_customer_id.py
delete mode 100644 backend/migrations/0057_user_entitlements.py
delete mode 100644 backend/migrations/0058_organization_entitlements_and_more.py
delete mode 100644 backend/migrations/0059_alter_invoicerecurringprofile_managers_and_more.py
delete mode 100644 backend/migrations/0060_user_require_change_password.py
delete mode 100644 backend/migrations/0061_defaultvalues_invoice_from_address_and_more.py
delete mode 100644 backend/migrations/0062_defaultvalues_invoice_account_holder_name_and_more.py
delete mode 100644 backend/migrations/0063_defaultvalues_email_template_recurring_invoices_invoice_cancelled_and_more.py
delete mode 100644 backend/migrations/0064_remove_invoice_payment_status_invoice_status.py
delete mode 100644 backend/migrations/0065_remove_invoiceurl_never_expire_passwordsecret_active_and_more.py
delete mode 100644 backend/migrations/0066_delete_apikey_remove_verificationcodes_expiry_and_more.py
delete mode 100644 backend/migrations/0067_remove_apiauthtoken_expired_and_more.py
delete mode 100644 backend/migrations/0068_invoice_created_at_invoice_status_updated_at_and_more.py
delete mode 100644 backend/migrations/0069_alter_auditlog_action.py
delete mode 100644 backend/migrations/0070_remove_invoice_invoice_id_and_more.py
create mode 100644 backend/modals.py
diff --git a/backend/context_processors.py b/backend/context_processors.py
index 5ba9e8f12..4dbac686c 100644
--- a/backend/context_processors.py
+++ b/backend/context_processors.py
@@ -63,7 +63,9 @@ def get_git_revision(base_path):
data["day_names_monday_first"] = [day for day in calendar.day_name]
if hasattr(request, "htmx") and request.htmx.boosted:
- data["base"] = "core/base/htmx.html"
+ data["base"] = "base/htmx.html"
+ else:
+ data["base"] = "base/base.html"
return data
diff --git a/backend/migrations/0001_initial.py b/backend/migrations/0001_initial.py
index b6572b794..a7107828c 100644
--- a/backend/migrations/0001_initial.py
+++ b/backend/migrations/0001_initial.py
@@ -1,24 +1,23 @@
-# Generated by Django 4.2.5 on 2023-11-25 20:13
+# Generated by Django 5.1.4 on 2024-12-21 22:26
-import django.contrib.auth.validators
-import django.utils.timezone
+import backend.data.default_email_templates
+import core.models
+import django.core.validators
+import django.db.models.manager
import shortuuid.django_fields
-from django.conf import settings
+import uuid
from django.db import migrations, models
-import backend.models
-
class Migration(migrations.Migration):
+
initial = True
- dependencies = [
- ("auth", "0012_alter_user_first_name_max_length"),
- ]
+ dependencies = []
operations = [
migrations.CreateModel(
- name="User",
+ name="Client",
fields=[
(
"id",
@@ -29,96 +28,155 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("password", models.CharField(max_length=128, verbose_name="password")),
+ ("active", models.BooleanField(default=True)),
+ ("name", models.CharField(max_length=64)),
(
- "last_login",
- models.DateTimeField(blank=True, null=True, verbose_name="last login"),
+ "phone_number",
+ models.CharField(blank=True, max_length=100, null=True),
),
+ ("email", models.EmailField(blank=True, max_length=254, null=True)),
+ ("email_verified", models.BooleanField(default=False)),
+ ("company", models.CharField(blank=True, max_length=100, null=True)),
(
- "is_superuser",
- models.BooleanField(
- default=False,
- help_text="Designates that this user has all permissions without explicitly assigning them.",
- verbose_name="superuser status",
+ "contact_method",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ ("is_representative", models.BooleanField(default=False)),
+ ("address", models.TextField(blank=True, max_length=100, null=True)),
+ ("city", models.CharField(blank=True, max_length=100, null=True)),
+ ("country", models.CharField(blank=True, max_length=100, null=True)),
+ ],
+ options={
+ "abstract": False,
+ },
+ ),
+ migrations.CreateModel(
+ name="DefaultValues",
+ fields=[
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
),
),
(
- "username",
+ "currency",
models.CharField(
- error_messages={"unique": "A user with that username already exists."},
- help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
- max_length=150,
- unique=True,
- validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
- verbose_name="username",
+ choices=[
+ ("GBP", "British Pound Sterling"),
+ ("EUR", "Euro"),
+ ("USD", "United States Dollar"),
+ ("JPY", "Japanese Yen"),
+ ("INR", "Indian Rupee"),
+ ("AUD", "Australian Dollar"),
+ ("CAD", "Canadian Dollar"),
+ ],
+ default="GBP",
+ max_length=3,
),
),
+ ("invoice_due_date_value", models.PositiveSmallIntegerField(default=7)),
(
- "first_name",
- models.CharField(blank=True, max_length=150, verbose_name="first name"),
+ "invoice_due_date_type",
+ models.CharField(
+ choices=[
+ ("days_after", "Days After"),
+ ("date_following", "Date Following"),
+ ("date_current", "Date Current"),
+ ],
+ default="days_after",
+ max_length=20,
+ ),
),
+ ("invoice_date_value", models.PositiveSmallIntegerField(default=15)),
(
- "last_name",
- models.CharField(blank=True, max_length=150, verbose_name="last name"),
+ "invoice_date_type",
+ models.CharField(
+ choices=[
+ ("day_of_month", "Day Of Month"),
+ ("days_after", "Days After"),
+ ],
+ default="day_of_month",
+ max_length=20,
+ ),
),
(
- "email",
- models.EmailField(blank=True, max_length=254, verbose_name="email address"),
+ "invoice_from_name",
+ models.CharField(blank=True, max_length=100, null=True),
),
(
- "is_staff",
- models.BooleanField(
- default=False,
- help_text="Designates whether the user can log into this admin site.",
- verbose_name="staff status",
- ),
+ "invoice_from_company",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "invoice_from_address",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "invoice_from_city",
+ models.CharField(blank=True, max_length=100, null=True),
),
(
- "is_active",
- models.BooleanField(
- default=True,
- help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
- verbose_name="active",
+ "invoice_from_county",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "invoice_from_country",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "invoice_from_email",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "invoice_account_number",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "invoice_sort_code",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "invoice_account_holder_name",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "email_template_recurring_invoices_invoice_created",
+ models.TextField(
+ default=backend.data.default_email_templates.recurring_invoices_invoice_created_default_email_template
),
),
(
- "date_joined",
- models.DateTimeField(default=django.utils.timezone.now, verbose_name="date joined"),
+ "email_template_recurring_invoices_invoice_overdue",
+ models.TextField(
+ default=backend.data.default_email_templates.recurring_invoices_invoice_overdue_default_email_template
+ ),
),
(
- "groups",
- models.ManyToManyField(
- blank=True,
- help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
- related_name="user_set",
- related_query_name="user",
- to="auth.group",
- verbose_name="groups",
+ "email_template_recurring_invoices_invoice_cancelled",
+ models.TextField(
+ default=backend.data.default_email_templates.recurring_invoices_invoice_cancelled_default_email_template
),
),
(
- "user_permissions",
- models.ManyToManyField(
+ "default_invoice_logo",
+ models.ImageField(
blank=True,
- help_text="Specific permissions for this user.",
- related_name="user_set",
- related_query_name="user",
- to="auth.permission",
- verbose_name="user permissions",
+ null=True,
+ storage=core.models._private_storage,
+ upload_to="invoice_logos/",
),
),
],
options={
- "verbose_name": "user",
- "verbose_name_plural": "users",
"abstract": False,
},
- managers=[
- ("objects", backend.models.CustomUserManager()),
- ],
),
migrations.CreateModel(
- name="Client",
+ name="FileStorageFile",
fields=[
(
"id",
@@ -129,24 +187,20 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("active", models.BooleanField(default=True)),
- ("name", models.CharField(max_length=64)),
(
- "phone_number",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- ("email", models.EmailField(blank=True, max_length=254, null=True)),
- ("address", models.CharField(blank=True, max_length=100, null=True)),
- ("city", models.CharField(blank=True, max_length=100, null=True)),
- ("country", models.CharField(blank=True, max_length=100, null=True)),
- (
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
+ "file",
+ models.FileField(
+ storage=core.models._private_storage,
+ upload_to=core.models.upload_to_user_separate_folder,
),
),
+ ("file_uri_path", models.CharField(max_length=500)),
+ ("created_at", models.DateTimeField(auto_now_add=True)),
+ ("updated_at", models.DateTimeField(auto_now=True)),
],
+ options={
+ "abstract": False,
+ },
),
migrations.CreateModel(
name="Invoice",
@@ -160,11 +214,14 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("invoice_id", models.IntegerField(blank=True, null=True, unique=True)),
(
"client_name",
models.CharField(blank=True, max_length=100, null=True),
),
+ (
+ "client_email",
+ models.EmailField(blank=True, max_length=254, null=True),
+ ),
(
"client_company",
models.CharField(blank=True, max_length=100, null=True),
@@ -185,6 +242,7 @@ class Migration(migrations.Migration):
"client_country",
models.CharField(blank=True, max_length=100, null=True),
),
+ ("client_is_representative", models.BooleanField(default=False)),
("self_name", models.CharField(blank=True, max_length=100, null=True)),
(
"self_company",
@@ -203,7 +261,7 @@ class Migration(migrations.Migration):
"self_country",
models.CharField(blank=True, max_length=100, null=True),
),
- ("sort_code", models.CharField(blank=True, max_length=100, null=True)),
+ ("sort_code", models.CharField(blank=True, max_length=8, null=True)),
(
"account_holder_name",
models.CharField(blank=True, max_length=100, null=True),
@@ -212,42 +270,69 @@ class Migration(migrations.Migration):
"account_number",
models.CharField(blank=True, max_length=100, null=True),
),
- ("reference", models.CharField(blank=True, max_length=100, null=True)),
- (
- "invoice_number",
- models.CharField(blank=True, max_length=100, null=True),
- ),
("vat_number", models.CharField(blank=True, max_length=100, null=True)),
(
"logo",
- models.ImageField(blank=True, null=True, upload_to="invoice_logos"),
+ models.ImageField(
+ blank=True,
+ null=True,
+ storage=core.models._private_storage,
+ upload_to="invoice_logos",
+ ),
),
("notes", models.TextField(blank=True, null=True)),
+ (
+ "currency",
+ models.CharField(
+ choices=[
+ ("GBP", "British Pound Sterling"),
+ ("EUR", "Euro"),
+ ("USD", "United States Dollar"),
+ ("JPY", "Japanese Yen"),
+ ("INR", "Indian Rupee"),
+ ("AUD", "Australian Dollar"),
+ ("CAD", "Canadian Dollar"),
+ ],
+ default="GBP",
+ max_length=3,
+ ),
+ ),
("date_created", models.DateTimeField(auto_now_add=True)),
- ("date_due", models.DateField()),
("date_issued", models.DateField(blank=True, null=True)),
(
- "payment_status",
+ "discount_amount",
+ models.DecimalField(decimal_places=2, default=0, max_digits=15),
+ ),
+ (
+ "discount_percentage",
+ models.DecimalField(
+ decimal_places=2,
+ default=0,
+ max_digits=5,
+ validators=[django.core.validators.MaxValueValidator(100)],
+ ),
+ ),
+ ("created_at", models.DateTimeField(auto_now_add=True)),
+ ("updated_at", models.DateTimeField(auto_now=True)),
+ ("reference", models.CharField(blank=True, max_length=16, null=True)),
+ ("date_due", models.DateField()),
+ (
+ "status",
models.CharField(
choices=[
+ ("draft", "Draft"),
("pending", "Pending"),
("paid", "Paid"),
- ("overdue", "Overdue"),
],
- default="pending",
+ default="draft",
max_length=10,
),
),
- (
- "client_to",
- models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- to="backend.client",
- ),
- ),
+ ("status_updated_at", models.DateTimeField(auto_now_add=True)),
],
+ options={
+ "abstract": False,
+ },
),
migrations.CreateModel(
name="InvoiceItem",
@@ -279,7 +364,7 @@ class Migration(migrations.Migration):
],
),
migrations.CreateModel(
- name="Team",
+ name="InvoiceProduct",
fields=[
(
"id",
@@ -290,22 +375,20 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("name", models.CharField(max_length=100)),
- (
- "leader",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
+ ("name", models.CharField(max_length=50)),
+ ("description", models.CharField(max_length=100)),
+ ("quantity", models.IntegerField()),
(
- "members",
- models.ManyToManyField(related_name="teams_joined", to=settings.AUTH_USER_MODEL),
+ "rate",
+ models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True),
),
],
+ options={
+ "abstract": False,
+ },
),
migrations.CreateModel(
- name="UserSettings",
+ name="InvoiceRecurringProfile",
fields=[
(
"id",
@@ -316,7 +399,98 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("dark_mode", models.BooleanField(default=True)),
+ (
+ "boto_schedule_arn",
+ models.CharField(blank=True, max_length=2048, null=True),
+ ),
+ (
+ "boto_schedule_uuid",
+ models.UUIDField(blank=True, default=None, null=True),
+ ),
+ ("boto_last_updated", models.DateTimeField(auto_now=True)),
+ ("received", models.BooleanField(default=False)),
+ (
+ "boto_schedule_status",
+ models.CharField(
+ choices=[
+ ("pending", "Pending"),
+ ("creating", "Creating"),
+ ("completed", "Completed"),
+ ("failed", "Failed"),
+ ("deleting", "Deleting"),
+ ("cancelled", "Cancelled"),
+ ],
+ default="pending",
+ max_length=100,
+ ),
+ ),
+ (
+ "client_name",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "client_email",
+ models.EmailField(blank=True, max_length=254, null=True),
+ ),
+ (
+ "client_company",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "client_address",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "client_city",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "client_county",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "client_country",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ ("client_is_representative", models.BooleanField(default=False)),
+ ("self_name", models.CharField(blank=True, max_length=100, null=True)),
+ (
+ "self_company",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "self_address",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ ("self_city", models.CharField(blank=True, max_length=100, null=True)),
+ (
+ "self_county",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "self_country",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ ("sort_code", models.CharField(blank=True, max_length=8, null=True)),
+ (
+ "account_holder_name",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ (
+ "account_number",
+ models.CharField(blank=True, max_length=100, null=True),
+ ),
+ ("vat_number", models.CharField(blank=True, max_length=100, null=True)),
+ (
+ "logo",
+ models.ImageField(
+ blank=True,
+ null=True,
+ storage=core.models._private_storage,
+ upload_to="invoice_logos",
+ ),
+ ),
+ ("notes", models.TextField(blank=True, null=True)),
(
"currency",
models.CharField(
@@ -333,51 +507,72 @@ class Migration(migrations.Migration):
max_length=3,
),
),
+ ("date_created", models.DateTimeField(auto_now_add=True)),
+ ("date_issued", models.DateField(blank=True, null=True)),
(
- "profile_picture",
- models.ImageField(blank=True, null=True, upload_to="profile_pictures/"),
+ "discount_amount",
+ models.DecimalField(decimal_places=2, default=0, max_digits=15),
),
(
- "user",
- models.OneToOneField(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="user_profile",
- to=settings.AUTH_USER_MODEL,
+ "discount_percentage",
+ models.DecimalField(
+ decimal_places=2,
+ default=0,
+ max_digits=5,
+ validators=[django.core.validators.MaxValueValidator(100)],
),
),
- ],
- options={
- "verbose_name": "User Settings",
- "verbose_name_plural": "User Settings",
- },
- ),
- migrations.CreateModel(
- name="TracebackError",
- fields=[
+ ("created_at", models.DateTimeField(auto_now_add=True)),
+ ("updated_at", models.DateTimeField(auto_now=True)),
+ ("active", models.BooleanField(default=True)),
(
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
+ "status",
+ models.CharField(
+ choices=[
+ ("ongoing", "Ongoing"),
+ ("paused", "paused"),
+ ("cancelled", "cancelled"),
+ ],
+ default="paused",
+ max_length=10,
),
),
- ("error", models.CharField(max_length=5000, null=True)),
- ("date", models.DateTimeField(auto_now=True)),
(
- "user",
- models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
+ "frequency",
+ models.CharField(
+ choices=[
+ ("weekly", "Weekly"),
+ ("monthly", "Monthly"),
+ ("yearly", "Yearly"),
+ ],
+ default="monthly",
+ max_length=20,
),
),
+ ("end_date", models.DateField(blank=True, null=True)),
+ ("due_after_days", models.PositiveSmallIntegerField(default=7)),
+ (
+ "day_of_week",
+ models.PositiveSmallIntegerField(blank=True, null=True),
+ ),
+ (
+ "day_of_month",
+ models.PositiveSmallIntegerField(blank=True, null=True),
+ ),
+ (
+ "month_of_year",
+ models.PositiveSmallIntegerField(blank=True, null=True),
+ ),
+ ],
+ options={
+ "abstract": False,
+ },
+ managers=[
+ ("with_items", django.db.models.manager.Manager()),
],
),
migrations.CreateModel(
- name="TeamInvitation",
+ name="InvoiceReminder",
fields=[
(
"id",
@@ -388,67 +583,85 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("code", models.CharField(max_length=10)),
- ("expires", models.DateTimeField(blank=True, null=True)),
- ("active", models.BooleanField(default=True)),
+ ("created_at", models.DateTimeField(auto_now_add=True)),
(
- "invited_by",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
+ "boto_schedule_arn",
+ models.CharField(blank=True, max_length=2048, null=True),
),
(
- "team",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="team_invitations",
- to="backend.team",
+ "boto_schedule_uuid",
+ models.UUIDField(blank=True, default=None, null=True),
+ ),
+ ("boto_last_updated", models.DateTimeField(auto_now=True)),
+ ("received", models.BooleanField(default=False)),
+ (
+ "boto_schedule_status",
+ models.CharField(
+ choices=[
+ ("pending", "Pending"),
+ ("creating", "Creating"),
+ ("completed", "Completed"),
+ ("failed", "Failed"),
+ ("deleting", "Deleting"),
+ ("cancelled", "Cancelled"),
+ ],
+ default="pending",
+ max_length=100,
),
),
+ ("days", models.PositiveIntegerField(blank=True, null=True)),
(
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="team_invitations",
- to=settings.AUTH_USER_MODEL,
+ "reminder_type",
+ models.CharField(
+ choices=[
+ ("before_due", "Before Due"),
+ ("after_due", "After Due"),
+ ("on_overdue", "On Overdue"),
+ ],
+ default="before_due",
+ max_length=100,
),
),
],
options={
- "verbose_name": "Team Invitation",
- "verbose_name_plural": "Team Invitations",
+ "verbose_name": "Invoice Reminder",
+ "verbose_name_plural": "Invoice Reminders",
},
),
migrations.CreateModel(
- name="Receipt",
+ name="InvoiceURL",
fields=[
(
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
+ "expires",
+ models.DateTimeField(
+ blank=True,
+ help_text="When the item will expire",
+ null=True,
+ verbose_name="Expires",
),
),
- ("name", models.CharField(max_length=100)),
- ("image", models.ImageField(upload_to="receipts")),
- ("total_price", models.FloatField(blank=True, null=True)),
- ("date", models.DateField(blank=True, null=True)),
- ("date_uploaded", models.DateTimeField(auto_now=True)),
- ("receipt_parsed", models.JSONField(blank=True, null=True)),
+ ("active", models.BooleanField(default=True)),
(
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
+ "uuid",
+ shortuuid.django_fields.ShortUUIDField(
+ alphabet=None,
+ length=8,
+ max_length=8,
+ prefix="",
+ primary_key=True,
+ serialize=False,
),
),
+ ("system_created", models.BooleanField(default=False)),
+ ("created_on", models.DateTimeField(auto_now_add=True)),
],
+ options={
+ "verbose_name": "Invoice URL",
+ "verbose_name_plural": "Invoice URLs",
+ },
),
migrations.CreateModel(
- name="PasswordSecret",
+ name="MonthlyReport",
fields=[
(
"id",
@@ -459,59 +672,50 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("secret", models.TextField(max_length=300)),
- ("expires", models.DateTimeField(blank=True, null=True)),
(
- "user",
- models.OneToOneField(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="password_secrets",
- to=settings.AUTH_USER_MODEL,
- ),
+ "uuid",
+ models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
),
- ],
- ),
- migrations.CreateModel(
- name="Notification",
- fields=[
+ ("name", models.CharField(blank=True, max_length=100, null=True)),
(
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
+ "profit",
+ models.DecimalField(decimal_places=2, default=0, max_digits=15),
),
- ("message", models.CharField(max_length=100)),
+ ("invoices_sent", models.PositiveIntegerField(default=0)),
+ ("start_date", models.DateField()),
+ ("end_date", models.DateField()),
+ ("recurring_customers", models.PositiveIntegerField(default=0)),
(
- "action",
- models.CharField(
- choices=[
- ("normal", "Normal"),
- ("modal", "Modal"),
- ("redirect", "Redirect"),
- ],
- default="normal",
- max_length=10,
- ),
+ "payments_in",
+ models.DecimalField(decimal_places=2, default=0, max_digits=15),
),
(
- "action_value",
- models.CharField(blank=True, max_length=100, null=True),
+ "payments_out",
+ models.DecimalField(decimal_places=2, default=0, max_digits=15),
),
- ("date", models.DateTimeField(auto_now_add=True)),
(
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
+ "currency",
+ models.CharField(
+ choices=[
+ ("GBP", "British Pound Sterling"),
+ ("EUR", "Euro"),
+ ("USD", "United States Dollar"),
+ ("JPY", "Japanese Yen"),
+ ("INR", "Indian Rupee"),
+ ("AUD", "Australian Dollar"),
+ ("CAD", "Canadian Dollar"),
+ ],
+ default="GBP",
+ max_length=3,
),
),
],
+ options={
+ "abstract": False,
+ },
),
migrations.CreateModel(
- name="LoginLog",
+ name="MonthlyReportRow",
fields=[
(
"id",
@@ -522,66 +726,49 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("date", models.DateTimeField(auto_now_add=True)),
+ ("date", models.DateField()),
+ ("reference_number", models.CharField(max_length=100)),
+ ("item_type", models.CharField(max_length=100)),
+ ("client_name", models.CharField(blank=True, max_length=64, null=True)),
(
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
+ "paid_in",
+ models.DecimalField(decimal_places=2, default=0, max_digits=15),
+ ),
+ (
+ "paid_out",
+ models.DecimalField(decimal_places=2, default=0, max_digits=15),
),
],
),
migrations.CreateModel(
- name="InvoiceURL",
+ name="MultiFileUpload",
fields=[
(
- "uuid",
- shortuuid.django_fields.ShortUUIDField(
- alphabet=None,
- length=8,
- max_length=8,
- prefix="",
+ "id",
+ models.BigAutoField(
+ auto_created=True,
primary_key=True,
serialize=False,
+ verbose_name="ID",
),
),
- ("created_on", models.DateTimeField(auto_now_add=True)),
- ("expires", models.DateTimeField(blank=True, null=True)),
- ("active", models.BooleanField(default=True)),
+ ("started_at", models.DateTimeField(auto_now_add=True)),
+ ("updated_at", models.DateTimeField(auto_now=True)),
(
- "created_by",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
+ "finished_at",
+ models.DateTimeField(blank=True, editable=False, null=True),
),
(
- "invoice",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="invoice_urls",
- to="backend.invoice",
- ),
+ "uuid",
+ models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
),
],
options={
- "verbose_name": "Invoice URL",
- "verbose_name_plural": "Invoice URLs",
+ "abstract": False,
},
),
- migrations.AddField(
- model_name="invoice",
- name="items",
- field=models.ManyToManyField(to="backend.invoiceitem"),
- ),
- migrations.AddField(
- model_name="invoice",
- name="user",
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
migrations.CreateModel(
- name="Error",
+ name="Receipt",
fields=[
(
"id",
@@ -592,21 +779,30 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("error", models.CharField(max_length=250, null=True)),
- ("error_code", models.CharField(max_length=100, null=True)),
- ("error_colour", models.CharField(default="danger", max_length=25)),
- ("date", models.DateTimeField(auto_now=True)),
+ ("name", models.CharField(max_length=100)),
(
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
+ "image",
+ models.ImageField(storage=core.models._private_storage, upload_to="receipts"),
+ ),
+ ("total_price", models.FloatField(blank=True, null=True)),
+ ("date", models.DateField(blank=True, null=True)),
+ ("date_uploaded", models.DateTimeField(auto_now_add=True)),
+ ("receipt_parsed", models.JSONField(blank=True, null=True)),
+ (
+ "merchant_store",
+ models.CharField(blank=True, max_length=255, null=True),
+ ),
+ (
+ "purchase_category",
+ models.CharField(blank=True, max_length=200, null=True),
),
],
+ options={
+ "abstract": False,
+ },
),
migrations.CreateModel(
- name="AuditLog",
+ name="ReceiptDownloadToken",
fields=[
(
"id",
@@ -617,16 +813,9 @@ class Migration(migrations.Migration):
verbose_name="ID",
),
),
- ("action", models.CharField(max_length=100)),
- ("date", models.DateTimeField(auto_now_add=True)),
(
- "user",
- models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- to=settings.AUTH_USER_MODEL,
- ),
+ "token",
+ models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
),
],
),
diff --git a/backend/migrations/0002_alter_receipt_date_uploaded.py b/backend/migrations/0002_alter_receipt_date_uploaded.py
deleted file mode 100644
index 190b1949f..000000000
--- a/backend/migrations/0002_alter_receipt_date_uploaded.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 4.2.5 on 2023-11-25 22:41
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0001_initial"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="receipt",
- name="date_uploaded",
- field=models.DateTimeField(auto_now_add=True),
- ),
- ]
diff --git a/backend/migrations/0002_initial.py b/backend/migrations/0002_initial.py
new file mode 100644
index 000000000..f121c1a32
--- /dev/null
+++ b/backend/migrations/0002_initial.py
@@ -0,0 +1,420 @@
+# Generated by Django 5.1.4 on 2024-12-21 22:26
+
+import django.db.models.deletion
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ("backend", "0001_initial"),
+ ("core", "0001_initial"),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="client",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="client",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="defaultvalues",
+ name="client",
+ field=models.OneToOneField(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="default_values",
+ to="backend.client",
+ ),
+ ),
+ migrations.AddField(
+ model_name="defaultvalues",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="defaultvalues",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="filestoragefile",
+ name="last_edited_by",
+ field=models.ForeignKey(
+ blank=True,
+ editable=False,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="files_edited",
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="filestoragefile",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="filestoragefile",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoice",
+ name="client_to",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to="backend.client",
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoice",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoice",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoice",
+ name="items",
+ field=models.ManyToManyField(blank=True, to="backend.invoiceitem"),
+ ),
+ migrations.AddField(
+ model_name="invoiceproduct",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoiceproduct",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoicerecurringprofile",
+ name="client_to",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to="backend.client",
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoicerecurringprofile",
+ name="items",
+ field=models.ManyToManyField(blank=True, to="backend.invoiceitem"),
+ ),
+ migrations.AddField(
+ model_name="invoicerecurringprofile",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoicerecurringprofile",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoice",
+ name="invoice_recurring_profile",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="generated_invoices",
+ to="backend.invoicerecurringprofile",
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoicereminder",
+ name="invoice",
+ field=models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="invoice_reminders",
+ to="backend.invoice",
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoiceurl",
+ name="created_by",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="invoiceurl",
+ name="invoice",
+ field=models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="invoice_urls",
+ to="backend.invoice",
+ ),
+ ),
+ migrations.AddField(
+ model_name="monthlyreport",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="monthlyreport",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="monthlyreportrow",
+ name="client",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="backend.client",
+ ),
+ ),
+ migrations.AddField(
+ model_name="monthlyreport",
+ name="items",
+ field=models.ManyToManyField(blank=True, to="backend.monthlyreportrow"),
+ ),
+ migrations.AddField(
+ model_name="multifileupload",
+ name="files",
+ field=models.ManyToManyField(related_name="multi_file_uploads", to="backend.filestoragefile"),
+ ),
+ migrations.AddField(
+ model_name="multifileupload",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="multifileupload",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="receipt",
+ name="organization",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to="core.organization",
+ ),
+ ),
+ migrations.AddField(
+ model_name="receipt",
+ name="user",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
+ migrations.AddField(
+ model_name="receiptdownloadtoken",
+ name="file",
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="backend.receipt"),
+ ),
+ migrations.AddField(
+ model_name="receiptdownloadtoken",
+ name="user",
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddConstraint(
+ model_name="client",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_client_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="defaultvalues",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_defaultvalues_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="filestoragefile",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_filestoragefile_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="invoiceproduct",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_invoiceproduct_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="invoicerecurringprofile",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_invoicerecurringprofile_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="invoice",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_invoice_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="monthlyreport",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_monthlyreport_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="multifileupload",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_multifileupload_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="receipt",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_receipt_check_user_or_organization",
+ ),
+ ),
+ ]
diff --git a/backend/migrations/0003_client_company_client_is_representative.py b/backend/migrations/0003_client_company_client_is_representative.py
deleted file mode 100644
index 8e6bc3a65..000000000
--- a/backend/migrations/0003_client_company_client_is_representative.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 4.1.7 on 2023-12-12 14:37
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0002_alter_receipt_date_uploaded"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="client",
- name="company",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="client",
- name="is_representative",
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/backend/migrations/0004_invoice_client_is_representative.py b/backend/migrations/0004_invoice_client_is_representative.py
deleted file mode 100644
index 4b5d16191..000000000
--- a/backend/migrations/0004_invoice_client_is_representative.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 4.1.7 on 2023-12-15 08:40
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0003_client_company_client_is_representative"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="invoice",
- name="client_is_representative",
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/backend/migrations/0005_invoiceproduct.py b/backend/migrations/0005_invoiceproduct.py
deleted file mode 100644
index f9d9a7678..000000000
--- a/backend/migrations/0005_invoiceproduct.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Generated by Django 4.1.7 on 2023-12-18 13:24
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0004_invoice_client_is_representative"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="InvoiceProduct",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("name", models.CharField(max_length=50)),
- ("description", models.CharField(max_length=100)),
- ("quantity", models.IntegerField()),
- (
- "rate",
- models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True),
- ),
- (
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- ],
- ),
- ]
diff --git a/backend/migrations/0006_receipt_merchant_store_receipt_purchase_category.py b/backend/migrations/0006_receipt_merchant_store_receipt_purchase_category.py
deleted file mode 100644
index 955990917..000000000
--- a/backend/migrations/0006_receipt_merchant_store_receipt_purchase_category.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 4.2.7 on 2024-01-08 20:32
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0005_invoiceproduct"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="receipt",
- name="merchant_store",
- field=models.CharField(default=None, max_length=200),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name="receipt",
- name="purchase_category",
- field=models.CharField(default=django.utils.timezone.now, max_length=200),
- preserve_default=False,
- ),
- ]
diff --git a/backend/migrations/0007_alter_receipt_merchant_store_and_more.py b/backend/migrations/0007_alter_receipt_merchant_store_and_more.py
deleted file mode 100644
index 1d061f8be..000000000
--- a/backend/migrations/0007_alter_receipt_merchant_store_and_more.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 4.2.7 on 2024-01-09 08:21
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0006_receipt_merchant_store_receipt_purchase_category"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="receipt",
- name="merchant_store",
- field=models.CharField(blank=True, max_length=255, null=True),
- ),
- migrations.AlterField(
- model_name="receipt",
- name="purchase_category",
- field=models.CharField(blank=True, max_length=200, null=True),
- ),
- ]
diff --git a/backend/migrations/0008_receiptdownloadtoken.py b/backend/migrations/0008_receiptdownloadtoken.py
deleted file mode 100644
index ccd39f78a..000000000
--- a/backend/migrations/0008_receiptdownloadtoken.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# Generated by Django 4.2.7 on 2024-01-16 19:06
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import uuid
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0007_alter_receipt_merchant_store_and_more"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="ReceiptDownloadToken",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- (
- "token",
- models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
- ),
- (
- "file",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to="backend.receipt",
- ),
- ),
- (
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- ],
- ),
- ]
diff --git a/backend/migrations/0009_alter_invoice_sort_code.py b/backend/migrations/0009_alter_invoice_sort_code.py
deleted file mode 100644
index 6b0a9e71f..000000000
--- a/backend/migrations/0009_alter_invoice_sort_code.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 4.1.7 on 2024-01-31 08:15
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0008_receiptdownloadtoken"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="invoice",
- name="sort_code",
- field=models.CharField(blank=True, max_length=8, null=True),
- ),
- ]
diff --git a/backend/migrations/0010_user_logged_in_as_team.py b/backend/migrations/0010_user_logged_in_as_team.py
deleted file mode 100644
index d6978976c..000000000
--- a/backend/migrations/0010_user_logged_in_as_team.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 5.0.1 on 2024-02-04 19:20
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0009_alter_invoice_sort_code"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="user",
- name="logged_in_as_team",
- field=models.ForeignKey(
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- to="backend.team",
- ),
- ),
- ]
diff --git a/backend/migrations/0011_alter_team_leader.py b/backend/migrations/0011_alter_team_leader.py
deleted file mode 100644
index 535e5a5f2..000000000
--- a/backend/migrations/0011_alter_team_leader.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 5.0.1 on 2024-02-04 20:36
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0010_user_logged_in_as_team"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="team",
- name="leader",
- field=models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="teams_leader_of",
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- ]
diff --git a/backend/migrations/0012_receipt_organization_alter_receipt_user_and_more.py b/backend/migrations/0012_receipt_organization_alter_receipt_user_and_more.py
deleted file mode 100644
index da5f66196..000000000
--- a/backend/migrations/0012_receipt_organization_alter_receipt_user_and_more.py
+++ /dev/null
@@ -1,44 +0,0 @@
-# Generated by Django 5.0.1 on 2024-02-05 13:41
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0011_alter_team_leader"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="receipt",
- name="organization",
- field=models.ForeignKey(
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="backend.team",
- ),
- ),
- migrations.AlterField(
- model_name="receipt",
- name="user",
- field=models.ForeignKey(
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddConstraint(
- model_name="receipt",
- constraint=models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_receipt_check_user_or_organization",
- ),
- ),
- ]
diff --git a/backend/migrations/0013_auditlog_organization_client_organization_and_more.py b/backend/migrations/0013_auditlog_organization_client_organization_and_more.py
deleted file mode 100644
index cbad74139..000000000
--- a/backend/migrations/0013_auditlog_organization_client_organization_and_more.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# Generated by Django 5.0.1 on 2024-02-05 15:30
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0012_receipt_organization_alter_receipt_user_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="auditlog",
- name="organization",
- field=models.ForeignKey(
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- to="backend.team",
- ),
- ),
- migrations.AddField(
- model_name="client",
- name="organization",
- field=models.ForeignKey(
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="backend.team",
- ),
- ),
- migrations.AddField(
- model_name="invoice",
- name="organization",
- field=models.ForeignKey(
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="backend.team",
- ),
- ),
- migrations.AlterField(
- model_name="client",
- name="user",
- field=models.ForeignKey(
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AlterField(
- model_name="invoice",
- name="user",
- field=models.ForeignKey(
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddConstraint(
- model_name="client",
- constraint=models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_client_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="invoice",
- constraint=models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_invoice_check_user_or_organization",
- ),
- ),
- ]
diff --git a/backend/migrations/0014_notification_extra_type_notification_extra_value.py b/backend/migrations/0014_notification_extra_type_notification_extra_value.py
deleted file mode 100644
index 60b907752..000000000
--- a/backend/migrations/0014_notification_extra_type_notification_extra_value.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 5.0.1 on 2024-02-06 08:34
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0013_auditlog_organization_client_organization_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="notification",
- name="extra_type",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="notification",
- name="extra_value",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- ]
diff --git a/backend/migrations/0015_alter_notification_user_alter_team_name.py b/backend/migrations/0015_alter_notification_user_alter_team_name.py
deleted file mode 100644
index 131a4ee8a..000000000
--- a/backend/migrations/0015_alter_notification_user_alter_team_name.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Generated by Django 5.0.1 on 2024-02-07 08:10
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0014_notification_extra_type_notification_extra_value"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="notification",
- name="user",
- field=models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="user_notifications",
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AlterField(
- model_name="team",
- name="name",
- field=models.CharField(max_length=100, unique=True),
- ),
- ]
diff --git a/backend/migrations/0016_alter_invoice_logo_alter_receipt_image_and_more.py b/backend/migrations/0016_alter_invoice_logo_alter_receipt_image_and_more.py
deleted file mode 100644
index 1372786f2..000000000
--- a/backend/migrations/0016_alter_invoice_logo_alter_receipt_image_and_more.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Generated by Django 5.0.2 on 2024-02-14 19:26
-
-from django.db import migrations, models
-
-import settings.settings
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0015_alter_notification_user_alter_team_name"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="invoice",
- name="logo",
- field=models.ImageField(
- blank=True,
- null=True,
- storage=settings.settings.CustomPrivateMediaStorage(),
- upload_to="invoice_logos",
- ),
- ),
- migrations.AlterField(
- model_name="receipt",
- name="image",
- field=models.ImageField(
- storage=settings.settings.CustomPrivateMediaStorage(),
- upload_to="receipts",
- ),
- ),
- migrations.AlterField(
- model_name="usersettings",
- name="profile_picture",
- field=models.ImageField(
- blank=True,
- null=True,
- storage=settings.settings.CustomPublicMediaStorage(),
- upload_to="profile_pictures/",
- ),
- ),
- ]
diff --git a/backend/migrations/0017_featureflags.py b/backend/migrations/0017_featureflags.py
deleted file mode 100644
index 22721a0c2..000000000
--- a/backend/migrations/0017_featureflags.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 5.0.2 on 2024-02-18 20:17
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0016_alter_invoice_logo_alter_receipt_image_and_more"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="FeatureFlags",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("name", models.CharField(max_length=100)),
- ("value", models.BooleanField(default=False)),
- ("updated_at", models.DateTimeField(auto_now=True)),
- ],
- ),
- ]
diff --git a/backend/migrations/0018_user_role.py b/backend/migrations/0018_user_role.py
deleted file mode 100644
index 5bbecc7c9..000000000
--- a/backend/migrations/0018_user_role.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 5.0.1 on 2024-02-20 10:18
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0017_featureflags"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="user",
- name="role",
- field=models.CharField(
- choices=[
- ("DEV", "Developer"),
- ("STAFF", "Staff"),
- ("USER", "User"),
- ("TESTER", "Tester"),
- ],
- default="USER",
- max_length=10,
- ),
- ),
- ]
diff --git a/backend/migrations/0019_alter_featureflags_options_and_more.py b/backend/migrations/0019_alter_featureflags_options_and_more.py
deleted file mode 100644
index af94ee737..000000000
--- a/backend/migrations/0019_alter_featureflags_options_and_more.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# Generated by Django 5.0.2 on 2024-02-22 18:16
-
-import datetime
-import uuid
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0018_user_role"),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name="featureflags",
- options={
- "verbose_name": "Feature Flag",
- "verbose_name_plural": "Feature Flags",
- },
- ),
- migrations.AddField(
- model_name="user",
- name="awaiting_email_verification",
- field=models.BooleanField(default=True),
- ),
- migrations.CreateModel(
- name="VerificationCodes",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- (
- "uuid",
- models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
- ),
- ("created", models.DateTimeField(auto_now_add=True)),
- (
- "expiry",
- models.DateTimeField(default=datetime.datetime(2024, 2, 22, 21, 16, 55, 46745, tzinfo=datetime.timezone.utc)),
- ),
- (
- "service",
- models.CharField(
- choices=[
- ("create_account", "Create Account"),
- ("reset_password", "Reset Password"),
- ],
- max_length=14,
- ),
- ),
- (
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- ],
- ),
- ]
diff --git a/backend/migrations/0020_alter_verificationcodes_options_and_more.py b/backend/migrations/0020_alter_verificationcodes_options_and_more.py
deleted file mode 100644
index 0285241ea..000000000
--- a/backend/migrations/0020_alter_verificationcodes_options_and_more.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Generated by Django 5.0.2 on 2024-02-22 20:41
-
-import datetime
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0019_alter_featureflags_options_and_more"),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name="verificationcodes",
- options={
- "verbose_name": "Verification Code",
- "verbose_name_plural": "Verification Codes",
- },
- ),
- migrations.AddField(
- model_name="verificationcodes",
- name="token",
- field=models.TextField(default="BZQQWE", editable=False),
- ),
- migrations.AlterField(
- model_name="verificationcodes",
- name="expiry",
- field=models.DateTimeField(default=datetime.datetime(2024, 2, 22, 23, 41, 26, 332896, tzinfo=datetime.timezone.utc)),
- ),
- ]
diff --git a/backend/migrations/0021_alter_verificationcodes_expiry_and_more.py b/backend/migrations/0021_alter_verificationcodes_expiry_and_more.py
deleted file mode 100644
index 71adcf375..000000000
--- a/backend/migrations/0021_alter_verificationcodes_expiry_and_more.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 5.0.2 on 2024-02-23 19:00
-
-import datetime
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0020_alter_verificationcodes_options_and_more"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="verificationcodes",
- name="expiry",
- field=models.DateTimeField(default=datetime.datetime(2024, 2, 23, 22, 0, 25, 744643, tzinfo=datetime.timezone.utc)),
- ),
- migrations.AlterField(
- model_name="verificationcodes",
- name="token",
- field=models.TextField(default="XBNKTM", editable=False),
- ),
- ]
diff --git a/backend/migrations/0022_loginlog_service_alter_verificationcodes_expiry_and_more.py b/backend/migrations/0022_loginlog_service_alter_verificationcodes_expiry_and_more.py
deleted file mode 100644
index ae25f4c2c..000000000
--- a/backend/migrations/0022_loginlog_service_alter_verificationcodes_expiry_and_more.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 5.0.2 on 2024-02-25 11:42
-
-from django.db import migrations, models
-
-import backend.models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0021_alter_verificationcodes_expiry_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="loginlog",
- name="service",
- field=models.CharField(choices=[("manual", "Manual"), ("magic_link", "Magic Link")], default="manual", max_length=14),
- ),
- migrations.AlterField(
- model_name="verificationcodes",
- name="expiry",
- field=models.DateTimeField(default=backend.models.add_3hrs_from_now),
- ),
- migrations.AlterField(
- model_name="verificationcodes",
- name="token",
- field=models.TextField(default=backend.models.RandomCode, editable=False),
- ),
- ]
diff --git a/backend/migrations/0023_apikey_invoiceonetimeschedule.py b/backend/migrations/0023_apikey_invoiceonetimeschedule.py
deleted file mode 100644
index f472a2c9f..000000000
--- a/backend/migrations/0023_apikey_invoiceonetimeschedule.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Generated by Django 5.0.3 on 2024-03-08 23:17
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-import backend.models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0022_loginlog_service_alter_verificationcodes_expiry_and_more"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="APIKey",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("service", models.CharField(choices=[("aws_api_destination", "Aws Api Destination")], max_length=20, null=True)),
- ("key", models.CharField(default=backend.models.RandomAPICode, max_length=100)),
- ("last_used", models.DateTimeField(auto_now_add=True)),
- ],
- options={
- "verbose_name": "API Key",
- "verbose_name_plural": "API Keys",
- },
- ),
- migrations.CreateModel(
- name="InvoiceOnetimeSchedule",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("stored_schedule_arn", models.CharField(blank=True, max_length=100, null=True)),
- ("received", models.BooleanField(default=False)),
- (
- "status",
- models.CharField(
- choices=[
- ("pending", "Pending"),
- ("creating", "Creating"),
- ("completed", "Completed"),
- ("failed", "Failed"),
- ("deleting", "Deleting"),
- ("cancelled", "Cancelled"),
- ],
- default="pending",
- max_length=100,
- ),
- ),
- ("due", models.DateTimeField()),
- (
- "invoice",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE, related_name="onetime_invoice_schedules", to="backend.invoice"
- ),
- ),
- ],
- options={
- "verbose_name": "One-Time Invoice Schedule",
- "verbose_name_plural": "One-Time Invoice Schedules",
- },
- ),
- ]
diff --git a/backend/migrations/0024_invoiceurl_never_expire_invoiceurl_system_created_and_more.py b/backend/migrations/0024_invoiceurl_never_expire_invoiceurl_system_created_and_more.py
deleted file mode 100644
index 0eed8347d..000000000
--- a/backend/migrations/0024_invoiceurl_never_expire_invoiceurl_system_created_and_more.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 5.0.3 on 2024-03-09 13:50
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0023_apikey_invoiceonetimeschedule"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="invoiceurl",
- name="never_expire",
- field=models.BooleanField(default=False),
- ),
- migrations.AddField(
- model_name="invoiceurl",
- name="system_created",
- field=models.BooleanField(default=False),
- ),
- migrations.AlterField(
- model_name="invoiceurl",
- name="created_by",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ]
diff --git a/backend/migrations/0025_alter_invoiceonetimeschedule_stored_schedule_arn.py b/backend/migrations/0025_alter_invoiceonetimeschedule_stored_schedule_arn.py
deleted file mode 100644
index 410dac5f9..000000000
--- a/backend/migrations/0025_alter_invoiceonetimeschedule_stored_schedule_arn.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.3 on 2024-03-16 14:17
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0024_invoiceurl_never_expire_invoiceurl_system_created_and_more"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="invoiceonetimeschedule",
- name="stored_schedule_arn",
- field=models.CharField(blank=True, max_length=500, null=True),
- ),
- ]
diff --git a/backend/migrations/0026_invoice_discount_amount_invoice_discount_percentage.py b/backend/migrations/0026_invoice_discount_amount_invoice_discount_percentage.py
deleted file mode 100644
index 04c63b4f1..000000000
--- a/backend/migrations/0026_invoice_discount_amount_invoice_discount_percentage.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# Generated by Django 5.0.3 on 2024-03-29 20:00
-
-import django.core.validators
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0025_alter_invoiceonetimeschedule_stored_schedule_arn"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="invoice",
- name="discount_amount",
- field=models.DecimalField(decimal_places=2, default=0, max_digits=15),
- ),
- migrations.AddField(
- model_name="invoice",
- name="discount_percentage",
- field=models.DecimalField(
- decimal_places=2, default=0, max_digits=5, validators=[django.core.validators.MaxValueValidator(100)]
- ),
- ),
- ]
diff --git a/backend/migrations/0027_invoice_currency.py b/backend/migrations/0027_invoice_currency.py
deleted file mode 100644
index f2c9a9de4..000000000
--- a/backend/migrations/0027_invoice_currency.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 5.0.3 on 2024-03-31 23:19
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0026_invoice_discount_amount_invoice_discount_percentage"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="invoice",
- name="currency",
- field=models.CharField(
- choices=[
- ("GBP", "British Pound Sterling"),
- ("EUR", "Euro"),
- ("USD", "United States Dollar"),
- ("JPY", "Japanese Yen"),
- ("INR", "Indian Rupee"),
- ("AUD", "Australian Dollar"),
- ("CAD", "Canadian Dollar"),
- ],
- default="GBP",
- max_length=3,
- ),
- ),
- ]
diff --git a/backend/migrations/0028_quotalimit_quotaincreaserequest_quotaoverrides_and_more.py b/backend/migrations/0028_quotalimit_quotaincreaserequest_quotaoverrides_and_more.py
deleted file mode 100644
index ca011aa92..000000000
--- a/backend/migrations/0028_quotalimit_quotaincreaserequest_quotaoverrides_and_more.py
+++ /dev/null
@@ -1,111 +0,0 @@
-# Generated by Django 5.0.3 on 2024-04-01 17:55
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0027_invoice_currency"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="QuotaLimit",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("slug", models.CharField(editable=False, max_length=100, unique=True)),
- ("name", models.CharField(editable=False, max_length=100)),
- ("description", models.TextField(blank=True, max_length=500, null=True)),
- ("value", models.IntegerField()),
- ("updated_at", models.DateTimeField(auto_now=True)),
- ("adjustable", models.BooleanField(default=True)),
- (
- "limit_type",
- models.CharField(
- choices=[
- ("per_month", "Per Month"),
- ("per_day", "Per Day"),
- ("per_client", "Per Client"),
- ("per_invoice", "Per Invoice"),
- ("per_team", "Per Team"),
- ("per_quota", "Per Quota"),
- ("forever", "Forever"),
- ],
- default="per_month",
- max_length=20,
- ),
- ),
- ],
- options={
- "verbose_name": "Quota Limit",
- "verbose_name_plural": "Quota Limits",
- },
- ),
- migrations.CreateModel(
- name="QuotaIncreaseRequest",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("new_value", models.IntegerField()),
- ("current_value", models.IntegerField()),
- ("updated_at", models.DateTimeField(auto_now=True)),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- (
- "status",
- models.CharField(
- choices=[("pending", "Pending"), ("approved", "Approved"), ("rejected", "Rejected")],
- default="pending",
- max_length=20,
- ),
- ),
- ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- (
- "quota_limit",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE, related_name="quota_increase_requests", to="backend.quotalimit"
- ),
- ),
- ],
- options={
- "verbose_name": "Quota Increase Request",
- "verbose_name_plural": "Quota Increase Requests",
- },
- ),
- migrations.CreateModel(
- name="QuotaOverrides",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("value", models.IntegerField()),
- ("updated_at", models.DateTimeField(auto_now=True)),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- (
- "quota_limit",
- models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="quota_overrides", to="backend.quotalimit"),
- ),
- ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- options={
- "verbose_name": "Quota Override",
- "verbose_name_plural": "Quota Overrides",
- },
- ),
- migrations.CreateModel(
- name="QuotaUsage",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("extra_data", models.IntegerField(blank=True, null=True)),
- (
- "quota_limit",
- models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="quota_usage", to="backend.quotalimit"),
- ),
- ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- options={
- "verbose_name": "Quota Usage",
- "verbose_name_plural": "Quota Usage",
- },
- ),
- ]
diff --git a/backend/migrations/0029_alter_invoice_organization_alter_invoice_user_and_more.py b/backend/migrations/0029_alter_invoice_organization_alter_invoice_user_and_more.py
deleted file mode 100644
index 59943713d..000000000
--- a/backend/migrations/0029_alter_invoice_organization_alter_invoice_user_and_more.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 5.0.3 on 2024-04-01 19:49
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0028_quotalimit_quotaincreaserequest_quotaoverrides_and_more"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="invoice",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.team"),
- ),
- migrations.AlterField(
- model_name="invoice",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name="user",
- name="logged_in_as_team",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="backend.team"),
- ),
- ]
diff --git a/backend/migrations/0030_alter_invoice_items.py b/backend/migrations/0030_alter_invoice_items.py
deleted file mode 100644
index f8906bfde..000000000
--- a/backend/migrations/0030_alter_invoice_items.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.3 on 2024-04-04 15:24
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0029_alter_invoice_organization_alter_invoice_user_and_more"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="invoice",
- name="items",
- field=models.ManyToManyField(blank=True, to="backend.invoiceitem"),
- ),
- ]
diff --git a/backend/migrations/0031_featureflags_description_alter_featureflags_name.py b/backend/migrations/0031_featureflags_description_alter_featureflags_name.py
deleted file mode 100644
index c93047183..000000000
--- a/backend/migrations/0031_featureflags_description_alter_featureflags_name.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 5.0.4 on 2024-04-05 19:52
-from __future__ import annotations
-
-from django.db import migrations
-from django.db import models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0030_alter_invoice_items"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="featureflags",
- name="description",
- field=models.TextField(blank=True, editable=False, max_length=500, null=True),
- ),
- migrations.AlterField(
- model_name="featureflags",
- name="name",
- field=models.CharField(editable=False, max_length=100, unique=True),
- ),
- ]
diff --git a/backend/migrations/0032_client_email_verified_alter_client_organization_and_more.py b/backend/migrations/0032_client_email_verified_alter_client_organization_and_more.py
deleted file mode 100644
index 31ef9fa2a..000000000
--- a/backend/migrations/0032_client_email_verified_alter_client_organization_and_more.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# Generated by Django 5.0.4 on 2024-04-10 13:40
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0031_featureflags_description_alter_featureflags_name"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="client",
- name="email_verified",
- field=models.BooleanField(default=False),
- ),
- migrations.AlterField(
- model_name="client",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.team"),
- ),
- migrations.AlterField(
- model_name="client",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.CreateModel(
- name="EmailSendStatus",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("updated_at", models.DateTimeField(auto_now=True)),
- ("updated_status_at", models.DateTimeField(auto_now_add=True)),
- ("recipient", models.TextField()),
- ("aws_message_id", models.CharField(blank=True, editable=False, max_length=100, null=True)),
- (
- "status",
- models.CharField(
- choices=[
- ("send", "Send"),
- ("reject", "Reject"),
- ("bounce", "Bounce"),
- ("complaint", "Complaint"),
- ("delivery", "Delivery"),
- ("open", "Open"),
- ("click", "Click"),
- ("rendering_failure", "Rendering_Failure"),
- ("delivery_delay", "Delivery_Delay"),
- ("subscription", "Subscription"),
- ("failed_to_send", "Failed_To_Send"),
- ("pending", "Pending"),
- ],
- max_length=20,
- ),
- ),
- (
- "organization",
- models.ForeignKey(
- blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name="emails_created", to="backend.team"
- ),
- ),
- (
- "sent_by",
- models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- related_name="emails_sent",
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- (
- "user",
- models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- related_name="emails_created",
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- ],
- ),
- migrations.AddConstraint(
- model_name="emailsendstatus",
- constraint=models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_emailsendstatus_check_user_or_organization",
- ),
- ),
- ]
diff --git a/backend/migrations/0033_alter_auditlog_organization.py b/backend/migrations/0033_alter_auditlog_organization.py
deleted file mode 100644
index 202d0f74c..000000000
--- a/backend/migrations/0033_alter_auditlog_organization.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 5.0.4 on 2024-04-10 16:11
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0032_client_email_verified_alter_client_organization_and_more"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="auditlog",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="backend.team"),
- ),
- ]
diff --git a/backend/migrations/0034_invoice_client_email_quotaincreaserequest_reason_and_more.py b/backend/migrations/0034_invoice_client_email_quotaincreaserequest_reason_and_more.py
deleted file mode 100644
index 6657a171c..000000000
--- a/backend/migrations/0034_invoice_client_email_quotaincreaserequest_reason_and_more.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Generated by Django 5.0.4 on 2024-04-19 20:15
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0033_alter_auditlog_organization"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="invoice",
- name="client_email",
- field=models.EmailField(blank=True, max_length=254, null=True),
- ),
- migrations.AddField(
- model_name="quotaincreaserequest",
- name="reason",
- field=models.CharField(default="", max_length=1000),
- preserve_default=False,
- ),
- migrations.CreateModel(
- name="InvoiceReminder",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("stored_schedule_arn", models.CharField(blank=True, max_length=500, null=True)),
- ("received", models.BooleanField(default=False)),
- (
- "status",
- models.CharField(
- choices=[
- ("pending", "Pending"),
- ("creating", "Creating"),
- ("completed", "Completed"),
- ("failed", "Failed"),
- ("deleting", "Deleting"),
- ("cancelled", "Cancelled"),
- ],
- default="pending",
- max_length=100,
- ),
- ),
- ("days", models.PositiveIntegerField(blank=True, null=True)),
- (
- "reminder_type",
- models.CharField(
- choices=[("before_due", "Before Due"), ("after_due", "After Due"), ("on_overdue", "On Overdue")],
- default="before_due",
- max_length=100,
- ),
- ),
- (
- "invoice",
- models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="invoice_reminders", to="backend.invoice"),
- ),
- ],
- options={
- "verbose_name": "Invoice Reminder",
- "verbose_name_plural": "Invoice Reminders",
- },
- ),
- ]
diff --git a/backend/migrations/0035_client_contact_method.py b/backend/migrations/0035_client_contact_method.py
deleted file mode 100644
index 7cda02391..000000000
--- a/backend/migrations/0035_client_contact_method.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.4 on 2024-05-30 01:58
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0034_invoice_client_email_quotaincreaserequest_reason_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="client",
- name="contact_method",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- ]
diff --git a/backend/migrations/0036_alter_client_address_clientdefaults.py b/backend/migrations/0036_alter_client_address_clientdefaults.py
deleted file mode 100644
index 940b7486d..000000000
--- a/backend/migrations/0036_alter_client_address_clientdefaults.py
+++ /dev/null
@@ -1,61 +0,0 @@
-# Generated by Django 5.0.4 on 2024-06-18 17:07
-
-import django.db.models.deletion
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0035_client_contact_method"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="client",
- name="address",
- field=models.TextField(blank=True, max_length=100, null=True),
- ),
- migrations.CreateModel(
- name="ClientDefaults",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- (
- "currency",
- models.CharField(
- choices=[
- ("GBP", "British Pound Sterling"),
- ("EUR", "Euro"),
- ("USD", "United States Dollar"),
- ("JPY", "Japanese Yen"),
- ("INR", "Indian Rupee"),
- ("AUD", "Australian Dollar"),
- ("CAD", "Canadian Dollar"),
- ],
- default="GBP",
- max_length=3,
- ),
- ),
- ("invoice_due_date_value", models.PositiveSmallIntegerField(default=7)),
- (
- "invoice_due_date_type",
- models.CharField(
- choices=[("days_after", "Days After"), ("date_following", "Date Following"), ("date_current", "Date Current")],
- default="days_after",
- max_length=20,
- ),
- ),
- ("invoice_date_value", models.PositiveSmallIntegerField(default=15)),
- (
- "invoice_date_type",
- models.CharField(
- choices=[("day_of_month", "Day Of Month"), ("days_after", "Days After")], default="day_of_month", max_length=20
- ),
- ),
- (
- "client",
- models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name="client_defaults", to="backend.client"),
- ),
- ],
- ),
- ]
diff --git a/backend/migrations/0036_apiauthtoken.py b/backend/migrations/0036_apiauthtoken.py
deleted file mode 100644
index d9ce8ac20..000000000
--- a/backend/migrations/0036_apiauthtoken.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 5.0.4 on 2024-06-10 16:59
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0035_client_contact_method"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="APIAuthToken",
- fields=[
- ("key", models.CharField(max_length=40, primary_key=True, serialize=False, verbose_name="Key")),
- ("created", models.DateTimeField(auto_now_add=True, verbose_name="Created")),
- ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- ),
- ]
diff --git a/backend/migrations/0037_merge_20240619_2223.py b/backend/migrations/0037_merge_20240619_2223.py
deleted file mode 100644
index a3adee93d..000000000
--- a/backend/migrations/0037_merge_20240619_2223.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Generated by Django 5.0.4 on 2024-06-19 21:23
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0036_alter_client_address_clientdefaults"),
- ("backend", "0036_apiauthtoken"),
- ]
-
- operations = []
diff --git a/backend/migrations/0038_alter_apiauthtoken_options.py b/backend/migrations/0038_alter_apiauthtoken_options.py
deleted file mode 100644
index 79e6017fa..000000000
--- a/backend/migrations/0038_alter_apiauthtoken_options.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 5.0.6 on 2024-06-20 16:42
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0037_merge_20240619_2223"),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name="apiauthtoken",
- options={"verbose_name": "API Key", "verbose_name_plural": "API Keys"},
- ),
- ]
diff --git a/backend/migrations/0039_apiauthtoken_active_apiauthtoken_description_and_more.py b/backend/migrations/0039_apiauthtoken_active_apiauthtoken_description_and_more.py
deleted file mode 100644
index b4096b57f..000000000
--- a/backend/migrations/0039_apiauthtoken_active_apiauthtoken_description_and_more.py
+++ /dev/null
@@ -1,59 +0,0 @@
-# Generated by Django 5.0.6 on 2024-06-21 17:18
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0038_alter_apiauthtoken_options"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="apiauthtoken",
- name="key",
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="active",
- field=models.BooleanField(default=True, help_text="If the key is active", verbose_name="Active"),
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="description",
- field=models.TextField(blank=True, null=True, verbose_name="Description"),
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="expired",
- field=models.BooleanField(default=False, help_text="If the key has expired", verbose_name="Expired"),
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="expires",
- field=models.DateTimeField(blank=True, help_text="Leave blank for no expiry", null=True, verbose_name="Expires"),
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="id",
- field=models.AutoField(primary_key=True, serialize=False, auto_created=True),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="last_used",
- field=models.DateTimeField(blank=True, null=True, verbose_name="Last Used"),
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="name",
- field=models.CharField(default="bob", max_length=64, verbose_name="Key Name"),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="key",
- field=models.CharField(max_length=40, unique=True),
- ),
- ]
diff --git a/backend/migrations/0040_apiauthtoken_scopes_apiauthtoken_team_and_more.py b/backend/migrations/0040_apiauthtoken_scopes_apiauthtoken_team_and_more.py
deleted file mode 100644
index a71c884de..000000000
--- a/backend/migrations/0040_apiauthtoken_scopes_apiauthtoken_team_and_more.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Generated by Django 5.0.6 on 2024-06-21 19:51
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0039_apiauthtoken_active_apiauthtoken_description_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="apiauthtoken",
- name="scopes",
- field=models.JSONField(default=list, help_text="List of permitted scopes", verbose_name="Scopes"),
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="team",
- field=models.ForeignKey(
- blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name="tokens", to="backend.team"
- ),
- ),
- migrations.AlterField(
- model_name="apiauthtoken",
- name="id",
- field=models.AutoField(primary_key=True, serialize=False),
- ),
- migrations.AlterField(
- model_name="apiauthtoken",
- name="key",
- field=models.CharField(max_length=40, unique=True, verbose_name="Key"),
- ),
- migrations.CreateModel(
- name="TeamMemberPermission",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("scopes", models.JSONField(default=list, help_text="List of permitted scopes", verbose_name="Scopes")),
- ("team", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="permissions", to="backend.team")),
- (
- "user",
- models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE, related_name="team_permissions", to=settings.AUTH_USER_MODEL
- ),
- ),
- ],
- options={
- "unique_together": {("team", "user")},
- },
- ),
- ]
diff --git a/backend/migrations/0041_alter_apiauthtoken_user.py b/backend/migrations/0041_alter_apiauthtoken_user.py
deleted file mode 100644
index 5fb36919a..000000000
--- a/backend/migrations/0041_alter_apiauthtoken_user.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Generated by Django 5.0.6 on 2024-06-28 23:57
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0040_apiauthtoken_scopes_apiauthtoken_team_and_more"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="apiauthtoken",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ]
diff --git a/backend/migrations/0042_remove_apiauthtoken_key_apiauthtoken_hashed_key.py b/backend/migrations/0042_remove_apiauthtoken_key_apiauthtoken_hashed_key.py
deleted file mode 100644
index ad1934645..000000000
--- a/backend/migrations/0042_remove_apiauthtoken_key_apiauthtoken_hashed_key.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Generated by Django 5.0.6 on 2024-06-29 18:19
-from django.contrib.auth.hashers import make_password
-from django.db import migrations, models, transaction
-
-
-def forwards_func(apps, schema_editor):
- APIAuthToken = apps.get_model("backend", "APIAuthToken")
-
- tokens = APIAuthToken.objects.all()
-
- for token in tokens:
- token.hashed_key = make_password(token.key, salt="api_tokens", hasher="default")
-
- with transaction.atomic():
- APIAuthToken.objects.bulk_update(tokens, ["hashed_key"])
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ("backend", "0041_alter_apiauthtoken_user"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="apiauthtoken",
- name="hashed_key",
- field=models.CharField(max_length=128, unique=True, null=True, verbose_name="Key"),
- ),
- migrations.RunPython(forwards_func), # (cant really reverse)
- migrations.AlterField(
- model_name="apiauthtoken",
- name="hashed_key",
- field=models.CharField(max_length=128, unique=True, null=False, verbose_name="Key"),
- ),
- migrations.RemoveField(
- model_name="apiauthtoken",
- name="key",
- ),
- ]
diff --git a/backend/migrations/0043_rename_team_organization_remove_apiauthtoken_team_and_more.py b/backend/migrations/0043_rename_team_organization_remove_apiauthtoken_team_and_more.py
deleted file mode 100644
index ad41712b4..000000000
--- a/backend/migrations/0043_rename_team_organization_remove_apiauthtoken_team_and_more.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# Generated by Django 5.0.6 on 2024-06-30 19:11
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-def forwards_func(apps, schema_editor):
- QuotaIncreaseRequest = apps.get_model("backend", "QuotaIncreaseRequest")
-
- objs = QuotaIncreaseRequest.objects.all()
-
- for obj in objs:
- obj.requester = obj.user
-
- QuotaIncreaseRequest.objects.bulk_update(objs, ["requester"])
-
-
-def reverse_func(apps, schema_editor):
- pass
- # Reverse code not needed
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0042_remove_apiauthtoken_key_apiauthtoken_hashed_key"),
- ]
-
- operations = [
- migrations.RenameModel(
- old_name="Team",
- new_name="Organization",
- ),
- migrations.RemoveField(
- model_name="apiauthtoken",
- name="team",
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- migrations.AddField(
- model_name="invoiceproduct",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- migrations.AddField(
- model_name="quotaincreaserequest",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- migrations.AddField(
- model_name="quotaincreaserequest",
- name="requester",
- field=models.ForeignKey(
- default=1,
- on_delete=django.db.models.deletion.CASCADE,
- related_name="quota_increase_requests",
- to=settings.AUTH_USER_MODEL,
- ),
- preserve_default=False,
- ),
- migrations.RunPython(forwards_func, reverse_code=reverse_func),
- migrations.AddField(
- model_name="quotaoverrides",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- migrations.AddField(
- model_name="quotausage",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- migrations.AlterField(
- model_name="auditlog",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- migrations.AlterField(
- model_name="auditlog",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name="emailsendstatus",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- migrations.AlterField(
- model_name="emailsendstatus",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name="invoiceproduct",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name="quotaincreaserequest",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name="quotaoverrides",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name="quotausage",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AlterField(
- model_name="receipt",
- name="organization",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- migrations.AlterField(
- model_name="receipt",
- name="user",
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AddConstraint(
- model_name="invoiceproduct",
- constraint=models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_invoiceproduct_check_user_or_organization",
- ),
- ),
- ]
diff --git a/backend/migrations/0044_defaultvalues_delete_clientdefaults_and_more.py b/backend/migrations/0044_defaultvalues_delete_clientdefaults_and_more.py
deleted file mode 100644
index e2b6f8fd0..000000000
--- a/backend/migrations/0044_defaultvalues_delete_clientdefaults_and_more.py
+++ /dev/null
@@ -1,88 +0,0 @@
-# Generated by Django 5.0.6 on 2024-07-18 21:32
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0043_rename_team_organization_remove_apiauthtoken_team_and_more"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="DefaultValues",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- (
- "currency",
- models.CharField(
- choices=[
- ("GBP", "British Pound Sterling"),
- ("EUR", "Euro"),
- ("USD", "United States Dollar"),
- ("JPY", "Japanese Yen"),
- ("INR", "Indian Rupee"),
- ("AUD", "Australian Dollar"),
- ("CAD", "Canadian Dollar"),
- ],
- default="GBP",
- max_length=3,
- ),
- ),
- ("invoice_due_date_value", models.PositiveSmallIntegerField(default=7)),
- (
- "invoice_due_date_type",
- models.CharField(
- choices=[("days_after", "Days After"), ("date_following", "Date Following"), ("date_current", "Date Current")],
- default="days_after",
- max_length=20,
- ),
- ),
- ("invoice_date_value", models.PositiveSmallIntegerField(default=15)),
- (
- "invoice_date_type",
- models.CharField(
- choices=[("day_of_month", "Day Of Month"), ("days_after", "Days After")], default="day_of_month", max_length=20
- ),
- ),
- (
- "client",
- models.OneToOneField(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- related_name="default_values",
- to="backend.client",
- ),
- ),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ],
- options={
- "abstract": False,
- },
- ),
- migrations.DeleteModel(
- name="ClientDefaults",
- ),
- migrations.AddConstraint(
- model_name="defaultvalues",
- constraint=models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_defaultvalues_check_user_or_organization",
- ),
- ),
- ]
diff --git a/backend/migrations/0045_usersettings_disabled_features.py b/backend/migrations/0045_usersettings_disabled_features.py
deleted file mode 100644
index a6e6bfcc1..000000000
--- a/backend/migrations/0045_usersettings_disabled_features.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.0.6 on 2024-07-19 22:44
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0044_defaultvalues_delete_clientdefaults_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="usersettings",
- name="disabled_features",
- field=models.JSONField(default=list),
- ),
- ]
diff --git a/backend/migrations/0046_rename_status_invoicereminder_boto_schedule_status_and_more.py b/backend/migrations/0046_rename_status_invoicereminder_boto_schedule_status_and_more.py
deleted file mode 100644
index 332f04292..000000000
--- a/backend/migrations/0046_rename_status_invoicereminder_boto_schedule_status_and_more.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# Generated by Django 5.0.7 on 2024-08-22 15:09
-
-import backend.models
-import django.core.validators
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0045_usersettings_disabled_features"),
- ]
-
- operations = [
- migrations.RenameField(
- model_name="invoicereminder",
- old_name="status",
- new_name="boto_schedule_status",
- ),
- migrations.RemoveField(
- model_name="invoicereminder",
- name="stored_schedule_arn",
- ),
- migrations.AddField(
- model_name="apiauthtoken",
- name="administrator_service_type",
- field=models.CharField(blank=True, max_length=64, null=True, verbose_name="Administrator Service Type"),
- ),
- migrations.AddField(
- model_name="invoicereminder",
- name="boto_last_updated",
- field=models.DateTimeField(auto_now=True),
- ),
- migrations.AddField(
- model_name="invoicereminder",
- name="boto_schedule_arn",
- field=models.CharField(blank=True, max_length=2048, null=True),
- ),
- migrations.AddField(
- model_name="invoicereminder",
- name="boto_schedule_uuid",
- field=models.UUIDField(blank=True, default=None, null=True),
- ),
- migrations.AlterField(
- model_name="invoice",
- name="logo",
- field=models.ImageField(blank=True, null=True, storage=backend.models._private_storage, upload_to="invoice_logos"),
- ),
- migrations.AlterField(
- model_name="receipt",
- name="image",
- field=models.ImageField(storage=backend.models._private_storage, upload_to="receipts"),
- ),
- migrations.AlterField(
- model_name="teammemberpermission",
- name="user",
- field=models.OneToOneField(
- on_delete=django.db.models.deletion.CASCADE, related_name="team_permissions", to=settings.AUTH_USER_MODEL
- ),
- ),
- migrations.AlterField(
- model_name="usersettings",
- name="profile_picture",
- field=models.ImageField(blank=True, null=True, storage=backend.models._public_storage, upload_to="profile_pictures/"),
- ),
- migrations.CreateModel(
- name="InvoiceRecurringProfile",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("boto_schedule_arn", models.CharField(blank=True, max_length=2048, null=True)),
- ("boto_schedule_uuid", models.UUIDField(blank=True, default=None, null=True)),
- ("boto_last_updated", models.DateTimeField(auto_now=True)),
- ("received", models.BooleanField(default=False)),
- (
- "boto_schedule_status",
- models.CharField(
- choices=[
- ("pending", "Pending"),
- ("creating", "Creating"),
- ("completed", "Completed"),
- ("failed", "Failed"),
- ("deleting", "Deleting"),
- ("cancelled", "Cancelled"),
- ],
- default="pending",
- max_length=100,
- ),
- ),
- ("client_name", models.CharField(blank=True, max_length=100, null=True)),
- ("client_email", models.EmailField(blank=True, max_length=254, null=True)),
- ("client_company", models.CharField(blank=True, max_length=100, null=True)),
- ("client_address", models.CharField(blank=True, max_length=100, null=True)),
- ("client_city", models.CharField(blank=True, max_length=100, null=True)),
- ("client_county", models.CharField(blank=True, max_length=100, null=True)),
- ("client_country", models.CharField(blank=True, max_length=100, null=True)),
- ("client_is_representative", models.BooleanField(default=False)),
- ("self_name", models.CharField(blank=True, max_length=100, null=True)),
- ("self_company", models.CharField(blank=True, max_length=100, null=True)),
- ("self_address", models.CharField(blank=True, max_length=100, null=True)),
- ("self_city", models.CharField(blank=True, max_length=100, null=True)),
- ("self_county", models.CharField(blank=True, max_length=100, null=True)),
- ("self_country", models.CharField(blank=True, max_length=100, null=True)),
- ("sort_code", models.CharField(blank=True, max_length=8, null=True)),
- ("account_holder_name", models.CharField(blank=True, max_length=100, null=True)),
- ("account_number", models.CharField(blank=True, max_length=100, null=True)),
- ("reference", models.CharField(blank=True, max_length=100, null=True)),
- ("invoice_number", models.CharField(blank=True, max_length=100, null=True)),
- ("vat_number", models.CharField(blank=True, max_length=100, null=True)),
- ("logo", models.ImageField(blank=True, null=True, storage=backend.models._private_storage, upload_to="invoice_logos")),
- ("notes", models.TextField(blank=True, null=True)),
- (
- "currency",
- models.CharField(
- choices=[
- ("GBP", "British Pound Sterling"),
- ("EUR", "Euro"),
- ("USD", "United States Dollar"),
- ("JPY", "Japanese Yen"),
- ("INR", "Indian Rupee"),
- ("AUD", "Australian Dollar"),
- ("CAD", "Canadian Dollar"),
- ],
- default="GBP",
- max_length=3,
- ),
- ),
- ("date_created", models.DateTimeField(auto_now_add=True)),
- ("date_issued", models.DateField(blank=True, null=True)),
- ("discount_amount", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
- (
- "discount_percentage",
- models.DecimalField(
- decimal_places=2, default=0, max_digits=5, validators=[django.core.validators.MaxValueValidator(100)]
- ),
- ),
- ("active", models.BooleanField(default=True)),
- (
- "status",
- models.CharField(
- choices=[("ongoing", "Ongoing"), ("paused", "paused"), ("cancelled", "cancelled")], default="paused", max_length=10
- ),
- ),
- (
- "frequency",
- models.CharField(
- choices=[("weekly", "Weekly"), ("monthly", "Monthly"), ("yearly", "Yearly")], default="monthly", max_length=20
- ),
- ),
- ("end_date", models.DateField(blank=True, null=True)),
- ("due_after_days", models.PositiveSmallIntegerField(default=7)),
- ("day_of_week", models.PositiveSmallIntegerField(blank=True, null=True)),
- ("day_of_month", models.PositiveSmallIntegerField(blank=True, null=True)),
- ("month_of_year", models.PositiveSmallIntegerField(blank=True, null=True)),
- ("client_to", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="backend.client")),
- ("items", models.ManyToManyField(blank=True, to="backend.invoiceitem")),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ],
- options={
- "abstract": False,
- },
- ),
- migrations.AddField(
- model_name="invoice",
- name="invoice_recurring_profile",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- related_name="generated_invoices",
- to="backend.invoicerecurringprofile",
- ),
- ),
- migrations.DeleteModel(
- name="InvoiceOnetimeSchedule",
- ),
- migrations.AddConstraint(
- model_name="invoicerecurringprofile",
- constraint=models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_invoicerecurringprofile_check_user_or_organization",
- ),
- ),
- ]
diff --git a/backend/migrations/0047_defaultvalues_default_invoice_logo.py b/backend/migrations/0047_defaultvalues_default_invoice_logo.py
deleted file mode 100644
index 61a66dd8f..000000000
--- a/backend/migrations/0047_defaultvalues_default_invoice_logo.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 5.1 on 2024-08-23 11:13
-
-import settings.settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0046_rename_status_invoicereminder_boto_schedule_status_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="defaultvalues",
- name="default_invoice_logo",
- field=models.ImageField(
- blank=True, null=True, storage=settings.settings.CustomPublicMediaStorage(), upload_to="invoice_logos/"
- ),
- ),
- ]
diff --git a/backend/migrations/0048_alter_defaultvalues_default_invoice_logo.py b/backend/migrations/0048_alter_defaultvalues_default_invoice_logo.py
deleted file mode 100644
index a93d15ff2..000000000
--- a/backend/migrations/0048_alter_defaultvalues_default_invoice_logo.py
+++ /dev/null
@@ -1,21 +0,0 @@
-# Generated by Django 5.1 on 2024-08-23 11:54
-
-import backend.models
-from django.db import migrations, models
-
-from backend.models import _private_storage
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0047_defaultvalues_default_invoice_logo"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="defaultvalues",
- name="default_invoice_logo",
- field=models.ImageField(blank=True, null=True, storage=_private_storage, upload_to="invoice_logos/"),
- ),
- ]
diff --git a/backend/migrations/0049_filestoragefile.py b/backend/migrations/0049_filestoragefile.py
deleted file mode 100644
index 19b90a444..000000000
--- a/backend/migrations/0049_filestoragefile.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Generated by Django 5.1 on 2024-08-25 20:16
-
-import backend.models
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0048_alter_defaultvalues_default_invoice_logo"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="FileStorageFile",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- (
- "file",
- models.FileField(storage=backend.models._private_storage, upload_to=backend.models.upload_to_user_separate_folder),
- ),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("updated_at", models.DateTimeField(auto_now=True)),
- (
- "last_edited_by",
- models.ForeignKey(
- blank=True,
- editable=False,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- related_name="files_edited",
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ],
- options={
- "abstract": False,
- "constraints": [
- models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_filestoragefile_check_user_or_organization",
- )
- ],
- },
- ),
- ]
diff --git a/backend/migrations/0050_multifileupload.py b/backend/migrations/0050_multifileupload.py
deleted file mode 100644
index 1b8f11dd9..000000000
--- a/backend/migrations/0050_multifileupload.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Generated by Django 5.1 on 2024-08-26 10:53
-
-import django.db.models.deletion
-import uuid
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0049_filestoragefile"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="MultiFileUpload",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("started_at", models.DateTimeField(auto_now_add=True)),
- ("updated_at", models.DateTimeField(auto_now=True)),
- ("finished_at", models.DateTimeField(blank=True, editable=False, null=True)),
- ("uuid", models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
- ("files", models.ManyToManyField(related_name="multi_file_uploads", to="backend.filestoragefile")),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ],
- options={
- "abstract": False,
- "constraints": [
- models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_multifileupload_check_user_or_organization",
- )
- ],
- },
- ),
- ]
diff --git a/backend/migrations/0051_planfeaturegroup_subscriptionplan_planfeature_and_more.py b/backend/migrations/0051_planfeaturegroup_subscriptionplan_planfeature_and_more.py
deleted file mode 100644
index fe7c81423..000000000
--- a/backend/migrations/0051_planfeaturegroup_subscriptionplan_planfeature_and_more.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# Generated by Django 5.1 on 2024-08-26 17:46
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0050_multifileupload"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="PlanFeatureGroup",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("name", models.CharField(max_length=50)),
- ],
- ),
- migrations.CreateModel(
- name="SubscriptionPlan",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- (
- "name",
- models.CharField(
- choices=[
- ("free", "Free Plan"),
- ("basic", "Basic Plan"),
- ("standard", "Standard Plan"),
- ("enterprise", "Enterprise Plan"),
- ],
- max_length=50,
- unique=True,
- ),
- ),
- ("price_per_month", models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
- ("description", models.TextField(blank=True, max_length=500, null=True)),
- ("maximum_duration_months", models.IntegerField(blank=True, null=True)),
- ],
- ),
- migrations.CreateModel(
- name="PlanFeature",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("slug", models.CharField(editable=False, max_length=100, unique=True)),
- ("name", models.CharField(editable=False, max_length=100)),
- ("description", models.TextField(blank=True, max_length=500, null=True)),
- (
- "group",
- models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="features", to="backend.planfeaturegroup"),
- ),
- (
- "subscription_plan",
- models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="features", to="backend.subscriptionplan"),
- ),
- ],
- ),
- migrations.CreateModel(
- name="PlanFeatureVersion",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("version", models.IntegerField(editable=False)),
- ("free_tier_limit", models.FloatField(default=0)),
- ("free_period_in_months", models.IntegerField(blank=True, null=True)),
- ("unit", models.CharField(max_length=20)),
- ("cost_per_unit", models.DecimalField(decimal_places=6, max_digits=10)),
- ("units_per_cost", models.FloatField(default=1)),
- ("minimum_billable_size", models.FloatField(blank=True, null=True)),
- ("valid_from", models.DateTimeField(auto_now_add=True)),
- ("valid_to", models.DateTimeField(blank=True, null=True)),
- ("plan_feature", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="backend.planfeature")),
- ],
- ),
- migrations.CreateModel(
- name="Usage",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("feature", models.CharField(max_length=50)),
- ("quantity", models.FloatField()),
- ("unit", models.CharField(max_length=20)),
- ("timestamp", models.DateTimeField(auto_now_add=True)),
- ("end_time", models.DateTimeField(blank=True, null=True)),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ],
- options={
- "abstract": False,
- "constraints": [
- models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_usage_check_user_or_organization",
- )
- ],
- },
- ),
- migrations.CreateModel(
- name="UserPlan",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("start_date", models.DateField()),
- ("is_active", models.BooleanField(default=True)),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- ("plan", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="backend.planfeature")),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ],
- options={
- "abstract": False,
- "constraints": [
- models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_userplan_check_user_or_organization",
- )
- ],
- },
- ),
- migrations.CreateModel(
- name="UserSubscription",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("custom_subscription_price_per_month", models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)),
- ("start_date", models.DateField()),
- ("end_date", models.DateField(blank=True, null=True)),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- (
- "subscription_plan",
- models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to="backend.subscriptionplan"),
- ),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ],
- options={
- "abstract": False,
- "constraints": [
- models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_usersubscription_check_user_or_organization",
- )
- ],
- },
- ),
- ]
diff --git a/backend/migrations/0052_filestoragefile_file_uri_path.py b/backend/migrations/0052_filestoragefile_file_uri_path.py
deleted file mode 100644
index 34ddd213e..000000000
--- a/backend/migrations/0052_filestoragefile_file_uri_path.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 5.1 on 2024-08-27 08:49
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0051_planfeaturegroup_subscriptionplan_planfeature_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="filestoragefile",
- name="file_uri_path",
- field=models.CharField(default="/null/", max_length=500),
- preserve_default=False,
- ),
- ]
diff --git a/backend/migrations/0053_usage_instance_id_alter_planfeature_name_and_more.py b/backend/migrations/0053_usage_instance_id_alter_planfeature_name_and_more.py
deleted file mode 100644
index bbac4f49e..000000000
--- a/backend/migrations/0053_usage_instance_id_alter_planfeature_name_and_more.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Generated by Django 5.1 on 2024-08-27 18:33
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0052_filestoragefile_file_uri_path"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="usage",
- name="instance_id",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AlterField(
- model_name="planfeature",
- name="name",
- field=models.CharField(max_length=100),
- ),
- migrations.AlterField(
- model_name="planfeature",
- name="slug",
- field=models.CharField(editable=False, max_length=100),
- ),
- migrations.AlterField(
- model_name="subscriptionplan",
- name="name",
- field=models.CharField(max_length=50, unique=True),
- ),
- migrations.AlterField(
- model_name="usersubscription",
- name="end_date",
- field=models.DateTimeField(blank=True, null=True),
- ),
- migrations.AlterField(
- model_name="usersubscription",
- name="start_date",
- field=models.DateTimeField(auto_now_add=True),
- ),
- migrations.DeleteModel(
- name="UserPlan",
- ),
- ]
diff --git a/backend/migrations/0054_transferusage_storageusage.py b/backend/migrations/0054_transferusage_storageusage.py
deleted file mode 100644
index 2fe694416..000000000
--- a/backend/migrations/0054_transferusage_storageusage.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Generated by Django 5.1 on 2024-08-28 14:43
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0053_usage_instance_id_alter_planfeature_name_and_more"),
- ]
-
- operations = [
- migrations.CreateModel(
- name="TransferUsage",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("amount_in_MB", models.DecimalField(decimal_places=2, max_digits=10)),
- ("timestamp", models.DateTimeField(auto_now_add=True)),
- ("feature", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="backend.planfeature")),
- ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- ),
- migrations.CreateModel(
- name="StorageUsage",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("file_uri_path", models.CharField(max_length=255)),
- ("size_in_MB", models.DecimalField(decimal_places=8, max_digits=10)),
- ("start_time", models.DateTimeField(auto_now_add=True)),
- ("end_time", models.DateTimeField(blank=True, null=True)),
- ("feature", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="backend.planfeature")),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ],
- options={
- "abstract": False,
- "constraints": [
- models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_storageusage_check_user_or_organization",
- )
- ],
- },
- ),
- ]
diff --git a/backend/migrations/0055_remove_planfeature_group_and_more.py b/backend/migrations/0055_remove_planfeature_group_and_more.py
deleted file mode 100644
index 54a56f2c5..000000000
--- a/backend/migrations/0055_remove_planfeature_group_and_more.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Generated by Django 5.1 on 2024-08-29 13:09
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0054_transferusage_storageusage"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="planfeature",
- name="group",
- ),
- migrations.RemoveField(
- model_name="planfeature",
- name="subscription_plan",
- ),
- migrations.RemoveField(
- model_name="planfeatureversion",
- name="plan_feature",
- ),
- migrations.RemoveField(
- model_name="storageusage",
- name="feature",
- ),
- migrations.RemoveField(
- model_name="transferusage",
- name="feature",
- ),
- # migrations.RemoveField(
- # model_name="storageusage",
- # name="organization",
- # ),
- # migrations.RemoveField(
- # model_name="storageusage",
- # name="user",
- # ),
- migrations.RemoveField(
- model_name="usersubscription",
- name="subscription_plan",
- ),
- # migrations.RemoveField(
- # model_name="transferusage",
- # name="user",
- # ),
- # migrations.RemoveField(
- # model_name="usage",
- # name="organization",
- # ),
- # migrations.RemoveField(
- # model_name="usage",
- # name="user",
- # ),
- # migrations.RemoveField(
- # model_name="usersubscription",
- # name="organization",
- # ),
- # migrations.RemoveField(
- # model_name="usersubscription",
- # name="user",
- # ),
- migrations.DeleteModel(
- name="PlanFeatureGroup",
- ),
- migrations.DeleteModel(
- name="PlanFeatureVersion",
- ),
- migrations.DeleteModel(
- name="PlanFeature",
- ),
- migrations.DeleteModel(
- name="StorageUsage",
- ),
- migrations.DeleteModel(
- name="SubscriptionPlan",
- ),
- migrations.DeleteModel(
- name="TransferUsage",
- ),
- migrations.DeleteModel(
- name="Usage",
- ),
- migrations.DeleteModel(
- name="UserSubscription",
- ),
- ]
diff --git a/backend/migrations/0056_user_stripe_customer_id.py b/backend/migrations/0056_user_stripe_customer_id.py
deleted file mode 100644
index 2d2938fe6..000000000
--- a/backend/migrations/0056_user_stripe_customer_id.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.1 on 2024-08-29 13:44
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0055_remove_planfeature_group_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="user",
- name="stripe_customer_id",
- field=models.CharField(blank=True, max_length=255, null=True),
- ),
- ]
diff --git a/backend/migrations/0057_user_entitlements.py b/backend/migrations/0057_user_entitlements.py
deleted file mode 100644
index d5aa91033..000000000
--- a/backend/migrations/0057_user_entitlements.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.1 on 2024-09-01 17:55
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0056_user_stripe_customer_id"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="user",
- name="entitlements",
- field=models.JSONField(blank=True, default=list, null=True),
- ),
- ]
diff --git a/backend/migrations/0058_organization_entitlements_and_more.py b/backend/migrations/0058_organization_entitlements_and_more.py
deleted file mode 100644
index f526ef1b0..000000000
--- a/backend/migrations/0058_organization_entitlements_and_more.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 5.1 on 2024-09-02 18:02
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0057_user_entitlements"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="organization",
- name="entitlements",
- field=models.JSONField(blank=True, default=list, null=True),
- ),
- migrations.AddField(
- model_name="organization",
- name="stripe_customer_id",
- field=models.CharField(blank=True, max_length=255, null=True),
- ),
- ]
diff --git a/backend/migrations/0059_alter_invoicerecurringprofile_managers_and_more.py b/backend/migrations/0059_alter_invoicerecurringprofile_managers_and_more.py
deleted file mode 100644
index 3f0355aec..000000000
--- a/backend/migrations/0059_alter_invoicerecurringprofile_managers_and_more.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Generated by Django 5.1 on 2024-09-08 13:50
-
-import django.db.models.deletion
-import django.db.models.manager
-import uuid
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0058_organization_entitlements_and_more"),
- ]
-
- operations = [
- migrations.AlterModelManagers(
- name="invoicerecurringprofile",
- managers=[
- ("with_items", django.db.models.manager.Manager()),
- ],
- ),
- migrations.CreateModel(
- name="MonthlyReportRow",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("date", models.DateField()),
- ("reference_number", models.CharField(max_length=100)),
- ("item_type", models.CharField(max_length=100)),
- ("client_name", models.CharField(blank=True, max_length=64, null=True)),
- ("paid_in", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
- ("paid_out", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
- ("client", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.client")),
- ],
- ),
- migrations.CreateModel(
- name="MonthlyReport",
- fields=[
- ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
- ("uuid", models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
- ("name", models.CharField(blank=True, max_length=100, null=True)),
- ("profit", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
- ("invoices_sent", models.PositiveIntegerField(default=0)),
- ("start_date", models.DateField()),
- ("end_date", models.DateField()),
- ("recurring_customers", models.PositiveIntegerField(default=0)),
- ("payments_in", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
- ("payments_out", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
- (
- "currency",
- models.CharField(
- choices=[
- ("GBP", "British Pound Sterling"),
- ("EUR", "Euro"),
- ("USD", "United States Dollar"),
- ("JPY", "Japanese Yen"),
- ("INR", "Indian Rupee"),
- ("AUD", "Australian Dollar"),
- ("CAD", "Canadian Dollar"),
- ],
- default="GBP",
- max_length=3,
- ),
- ),
- (
- "organization",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="backend.organization"),
- ),
- (
- "user",
- models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- ("items", models.ManyToManyField(blank=True, to="backend.monthlyreportrow")),
- ],
- options={
- "abstract": False,
- "constraints": [
- models.CheckConstraint(
- check=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_monthlyreport_check_user_or_organization",
- )
- ],
- },
- ),
- ]
diff --git a/backend/migrations/0060_user_require_change_password.py b/backend/migrations/0060_user_require_change_password.py
deleted file mode 100644
index e17cf8764..000000000
--- a/backend/migrations/0060_user_require_change_password.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.1 on 2024-09-14 21:55
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0059_alter_invoicerecurringprofile_managers_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="user",
- name="require_change_password",
- field=models.BooleanField(default=False),
- ),
- ]
diff --git a/backend/migrations/0061_defaultvalues_invoice_from_address_and_more.py b/backend/migrations/0061_defaultvalues_invoice_from_address_and_more.py
deleted file mode 100644
index 053b5b856..000000000
--- a/backend/migrations/0061_defaultvalues_invoice_from_address_and_more.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Generated by Django 5.1 on 2024-09-16 19:47
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0060_user_require_change_password"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_from_address",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_from_city",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_from_company",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_from_country",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_from_county",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_from_name",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- ]
diff --git a/backend/migrations/0062_defaultvalues_invoice_account_holder_name_and_more.py b/backend/migrations/0062_defaultvalues_invoice_account_holder_name_and_more.py
deleted file mode 100644
index 076be836f..000000000
--- a/backend/migrations/0062_defaultvalues_invoice_account_holder_name_and_more.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# Generated by Django 5.1 on 2024-09-16 20:49
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0061_defaultvalues_invoice_from_address_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_account_holder_name",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_account_number",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_sort_code",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- ]
diff --git a/backend/migrations/0063_defaultvalues_email_template_recurring_invoices_invoice_cancelled_and_more.py b/backend/migrations/0063_defaultvalues_email_template_recurring_invoices_invoice_cancelled_and_more.py
deleted file mode 100644
index 5aefa7ed3..000000000
--- a/backend/migrations/0063_defaultvalues_email_template_recurring_invoices_invoice_cancelled_and_more.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Generated by Django 5.1 on 2024-09-28 18:46
-
-import backend.data.default_email_templates
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0062_defaultvalues_invoice_account_holder_name_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="defaultvalues",
- name="email_template_recurring_invoices_invoice_cancelled",
- field=models.TextField(
- default=backend.data.default_email_templates.recurring_invoices_invoice_cancelled_default_email_template
- ),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="email_template_recurring_invoices_invoice_created",
- field=models.TextField(default=backend.data.default_email_templates.recurring_invoices_invoice_created_default_email_template),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="email_template_recurring_invoices_invoice_overdue",
- field=models.TextField(default=backend.data.default_email_templates.recurring_invoices_invoice_overdue_default_email_template),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="invoice_from_email",
- field=models.CharField(blank=True, max_length=100, null=True),
- ),
- ]
diff --git a/backend/migrations/0064_remove_invoice_payment_status_invoice_status.py b/backend/migrations/0064_remove_invoice_payment_status_invoice_status.py
deleted file mode 100644
index d0049a0ed..000000000
--- a/backend/migrations/0064_remove_invoice_payment_status_invoice_status.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# Generated by Django 5.1 on 2024-09-28 19:36
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0063_defaultvalues_email_template_recurring_invoices_invoice_cancelled_and_more"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="invoice",
- name="payment_status",
- ),
- migrations.AddField(
- model_name="invoice",
- name="status",
- field=models.CharField(
- choices=[("draft", "Draft"), ("pending", "Pending"), ("paid", "Paid")], default="pending", max_length=10
- ),
- ),
- ]
diff --git a/backend/migrations/0065_remove_invoiceurl_never_expire_passwordsecret_active_and_more.py b/backend/migrations/0065_remove_invoiceurl_never_expire_passwordsecret_active_and_more.py
deleted file mode 100644
index 647f8b132..000000000
--- a/backend/migrations/0065_remove_invoiceurl_never_expire_passwordsecret_active_and_more.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Generated by Django 5.1.1 on 2024-10-02 20:00
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0064_remove_invoice_payment_status_invoice_status"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="invoiceurl",
- name="never_expire",
- ),
- migrations.AddField(
- model_name="passwordsecret",
- name="active",
- field=models.BooleanField(default=True),
- ),
- migrations.AlterField(
- model_name="invoice",
- name="status",
- field=models.CharField(choices=[("draft", "Draft"), ("pending", "Pending"), ("paid", "Paid")], default="draft", max_length=10),
- ),
- migrations.AlterField(
- model_name="invoiceurl",
- name="expires",
- field=models.DateTimeField(blank=True, help_text="When the item will expire", null=True, verbose_name="Expires"),
- ),
- migrations.AlterField(
- model_name="passwordsecret",
- name="expires",
- field=models.DateTimeField(blank=True, help_text="When the item will expire", null=True, verbose_name="Expires"),
- ),
- migrations.AlterField(
- model_name="teaminvitation",
- name="expires",
- field=models.DateTimeField(blank=True, help_text="When the item will expire", null=True, verbose_name="Expires"),
- ),
- ]
diff --git a/backend/migrations/0066_delete_apikey_remove_verificationcodes_expiry_and_more.py b/backend/migrations/0066_delete_apikey_remove_verificationcodes_expiry_and_more.py
deleted file mode 100644
index 4eaffce95..000000000
--- a/backend/migrations/0066_delete_apikey_remove_verificationcodes_expiry_and_more.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Generated by Django 5.1.1 on 2024-10-06 07:52
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0065_remove_invoiceurl_never_expire_passwordsecret_active_and_more"),
- ]
-
- operations = [
- migrations.DeleteModel(
- name="APIKey",
- ),
- migrations.RemoveField(
- model_name="verificationcodes",
- name="expiry",
- ),
- migrations.AddField(
- model_name="verificationcodes",
- name="active",
- field=models.BooleanField(default=True),
- ),
- migrations.AddField(
- model_name="verificationcodes",
- name="expires",
- field=models.DateTimeField(blank=True, help_text="When the item will expire", null=True, verbose_name="Expires"),
- ),
- ]
diff --git a/backend/migrations/0067_remove_apiauthtoken_expired_and_more.py b/backend/migrations/0067_remove_apiauthtoken_expired_and_more.py
deleted file mode 100644
index 1272d48dd..000000000
--- a/backend/migrations/0067_remove_apiauthtoken_expired_and_more.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 5.1.1 on 2024-10-19 19:03
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0066_delete_apikey_remove_verificationcodes_expiry_and_more"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="apiauthtoken",
- name="expired",
- ),
- migrations.AlterField(
- model_name="apiauthtoken",
- name="active",
- field=models.BooleanField(default=True),
- ),
- migrations.AlterField(
- model_name="apiauthtoken",
- name="expires",
- field=models.DateTimeField(blank=True, help_text="When the item will expire", null=True, verbose_name="Expires"),
- ),
- ]
diff --git a/backend/migrations/0068_invoice_created_at_invoice_status_updated_at_and_more.py b/backend/migrations/0068_invoice_created_at_invoice_status_updated_at_and_more.py
deleted file mode 100644
index 66513144b..000000000
--- a/backend/migrations/0068_invoice_created_at_invoice_status_updated_at_and_more.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Generated by Django 5.1.1 on 2024-11-08 19:57
-
-import django.utils.timezone
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0067_remove_apiauthtoken_expired_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="invoice",
- name="created_at",
- field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name="invoice",
- name="status_updated_at",
- field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
- preserve_default=False,
- ),
- migrations.AddField(
- model_name="invoice",
- name="updated_at",
- field=models.DateTimeField(auto_now=True),
- ),
- migrations.AddField(
- model_name="invoicerecurringprofile",
- name="updated_at",
- field=models.DateTimeField(auto_now=True),
- ),
- ]
diff --git a/backend/migrations/0069_alter_auditlog_action.py b/backend/migrations/0069_alter_auditlog_action.py
deleted file mode 100644
index e9aae2fbf..000000000
--- a/backend/migrations/0069_alter_auditlog_action.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 5.1.1 on 2024-11-15 09:10
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0068_invoice_created_at_invoice_status_updated_at_and_more"),
- ]
-
- operations = [
- migrations.AlterField(
- model_name="auditlog",
- name="action",
- field=models.CharField(max_length=300),
- ),
- ]
diff --git a/backend/migrations/0070_remove_invoice_invoice_id_and_more.py b/backend/migrations/0070_remove_invoice_invoice_id_and_more.py
deleted file mode 100644
index a059eaa86..000000000
--- a/backend/migrations/0070_remove_invoice_invoice_id_and_more.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Generated by Django 5.1.1 on 2024-11-17 15:06
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("backend", "0069_alter_auditlog_action"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="invoice",
- name="invoice_id",
- ),
- migrations.RemoveField(
- model_name="invoice",
- name="invoice_number",
- ),
- migrations.RemoveField(
- model_name="invoicerecurringprofile",
- name="invoice_number",
- ),
- migrations.RemoveField(
- model_name="invoicerecurringprofile",
- name="reference",
- ),
- migrations.AlterField(
- model_name="invoice",
- name="reference",
- field=models.CharField(blank=True, max_length=16, null=True),
- ),
- ]
diff --git a/backend/modals.py b/backend/modals.py
new file mode 100644
index 000000000..2023fd3ba
--- /dev/null
+++ b/backend/modals.py
@@ -0,0 +1,58 @@
+from core.service.modals.registry import Modal
+from core.types.requests import WebRequest
+from core.utils.feature_flags import get_feature_status
+from django.contrib import messages
+from django.shortcuts import render
+
+from backend.finance.models import InvoiceURL
+from backend.models import Client
+
+
+class InvoicesToDestinationModal(Modal):
+ modal_name = "invoices_to_destination"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ if existing_client := request.GET.get("client"):
+ context["existing_client_id"] = existing_client
+
+ return render(request, self.get_template_name(), context)
+
+
+class EmailContext:
+ def get_context(self, request: WebRequest) -> dict:
+ return {
+ "content_min_length": 64,
+ "content_max_length": 1000,
+ "email_list": Client.filter_by_owner(owner=request.actor).filter(email__isnull=False).values_list("email", flat=True),
+ }
+
+
+class SendSingleEmailModal(Modal, EmailContext):
+ modal_name = "send_single_email"
+
+ def get(self, request: WebRequest):
+ if not get_feature_status("areUserEmailsAllowed"):
+ messages.error(request, "Emails are disabled")
+ return render(request, "base/toast.html")
+
+ context = self.get_context(request)
+
+ if request.GET.get("type") == "invoice_code_send":
+ invoice_url: InvoiceURL | None = InvoiceURL.objects.filter(uuid=request.GET.get("code")).prefetch_related("invoice").first()
+
+ if not invoice_url or not invoice_url.invoice.has_access(request.user):
+ messages.error(request, "You don't have access to this invoice")
+ return render(request, "base/toast.html", {"autohide": False})
+
+ context["invoice"] = invoice_url.invoice
+ context["selected_clients"] = [
+ invoice_url.invoice.client_to.email if invoice_url.invoice.client_to else invoice_url.invoice.client_email
+ for value in [invoice_url.invoice.client_to.email if invoice_url.invoice.client_to else invoice_url.invoice.client_email]
+ if value is not None
+ ]
+
+ context["email_list"] = list(context["email_list"]) + context["selected_clients"]
+
+ return render(request, self.get_template_name(), context)
diff --git a/backend/urls.py b/backend/urls.py
index 5cdb90037..e2b7cdc8c 100644
--- a/backend/urls.py
+++ b/backend/urls.py
@@ -50,7 +50,7 @@
view_invoice_with_uuid_endpoint,
name="invoices view invoice",
),
- path("api/", include("backend.api.urls")),
+ path("api/", include("backend.api.urls", namespace="api")),
] + static(settings.STATIC_URL, document_root=settings.STATICFILES_DIRS[0])
handler500 = "core.views.other.errors.universal"
diff --git a/frontend/templates/base/+left_drawer.html b/frontend/templates/base/+left_drawer.html
index 3c8a986db..5c78a18f3 100644
--- a/frontend/templates/base/+left_drawer.html
+++ b/frontend/templates/base/+left_drawer.html
@@ -1,35 +1,26 @@
+{% extends "core/base/+left_drawer.html" %}
{% load feature_enabled %}
{% feature_enabled "areUserEmailsAllowed" as are_user_emails_allowed %}
-{#{% personal_feature_enabled request.user "invoices" as feature_enabled_invoices %}#}
-{#{% personal_feature_enabled request.user "receipts" as feature_enabled_receipts %}#}
-{#{% personal_feature_enabled request.user "email_sending" as feature_enabled_emails %}#}
-
-
-
-
+ {% endif %}
+ {#
#}
+ {# File Storage#}
+ {# #}
+{% endblock drawer_items %}
\ No newline at end of file
diff --git a/frontend/templates/base/_head.html b/frontend/templates/base/_head.html
index 2e634e3dd..014fef3ce 100644
--- a/frontend/templates/base/_head.html
+++ b/frontend/templates/base/_head.html
@@ -1,70 +1 @@
-{% load static %}
-{% load render_bundle from webpack_loader %}
-{% load tz_detect %}
-
-
-
-
My Finances
-
-
-
-
-
- {% if import_method == "public_cdn" %}
-
-
-
-
- {# #}
-
-
-
- {# #}
-
-
- {# #}
-
- {# #}
- {#
-
#}
-
- {# #}
- {# Not in use at the moment MAY USE LATER ^^ #}
-
-
-
- {# #}
-
- {% else %}
- {# #}
- {# #}
- {# #}
- {# {% render_bundle 'all' 'js' %}#}
- {% render_bundle 'init' 'js' %}
- {% render_bundle 'tableify' 'js' %}
- {% render_bundle 'htmx' 'js' %}
- {% render_bundle 'font_awesome' 'js' %}
-
-
- {% endif %}
- {% render_bundle 'receipt_downloads' 'js' %}
- {{ analytics|safe }}
- {% tz_detect %}
- {# #}
-
+{% extends "core/base/_head.html" %}
\ No newline at end of file
diff --git a/frontend/templates/base/auth.html b/frontend/templates/base/auth.html
index a5a6a95eb..bc617cf75 100644
--- a/frontend/templates/base/auth.html
+++ b/frontend/templates/base/auth.html
@@ -1,37 +1 @@
-{% load static %}
-
- {% include 'base/_head.html' %}
-
-
-
-
-
-
-
-
Dashboard
-
-

-
-
What do you get to manage?
-
✓ Client Lists
-
✓ Invoices
-
✓ Receipt Storage
-
✓ Financial Reports
-
-
-
-
- {# {% component "messages_list" %}#}
- {% include "base/toasts.html" %}
-
- {% block title %}
- {% endblock title %}
-
- {% block content %}
- {% endblock content %}
-
-
-
-
-
-
+{% extends "core/base/auth.html" %}
\ No newline at end of file
diff --git a/frontend/templates/base/base.html b/frontend/templates/base/base.html
index 37a54d72d..4619d0b7e 100644
--- a/frontend/templates/base/base.html
+++ b/frontend/templates/base/base.html
@@ -1,55 +1,4 @@
-
-
- {% include 'base/_head.html' %}
-
-
-
-
- {% include 'base/topbar/_topbar.html' %}
-
-
- {% include 'base/breadcrumbs.html' with breadcrumb_first_load=True %}
-
{% include 'base/toasts.html' %}
-
{# Leave here #}
-
-
-
- {% block content %}
- {% endblock content %}
-
- {% component "base:left_drawer" %}
-
-
-
-
-
-
-
Logout
-
Are you sure you would like to logout?
-
-
-
-
-
-
-
+{% extends "core/base/base.html" %}
+
+{% block topbar %}{% include 'base/topbar/_topbar.html' %}{% endblock %}
+{% block drawer %}{% include "base/+left_drawer.html" %}{% endblock drawer %}
\ No newline at end of file
diff --git a/frontend/templates/base/htmx.html b/frontend/templates/base/htmx.html
index 3836e49ad..8a5e838c4 100644
--- a/frontend/templates/base/htmx.html
+++ b/frontend/templates/base/htmx.html
@@ -1,11 +1,6 @@
-
- {% block content %}
- {% endblock content %}
-
-{# Profile Picture dropdown item #}
-
- {% component "base:topbar:icon_dropdown" %}
-
-{% include "base/+left_drawer.html" with swap=True %}
-{% include "base/breadcrumbs.html" with swap=True %}
-{% include "base/toasts.html" %}
+{% extends "core/base/htmx.html" %}
+{##}
+{% block left_drawer %}{% include "base/+left_drawer.html" with swap=True %}{% endblock %}
+
+{#{% include "base/breadcrumbs.html" with swap=True %}#}
+{#{% include "base/toasts.html" %}#}
diff --git a/frontend/templates/base/topbar/_topbar.html b/frontend/templates/base/topbar/_topbar.html
index c387696e6..3ea6f2eb2 100644
--- a/frontend/templates/base/topbar/_topbar.html
+++ b/frontend/templates/base/topbar/_topbar.html
@@ -1,72 +1,28 @@
-{% load static %}
+{% extends "core/base/topbar/_topbar.html" %}
{% load feature_enabled %}
{% personal_feature_enabled request.user "invoices" as feature_enabled_invoices %}
{% personal_feature_enabled request.user "receipts" as feature_enabled_receipts %}
{% personal_feature_enabled request.user "email_sending" as feature_enabled_emails %}
{% personal_feature_enabled request.user "monthly_reports" as feature_enabled_monthly_reports %}
-
-
-
-
-
-
-
- {# Right Icons #}
- {#
#}
- {# Profile Picture #}
-
-
-
- {% include "base/topbar/_notification_count.html" %}
-
-
-
-
-
-
-
- {# End of Profile Picture #}
- {#
#}
- {# End of Right Icons #}
-
-
+{% endblock topbar_list %}
\ No newline at end of file
diff --git a/frontend/templates/pages/dashboard.html b/frontend/templates/pages/dashboard.html
index 31ccd60b5..f3dd59102 100644
--- a/frontend/templates/pages/dashboard.html
+++ b/frontend/templates/pages/dashboard.html
@@ -1,4 +1,4 @@
-{% extends base|default:"core/base/base.html" %}
+{% extends base|default:"base/base.html" %}
{% block content %}
diff --git a/settings/settings.py b/settings/settings.py
index 681d3dc88..92993c056 100644
--- a/settings/settings.py
+++ b/settings/settings.py
@@ -47,11 +47,11 @@
INSTALLED_APPS = [
"django_extensions",
"django.contrib.admin",
+ "backend",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
- "backend",
"mathfilters",
"django.contrib.humanize",
"django_htmx",
From 21b402178f1ed4926864b4ce26fb1a0cb7d4ab1b Mon Sep 17 00:00:00 2001
From: Trey <73353716+TreyWW@users.noreply.github.com>
Date: Sun, 22 Dec 2024 19:31:51 +0000
Subject: [PATCH 04/21] Fixed all modals to work with new system
Signed-off-by: Trey <73353716+TreyWW@users.noreply.github.com>
---
backend/api/urls.py | 2 +-
backend/apps.py | 4 +
backend/modals.py | 216 ++++++++++++++-
.../topbar/_notification_dropdown_items.html | 4 +-
.../destinations/_from_destination.html | 2 +-
.../pages/invoices/dashboard/manage.html | 2 +-
.../single/edit/edit_from_destination.html | 56 ++--
.../single/edit/edit_to_destination.html | 64 ++---
.../single/manage_access/_table_row.html | 2 +-
.../single/schedules/reminders/container.html | 85 +++---
.../templates/pages/quotas/_fetch_body.html | 54 ++--
.../pages/receipts/_search_results.html | 245 +++++++++---------
.../templates/pages/receipts/dashboard.html | 2 +-
.../pages/settings/pages/profile.html | 11 +-
.../templates/pages/settings/teams/main.html | 94 +++----
15 files changed, 534 insertions(+), 309 deletions(-)
diff --git a/backend/api/urls.py b/backend/api/urls.py
index d6bd0a347..e73faae3d 100644
--- a/backend/api/urls.py
+++ b/backend/api/urls.py
@@ -4,7 +4,7 @@
from django.urls import path
urlpatterns = [
- path("finance/", include("backend.finance.api.urls")),
+ path("clients/", include("backend.clients.api.urls")),
]
app_name = "api"
diff --git a/backend/apps.py b/backend/apps.py
index 3ba516cc5..32eba1ed8 100644
--- a/backend/apps.py
+++ b/backend/apps.py
@@ -1,3 +1,5 @@
+import importlib
+
from django.apps import AppConfig
@@ -7,6 +9,8 @@ class BackendConfig(AppConfig):
def ready(self):
from .finance import signals
+ importlib.import_module("backend.modals")
+
# from .clients import signals
# from .storage import signals
# from .events import signals
diff --git a/backend/modals.py b/backend/modals.py
index 2023fd3ba..105638207 100644
--- a/backend/modals.py
+++ b/backend/modals.py
@@ -4,7 +4,8 @@
from django.contrib import messages
from django.shortcuts import render
-from backend.finance.models import InvoiceURL
+from backend.finance.models import InvoiceURL, Invoice, Receipt
+from backend.finance.service.defaults.get import get_account_defaults
from backend.models import Client
@@ -56,3 +57,216 @@ def get(self, request: WebRequest):
context["email_list"] = list(context["email_list"]) + context["selected_clients"]
return render(request, self.get_template_name(), context)
+
+
+class EditReceiptModal(Modal):
+ modal_name = "edit_receipt"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ try:
+ receipt = Receipt.filter_by_owner(request.actor).get(pk=request.GET.get("receipt_id"))
+ except Receipt.DoesNotExist:
+ return self.Response(request, context)
+
+ receipt_date = receipt.date.strftime("%Y-%m-%d") if receipt.date else ""
+ context.update(
+ {
+ "modal_id": f"modal_{receipt.id}_receipts_upload",
+ "receipt_id": request.GET.get("receipt_id"),
+ "receipt_name": receipt.name,
+ "receipt_date": receipt_date,
+ "merchant_store_name": receipt.merchant_store,
+ "purchase_category": receipt.purchase_category,
+ "total_price": receipt.total_price,
+ "has_receipt_image": True if receipt.image else False,
+ "edit_flag": True,
+ }
+ )
+
+ return self.Response(request, context)
+
+
+class UploadReceiptModal(Modal):
+ modal_name = "upload_receipt"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ context.update({"modal_id": "modal_receipts_upload"})
+
+ return self.Response(request, context)
+
+
+class EditInvoiceToModal(Modal):
+ modal_name = "edit_invoice_to"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ invoice_id = request.GET.get("invoice_id")
+
+ try:
+ invoice = Invoice.filter_by_owner(request.actor).get(id=invoice_id) # todo: add permission checks
+ except Invoice.DoesNotExist:
+ return self.Response(request, context)
+
+ if invoice.client_to:
+ context["to_name"] = invoice.client_to.name
+ context["to_company"] = invoice.client_to.company
+ context["to_email"] = invoice.client_to.email
+ context["to_address"] = invoice.client_to.address
+ context["existing_client_id"] = (
+ invoice.client_to.id
+ ) # context["to_city"] = invoice.client_to.city # context["to_county"] = invoice.client_to.county # context["to_country"] = invoice.client_to.country
+ else:
+ context["to_name"] = invoice.client_name
+ context["to_company"] = invoice.client_company
+ context["to_email"] = invoice.client_email
+ context["is_representative"] = invoice.client_is_representative
+ context["to_address"] = (
+ invoice.client_address
+ ) # context["to_city"] = invoice.client_city # context["to_county"] = invoice.client_county # context["to_country"] = invoice.client_country
+
+ return self.Response(request, context)
+
+
+class EditInvoiceFromModal(Modal):
+ modal_name = "edit_invoice_from"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ invoice_id = request.GET.get("invoice_id")
+
+ try:
+ invoice = Invoice.filter_by_owner(request.actor).get(id=invoice_id) # todo: add permission checks
+ except Invoice.DoesNotExist:
+ return self.Response(request, context)
+
+ context["from_name"] = invoice.self_name
+ context["from_company"] = invoice.self_company
+ context["from_address"] = invoice.self_address
+ context["from_city"] = invoice.self_city
+ context["from_county"] = invoice.self_county
+ context["from_country"] = invoice.self_country
+ return self.Response(request, context)
+
+
+# create_invoice_from
+class CreateInvoiceFromModal(Modal):
+ modal_name = "create_invoice_from"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ defaults = get_account_defaults(request.actor)
+
+ context["from_name"] = getattr(defaults, f"invoice_from_name")
+ context["from_company"] = getattr(defaults, f"invoice_from_company")
+ context["from_address"] = getattr(defaults, f"invoice_from_address")
+ context["from_city"] = getattr(defaults, f"invoice_from_city")
+ context["from_county"] = getattr(defaults, f"invoice_from_county")
+ context["from_country"] = getattr(defaults, f"invoice_from_country")
+
+ return self.Response(request, context)
+
+
+class InvoiceContext:
+ def get_context(self, request: WebRequest) -> dict:
+ try:
+ invoice = Invoice.filter_by_owner(request.actor).get(id=request.GET.get("invoice_id"))
+ if invoice.has_access(request.user):
+ return {"invoice": invoice}
+ except Invoice.DoesNotExist:
+ return {}
+
+
+class EditInvoiceDiscountModal(Modal, InvoiceContext):
+ modal_name = "invoices_edit_discount"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ return self.Response(request, context)
+
+
+# class ViewQuotaLimitInfoModal(Modal):
+# modal_name = 'view_quota_limit_info'
+#
+# def get(self, request: WebRequest):
+# context = self.get_context(request)
+#
+# try:
+# quota = QuotaLimit.objects.prefetch_related("quota_overrides").get(slug=context_value)
+# context["quota"] = quota
+# context["current_limit"] = quota.get_quota_limit(user=request.user, quota_limit=quota)
+# usage = quota.strict_get_quotas(user=request.user, quota_limit=quota)
+# context["quota_usage"] = usage.count() if usage != "Not Available" else "Not available"
+# print(context["quota_usage"])
+# except QuotaLimit.DoesNotExist:
+# ...
+#
+# return self.Response(request, context)
+
+
+class CreateInvoiceReminderModal(Modal):
+ modal_name = "create_invoice_reminder"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ try:
+ invoice = Invoice.filter_by_owner(request.actor).get(id=request.GET.get("invoice_id"))
+ if invoice.has_access(request.user):
+ context["invoice"] = invoice
+ else:
+ messages.error(request, "You don't have access to this invoice")
+ return render(request, "base/toasts.html")
+ except Invoice.DoesNotExist:
+ return self.Response(request, context)
+
+ return self.Response(request, context)
+
+
+class SendEmailContext:
+ def get_context(self, request: WebRequest) -> dict:
+ if not get_feature_status("areUserEmailsAllowed"):
+ messages.error(request, "Emails are disabled")
+ return render(request, "base/toast.html")
+
+ context = {}
+
+ context["content_min_length"] = 64
+ # quota = QuotaLimit.objects.prefetch_related("quota_overrides").get(slug="emails-email_character_count")
+ # context["content_max_length"] = quota.get_quota_limit(user=request.user, quota_limit=quota)
+ context["content_max_length"] = 1000
+
+ context["email_list"] = Client.filter_by_owner(owner=request.actor).filter(email__isnull=False).values_list("email", flat=True)
+
+ return context
+
+
+class InvoiceCodeSendModal(Modal, SendEmailContext):
+ modal_name = "invoice_code_send"
+
+ def get(self, request: WebRequest):
+ context = self.get_context(request)
+
+ invoice_url: InvoiceURL | None = InvoiceURL.objects.filter(uuid=request.GET.get("code")).prefetch_related("invoice").first()
+
+ if not invoice_url or not invoice_url.invoice.has_access(request.user):
+ messages.error(request, "You don't have access to this invoice")
+ return render(request, "base/toast.html", {"autohide": False})
+
+ context["invoice"] = invoice_url.invoice
+ context["selected_clients"] = [
+ invoice_url.invoice.client_to.email if invoice_url.invoice.client_to else invoice_url.invoice.client_email
+ for value in [invoice_url.invoice.client_to.email if invoice_url.invoice.client_to else invoice_url.invoice.client_email]
+ if value is not None
+ ]
+
+ context["email_list"] = list(context["email_list"]) + context["selected_clients"]
+
+ return self.Response(request, context)
diff --git a/frontend/templates/base/topbar/_notification_dropdown_items.html b/frontend/templates/base/topbar/_notification_dropdown_items.html
index 7d9e36d30..c63d45ef7 100644
--- a/frontend/templates/base/topbar/_notification_dropdown_items.html
+++ b/frontend/templates/base/topbar/_notification_dropdown_items.html
@@ -21,7 +21,7 @@
{# hx-trigger="click once"#}
{# hx-swap="beforeend"#}
{# hx-target="#modal_container"#}
-{# hx-get="{% url "api:base:modal retrieve with context" modal_name=notification.action_value context_type=notification.extra_type context_value=notification.extra_value %}">#}
+{# hx-get="{% url "api:base:modal retrieve" modal_name=notification.action_value context_type=notification.extra_type context_value=notification.extra_value %}">#}
{# {{ notification.message }}#}
{# #}
{# #}
@@ -65,7 +65,7 @@
hx-trigger="click once"
hx-swap="beforeend"
hx-target="#modal_container"
- hx-get="{% url "api:base:modal retrieve with context" modal_name=notification.action_value context_type=notification.extra_type context_value=notification.extra_value %}">
+ hx-get="{% url "api:base:modal retrieve" modal_name=notification.action_value %}?{{ notification.extra_type }}={{ notification.extra_value }}">
{{ notification.message }}
diff --git a/frontend/templates/pages/invoices/create/destinations/_from_destination.html b/frontend/templates/pages/invoices/create/destinations/_from_destination.html
index 066b8fc76..27b24a981 100644
--- a/frontend/templates/pages/invoices/create/destinations/_from_destination.html
+++ b/frontend/templates/pages/invoices/create/destinations/_from_destination.html
@@ -5,7 +5,7 @@
hx-trigger="click once"
hx-swap="beforeend"
hx-target="#modal_container"
- hx-get="{% url 'api:base:modal retrieve with context' modal_name="invoices_from_destination" context_type='create_invoice_from' context_value='b' %}">
+ hx-get="{% url 'api:base:modal retrieve' modal_name="create_invoice_from" %}">
{% endif %}
{{ from_name | default:"No name" }}
diff --git a/frontend/templates/pages/invoices/dashboard/manage.html b/frontend/templates/pages/invoices/dashboard/manage.html
index 1931fb886..314e2754a 100644
--- a/frontend/templates/pages/invoices/dashboard/manage.html
+++ b/frontend/templates/pages/invoices/dashboard/manage.html
@@ -104,7 +104,7 @@
hx-swap="beforeend"
hx-target="#modal_container"
_="on click call modal_invoices_edit_discount.showModal()"
- hx-get="{% url "api:base:modal retrieve with context" context_type="invoice" context_value=invoice.id modal_name="invoices_edit_discount" %}">
+ hx-get="{% url "api:base:modal retrieve" modal_name="invoices_edit_discount" %}?invoice_id={{ invoice.id }}">
Edit Discount
diff --git a/frontend/templates/pages/invoices/single/edit/edit_from_destination.html b/frontend/templates/pages/invoices/single/edit/edit_from_destination.html
index f39321506..abd196d2f 100644
--- a/frontend/templates/pages/invoices/single/edit/edit_from_destination.html
+++ b/frontend/templates/pages/invoices/single/edit/edit_from_destination.html
@@ -5,31 +5,31 @@
hx-trigger="click"
hx-swap="beforeend"
hx-target="#modal_container"
- hx-get="{% url 'api:base:modal retrieve with context' modal_name='invoices_from_destination' context_type='edit_invoice_from' context_value=invoice_object.id %}">
- {% endif %}
-
-
-
-
{{ from_name | default:"No name" }}
-
{{ from_company | default:"No company" }}
-
{{ from_address | default:"No address" }}
-
{{ from_city | default:"No city" }}
-
{{ from_county | default:"No county" }}
-
{{ from_country | default:"No country" }}
-
-
-
-
-
-
-
-
- {% if not swapping %}{% endif %}
+ hx-get="{% url 'api:base:modal retrieve' modal_name='edit_invoice_from' %}?invoice_id={{ invoice_object.id }}">
+{% endif %}
+
+
+
+
{{ from_name | default:"No name" }}
+
{{ from_company | default:"No company" }}
+
{{ from_address | default:"No address" }}
+
{{ from_city | default:"No city" }}
+
{{ from_county | default:"No county" }}
+
{{ from_country | default:"No country" }}
+
+
+
+
+
+
+
+
+{% if not swapping %}{% endif %}
diff --git a/frontend/templates/pages/invoices/single/edit/edit_to_destination.html b/frontend/templates/pages/invoices/single/edit/edit_to_destination.html
index c00a25b26..8b92e8079 100644
--- a/frontend/templates/pages/invoices/single/edit/edit_to_destination.html
+++ b/frontend/templates/pages/invoices/single/edit/edit_to_destination.html
@@ -6,36 +6,36 @@
To
hx-trigger="click once"
hx-swap="beforeend"
hx-target="#modal_container"
- hx-get=" {% url "api:base:modal retrieve with context" modal_name="invoices_to_destination" context_type="edit_invoice_to" context_value=invoice_object.id %}">
- {% endif %}
-
-
-
{{ to_name | default:"No name" }}
-
{{ to_company | default:"No company" }}
-
{{ to_email | default:"No email" }}
-
{{ to_address | default:"No address" }}
-
{{ to_city | default:"No city" }}
-
{{ to_county | default:"No county" }}
-
{{ to_country | default:"No country" }}
-
- {% if is_representative %}
- Is a
- {% else %}
- Is not a
- {% endif %}
+ hx-get=" {% url "api:base:modal retrieve" modal_name="edit_invoice_to" %}?invoice_id={{ invoice_object.id }}">
+{% endif %}
+
+
+
{{ to_name | default:"No name" }}
+
{{ to_company | default:"No company" }}
+
{{ to_email | default:"No email" }}
+
{{ to_address | default:"No address" }}
+
{{ to_city | default:"No city" }}
+
{{ to_county | default:"No county" }}
+
{{ to_country | default:"No country" }}
+
+ {% if is_representative %}
+ Is a
+ {% else %}
+ Is not a
+ {% endif %}
representative
-
-
-
-
-
-
-
-
-
-
-
-
- {% if not swapping %}{% endif %}
+
+
+
+
+
+
+
+
+
+
+
+
+{% if not swapping %}{% endif %}
diff --git a/frontend/templates/pages/invoices/single/manage_access/_table_row.html b/frontend/templates/pages/invoices/single/manage_access/_table_row.html
index 7a0c31db3..6bcca4350 100644
--- a/frontend/templates/pages/invoices/single/manage_access/_table_row.html
+++ b/frontend/templates/pages/invoices/single/manage_access/_table_row.html
@@ -19,7 +19,7 @@
hx-trigger="from:#single_send_button, click once"
hx-swap="beforeend"
hx-target="#modal_container"
- hx-get="{% url "api:base:modal retrieve with context" modal_name='send_bulk_email' context_value=code context_type='invoice_code_send' %}">
+ hx-get="{% url "api:base:modal retrieve" modal_name='invoice_code_send' %}?code={{ code }}">
diff --git a/frontend/templates/pages/invoices/single/schedules/reminders/container.html b/frontend/templates/pages/invoices/single/schedules/reminders/container.html
index 8303f3fb6..cd5fb0ffd 100644
--- a/frontend/templates/pages/invoices/single/schedules/reminders/container.html
+++ b/frontend/templates/pages/invoices/single/schedules/reminders/container.html
@@ -18,7 +18,7 @@
View Reminders
From e16ebcd6aa79f5e75f3f9fd1c593a23affc2be49 Mon Sep 17 00:00:00 2001
From: Trey <73353716+TreyWW@users.noreply.github.com>
Date: Tue, 31 Dec 2024 16:04:27 +0000
Subject: [PATCH 20/21] Removed system_health as defined in core
Signed-off-by: Trey <73353716+TreyWW@users.noreply.github.com>
---
backend/api/public/endpoints/system_health.py | 63 -------------------
1 file changed, 63 deletions(-)
delete mode 100644 backend/api/public/endpoints/system_health.py
diff --git a/backend/api/public/endpoints/system_health.py b/backend/api/public/endpoints/system_health.py
deleted file mode 100644
index 6f016289a..000000000
--- a/backend/api/public/endpoints/system_health.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from drf_yasg.utils import swagger_auto_schema
-from drf_yasg import openapi
-from django.db import connection, OperationalError
-from django.core.cache import cache
-
-from rest_framework.decorators import api_view, permission_classes
-
-from core.api.public.permissions import IsSuperuser
-from core.api.public.helpers.response import APIResponse
-
-
-@swagger_auto_schema(
- method="get",
- operation_description="Check the system's health by verifying database and external API connections.",
- responses={
- 200: openapi.Response(
- description="System health check result",
- schema=openapi.Schema(
- type=openapi.TYPE_OBJECT,
- properties={
- "problems": openapi.Schema(
- type=openapi.TYPE_ARRAY,
- items=openapi.Schema(
- type=openapi.TYPE_OBJECT,
- properties={
- "id": openapi.Schema(type=openapi.TYPE_STRING, description="Problem ID"),
- "message": openapi.Schema(type=openapi.TYPE_STRING, description="Problem message"),
- },
- ),
- ),
- "healthy": openapi.Schema(type=openapi.TYPE_BOOLEAN, description="Indicates overall system health"),
- },
- ),
- examples={
- "application/json": {
- "problems": [
- {"id": "database", "message": "database failed to connect"},
- ],
- "healthy": False,
- }
- },
- )
- },
-)
-@api_view(["GET"])
-@permission_classes([IsSuperuser])
-def system_health_endpoint(request):
- if not request.user or not request.user.is_superuser:
- return APIResponse(False, "User is not permitted to view internal information", status=403)
-
- problems = []
-
- try:
- connection.ensure_connection()
- except OperationalError:
- problems.append({"id": "database", "message": "database failed to connect"})
-
- try:
- cache._cache.get_client().ping()
- except ConnectionError:
- problems.append({"id": "redis", "message": "redis failed to connect"})
-
- return APIResponse({"problems": problems, "healthy": not bool(problems)})
From 28637e704f6eb89480fc8cb48950be94b4b0ebf2 Mon Sep 17 00:00:00 2001
From: Trey <73353716+TreyWW@users.noreply.github.com>
Date: Wed, 1 Jan 2025 17:57:49 +0000
Subject: [PATCH 21/21] Updated to use core/ templates, attempted to fix
migrations (broke them more though)
Signed-off-by: Trey <73353716+TreyWW@users.noreply.github.com>
---
backend/admin.py | 6 +-
backend/api/emails/fetch.py | 2 +-
backend/api/emails/send.py | 16 +-
backend/api/emails/status.py | 10 +-
.../api/public/endpoints/invoices/create.py | 2 +-
backend/api/settings/defaults.py | 10 +-
backend/api/settings/email_templates.py | 8 +-
backend/auth_backends.py | 22 -
backend/clients/api/delete.py | 4 +-
backend/clients/api/fetch.py | 4 +-
backend/clients/clients.py | 8 +-
backend/clients/models.py | 168 ++--
backend/clients/views/create.py | 2 +-
backend/clients/views/dashboard.py | 2 +-
backend/clients/views/detail.py | 4 +-
backend/context_processors.py | 70 +-
backend/decorators.py | 272 ------
backend/finance/api/invoices/delete.py | 8 +-
backend/finance/api/invoices/edit.py | 10 +-
backend/finance/api/invoices/fetch.py | 2 +-
backend/finance/api/invoices/manage.py | 2 +-
.../finance/api/invoices/recurring/delete.py | 8 +-
.../finance/api/invoices/recurring/edit.py | 10 +-
.../finance/api/invoices/recurring/fetch.py | 2 +-
.../recurring/generate_next_invoice_now.py | 8 +-
.../finance/api/invoices/recurring/poll.py | 2 +-
.../api/invoices/recurring/update_status.py | 4 +-
.../finance/api/invoices/reminders/create.py | 12 +-
.../finance/api/invoices/reminders/delete.py | 8 +-
.../finance/api/invoices/reminders/fetch.py | 8 +-
backend/finance/api/products/create.py | 2 +-
backend/finance/api/products/fetch.py | 2 +-
backend/finance/api/receipts/delete.py | 2 +-
backend/finance/api/receipts/download.py | 2 +-
backend/finance/api/receipts/edit.py | 4 +-
backend/finance/api/receipts/fetch.py | 2 +-
backend/finance/api/receipts/new.py | 2 +-
backend/finance/api/reports/generate.py | 6 +-
backend/finance/models.py | 84 +-
backend/finance/service/clients/create.py | 2 +-
backend/finance/service/defaults/get.py | 10 +-
backend/finance/service/defaults/update.py | 7 +-
.../service/invoices/common/create/create.py | 4 +-
.../invoices/common/create/get_page.py | 6 +-
.../recurring/generation/next_invoice.py | 6 +-
.../service/invoices/single/create/create.py | 4 +-
.../views/invoices/recurring/create.py | 2 +-
.../views/invoices/recurring/dashboard.py | 2 +-
.../finance/views/invoices/recurring/edit.py | 4 +-
.../views/invoices/recurring/overview.py | 2 +-
.../finance/views/invoices/single/create.py | 2 +-
.../views/invoices/single/dashboard.py | 2 +-
backend/finance/views/invoices/single/edit.py | 7 +-
.../views/invoices/single/manage_access.py | 4 +-
.../finance/views/invoices/single/overview.py | 2 +-
.../finance/views/invoices/single/schedule.py | 2 +-
backend/finance/views/invoices/single/view.py | 2 +-
backend/finance/views/receipts/dashboard.py | 2 +-
backend/middleware.py | 74 --
backend/migrations/0001_initial.py | 820 +++++++-----------
backend/migrations/0002_initial.py | 420 ---------
backend/modals.py | 10 +-
backend/models.py | 5 +-
backend/storage/api/delete.py | 4 +-
backend/storage/api/fetch.py | 2 +-
frontend/templates/base/htmx.html | 7 -
frontend/templates/core/auth/auth.html | 2 +-
.../{ => core}/base/+left_drawer.html | 0
frontend/templates/{ => core}/base/_head.html | 0
frontend/templates/{ => core}/base/base.html | 4 +-
.../{ => core}/base/breadcrumbs.html | 2 +-
.../{ => core}/base/breadcrumbs_ul.html | 0
frontend/templates/core/base/htmx.html | 7 +
frontend/templates/{ => core}/base/toast.html | 0
.../templates/{ => core}/base/toasts.html | 0
.../base/topbar/+icon_dropdown.html | 2 +-
.../base/topbar/_notification_count.html | 0
.../topbar/_notification_dropdown_items.html | 2 +-
.../base/topbar/_organizations_list.html | 0
.../{ => core}/base/topbar/_topbar.html | 0
.../base/topbar/team_selector/selector.html | 0
.../pages/clients/create/create.html | 14 +-
.../pages/clients/dashboard/dashboard.html | 2 +-
.../pages/clients/detail/dashboard.html | 12 +-
frontend/templates/pages/create_account.html | 156 ++--
frontend/templates/pages/dashboard.html | 2 +-
.../templates/pages/emails/dashboard.html | 16 +-
.../pages/file_storage/dashboard.html | 17 +-
.../templates/pages/file_storage/upload.html | 21 +-
frontend/templates/pages/forgot_password.html | 93 +-
.../pages/invoices/dashboard/core/main.html | 2 +-
.../recurring/dashboard/core/main.html | 2 +-
.../pages/invoices/single/edit/edit.html | 2 +-
.../single/manage_access/manage_access.html | 24 +-
.../pages/invoices/single/schedules/view.html | 4 +-
.../invoices/single/view/invoice_page.html | 334 +++----
.../templates/pages/landing/landing_base.html | 80 +-
frontend/templates/pages/login.html | 215 ++---
.../templates/pages/quotas/dashboard.html | 2 +-
frontend/templates/pages/quotas/list.html | 18 +-
.../templates/pages/quotas/view_requests.html | 70 +-
.../templates/pages/receipts/dashboard.html | 2 +-
.../templates/pages/reports/dashboard.html | 2 +-
.../pages/reports/monthly_report_base.html | 188 ++--
frontend/templates/pages/reset_password.html | 123 +--
frontend/templates/pages/settings/main.html | 2 +-
.../templates/pages/settings/teams/leave.html | 2 +-
.../pages/settings/teams/login_to_team.html | 9 +-
.../templates/pages/settings/teams/main.html | 90 +-
.../pages/settings/teams/permissions.html | 50 +-
poetry.lock | 9 +-
pyproject.toml | 3 +-
settings/local_settings.py | 4 +-
settings/settings.py | 20 +-
tests/api/test_clients.py | 12 +-
tests/views/test_clients.py | 2 +-
116 files changed, 1438 insertions(+), 2399 deletions(-)
delete mode 100644 backend/auth_backends.py
delete mode 100644 backend/decorators.py
delete mode 100644 backend/middleware.py
delete mode 100644 backend/migrations/0002_initial.py
delete mode 100644 frontend/templates/base/htmx.html
rename frontend/templates/{ => core}/base/+left_drawer.html (100%)
rename frontend/templates/{ => core}/base/_head.html (100%)
rename frontend/templates/{ => core}/base/base.html (53%)
rename frontend/templates/{ => core}/base/breadcrumbs.html (90%)
rename frontend/templates/{ => core}/base/breadcrumbs_ul.html (100%)
create mode 100644 frontend/templates/core/base/htmx.html
rename frontend/templates/{ => core}/base/toast.html (100%)
rename frontend/templates/{ => core}/base/toasts.html (100%)
rename frontend/templates/{ => core}/base/topbar/+icon_dropdown.html (96%)
rename frontend/templates/{ => core}/base/topbar/_notification_count.html (100%)
rename frontend/templates/{ => core}/base/topbar/_notification_dropdown_items.html (98%)
rename frontend/templates/{ => core}/base/topbar/_organizations_list.html (100%)
rename frontend/templates/{ => core}/base/topbar/_topbar.html (100%)
rename frontend/templates/{ => core}/base/topbar/team_selector/selector.html (100%)
diff --git a/backend/admin.py b/backend/admin.py
index 9b348839c..cd6793d8a 100644
--- a/backend/admin.py
+++ b/backend/admin.py
@@ -9,15 +9,13 @@
InvoiceProduct,
Receipt,
ReceiptDownloadToken,
+ FinanceDefaultValues,
)
-from backend.clients.models import Client, DefaultValues
-
# from django.contrib.auth.models imp/ort User
# admin.register(Invoice)
admin.site.register(
[
- Client,
Invoice,
InvoiceURL,
InvoiceItem,
@@ -26,7 +24,7 @@
ReceiptDownloadToken,
InvoiceReminder,
InvoiceRecurringProfile,
- DefaultValues,
+ FinanceDefaultValues,
]
)
diff --git a/backend/api/emails/fetch.py b/backend/api/emails/fetch.py
index fa1cf9cb1..dfc12b391 100644
--- a/backend/api/emails/fetch.py
+++ b/backend/api/emails/fetch.py
@@ -4,7 +4,7 @@
from django.shortcuts import render, redirect
from django_ratelimit.core import is_ratelimited
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.models import EmailSendStatus
from core.types.htmx import HtmxHttpRequest
diff --git a/backend/api/emails/send.py b/backend/api/emails/send.py
index 7c6bd86e5..968d3e578 100644
--- a/backend/api/emails/send.py
+++ b/backend/api/emails/send.py
@@ -16,8 +16,8 @@
from django.views.decorators.http import require_POST
from mypy_boto3_sesv2.type_defs import BulkEmailEntryResultTypeDef
-from backend.decorators import feature_flag_check, web_require_scopes
-from backend.decorators import htmx_only
+from core.decorators import feature_flag_check, web_require_scopes
+from core.decorators import htmx_only
from backend.models import Client
from backend.models import EmailSendStatus
@@ -82,7 +82,7 @@ def _send_bulk_email_view(request: WebRequest) -> HttpResponse:
if validated_bulk:
messages.error(request, validated_bulk)
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
message += email_footer()
message_single_line_html = message.replace("\r\n", "
").replace("\n", "
")
@@ -124,7 +124,7 @@ def _send_bulk_email_view(request: WebRequest) -> HttpResponse:
}
)
messages.success(request, f"Successfully emailed {len(email_list)} people.")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
EMAIL_SENT = send_templated_bulk_email(
email_list=email_list,
@@ -138,7 +138,7 @@ def _send_bulk_email_view(request: WebRequest) -> HttpResponse:
if EMAIL_SENT.failed:
messages.error(request, EMAIL_SENT.error)
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
# todo - fix
@@ -188,7 +188,7 @@ def _send_bulk_email_view(request: WebRequest) -> HttpResponse:
# except QuotaLimit.DoesNotExist:
# ...
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
def _send_single_email_view(request: WebRequest) -> HttpResponse:
@@ -205,7 +205,7 @@ def _send_single_email_view(request: WebRequest) -> HttpResponse:
if validated_single:
messages.error(request, validated_single)
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
message += email_footer()
message_single_line_html = message.replace("\r\n", "
").replace("\n", "
")
@@ -249,7 +249,7 @@ def _send_single_email_view(request: WebRequest) -> HttpResponse:
# QuotaUsage.create_str(request.user, "emails-single-count", status_object.id)
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
def validate_bulk_inputs(*, request, emails, clients, message, subject) -> str | None:
diff --git a/backend/api/emails/status.py b/backend/api/emails/status.py
index 218522db5..6df99aa32 100644
--- a/backend/api/emails/status.py
+++ b/backend/api/emails/status.py
@@ -8,7 +8,7 @@
from django_ratelimit.core import is_ratelimited
from mypy_boto3_sesv2.type_defs import GetMessageInsightsResponseTypeDef, InsightsEventTypeDef
-from backend.decorators import htmx_only, feature_flag_check, web_require_scopes
+from core.decorators import htmx_only, feature_flag_check, web_require_scopes
from backend.models import EmailSendStatus
from core.types.htmx import HtmxHttpRequest
from settings.helpers import EMAIL_CLIENT
@@ -26,13 +26,13 @@ def get_status_view(request: HtmxHttpRequest, status_id: str) -> HttpResponse:
EMAIL_STATUS = EmailSendStatus.objects.get(user=request.user, id=status_id)
except EmailSendStatus.DoesNotExist:
messages.error(request, "Status not found")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
message_insight = get_message_insights(message_id=EMAIL_STATUS.aws_message_id) # type: ignore[arg-type]
if isinstance(message_insight, str):
messages.error(request, message_insight)
- return render(request, "base/toast.html", {"autohide": False})
+ return render(request, "core/base/toast.html", {"autohide": False})
important_info = get_important_info_from_response(message_insight)
@@ -41,7 +41,7 @@ def get_status_view(request: HtmxHttpRequest, status_id: str) -> HttpResponse:
EMAIL_STATUS.save()
messages.success(request, f"Status updated to {important_info['status']}")
- return render(request, "base/toast.html", {"autohide": False})
+ return render(request, "core/base/toast.html", {"autohide": False})
@require_POST
@@ -52,7 +52,7 @@ def refresh_all_statuses_view(request: HtmxHttpRequest) -> HttpResponse:
request, group="email-refresh_all_statuses", key="user", rate="1/m", increment=True
):
messages.error(request, "Woah, slow down! Refreshing the statuses takes a while, give us a break!")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
if request.user.logged_in_as_team:
ALL_STATUSES = EmailSendStatus.objects.filter(organization=request.user.logged_in_as_team)
else:
diff --git a/backend/api/public/endpoints/invoices/create.py b/backend/api/public/endpoints/invoices/create.py
index 0cb04a737..a00f28e44 100644
--- a/backend/api/public/endpoints/invoices/create.py
+++ b/backend/api/public/endpoints/invoices/create.py
@@ -4,7 +4,7 @@
from rest_framework.decorators import api_view
from rest_framework.response import Response
-from backend.clients.models import Client
+from backend.models import Client
from core.api.public.decorators import require_scopes
from core.api.public.helpers.response import APIResponse
from backend.api.public.serializers.invoices import InvoiceSerializer
diff --git a/backend/api/settings/defaults.py b/backend/api/settings/defaults.py
index df235bb22..12f186703 100644
--- a/backend/api/settings/defaults.py
+++ b/backend/api/settings/defaults.py
@@ -4,7 +4,7 @@
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
-from backend.clients.models import Client
+from backend.models import Client
from backend.finance.service.clients.validate import validate_client
from backend.finance.service.defaults.get import get_account_defaults
from backend.finance.service.defaults.update import change_client_defaults
@@ -62,10 +62,10 @@ def change_client_defaults_endpoint(request: WebRequest, client_id: int | None =
if response.failed:
messages.error(request, response.error)
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
messages.success(request, "Successfully updated client defaults")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
@require_http_methods(["DELETE"])
@@ -85,11 +85,11 @@ def remove_client_default_logo_endpoint(request: WebRequest, client_id: int | No
if not defaults.default_invoice_logo:
messages.error(request, "No default logo to remove")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
defaults.default_invoice_logo.delete()
messages.success(request, "Successfully updated client defaults")
- resp = render(request, "base/toast.html")
+ resp = render(request, "core/base/toast.html")
resp["HX-Refresh"] = "true"
return resp
diff --git a/backend/api/settings/email_templates.py b/backend/api/settings/email_templates.py
index a54fe39be..77fc0ff9b 100644
--- a/backend/api/settings/email_templates.py
+++ b/backend/api/settings/email_templates.py
@@ -3,7 +3,7 @@
from django.views.decorators.http import require_POST
from backend.finance.service.defaults.get import get_account_defaults
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from core.types.requests import WebRequest
@@ -15,11 +15,11 @@ def save_email_template(request: WebRequest, template: str):
if template not in ["invoice_created", "invoice_overdue", "invoice_cancelled"]:
messages.error(request, f"Invalid template: {template}")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
if content is None:
messages.error(request, f"Missing content for template: {template}")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
acc_defaults = get_account_defaults(request.actor)
@@ -29,4 +29,4 @@ def save_email_template(request: WebRequest, template: str):
messages.success(request, f"Email template '{template}' saved successfully")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
diff --git a/backend/auth_backends.py b/backend/auth_backends.py
deleted file mode 100644
index e9f1e6fa9..000000000
--- a/backend/auth_backends.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from django.contrib.auth import get_user_model
-from django.contrib.auth.backends import ModelBackend
-
-
-class EmailInsteadOfUsernameBackend(ModelBackend):
- def authenticate(self, request, username=None, password=None, **kwargs):
- UserModel = get_user_model()
-
- if username is None or password is None:
- return
-
- try:
- user = UserModel.objects.get(email=username)
- except UserModel.DoesNotExist:
- # Run the default password hasher once to reduce the timing
- # difference between an existing and a nonexistent user (#20760).
- UserModel().set_password(password)
- return None
- else:
- if user.check_password(password):
- return user
- return None
diff --git a/backend/clients/api/delete.py b/backend/clients/api/delete.py
index a0416f220..8cb9d4a8c 100644
--- a/backend/clients/api/delete.py
+++ b/backend/clients/api/delete.py
@@ -2,7 +2,7 @@
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.service.clients.delete import delete_client, DeleteClientServiceResponse
from core.types.requests import WebRequest
@@ -16,4 +16,4 @@ def client_delete(request: WebRequest, id: int):
messages.error(request, response.error)
else:
messages.success(request, f"Successfully deleted client #{id}")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
diff --git a/backend/clients/api/fetch.py b/backend/clients/api/fetch.py
index bbbac23ed..d5ebef01a 100644
--- a/backend/clients/api/fetch.py
+++ b/backend/clients/api/fetch.py
@@ -1,8 +1,8 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
-from backend.clients.models import Client
+from core.decorators import web_require_scopes
+from backend.models import Client
from backend.finance.service.clients.get import fetch_clients, FetchClientServiceResponse
from core.types.htmx import HtmxHttpRequest
from core.types.requests import WebRequest
diff --git a/backend/clients/clients.py b/backend/clients/clients.py
index adf7cbb68..231981f46 100644
--- a/backend/clients/clients.py
+++ b/backend/clients/clients.py
@@ -3,7 +3,7 @@
from django.dispatch import receiver
from django.db.models.signals import post_save
-from backend.clients.models import Client, DefaultValues
+from backend.models import Client, FinanceDefaultValues
logger = logging.getLogger(__name__)
@@ -16,11 +16,11 @@ def create_client_defaults(sender: Type[Client], instance: Client, created, **kw
logger.info(f"Creating client defaults for client #{instance.id}")
if instance.user:
- account_defaults, _ = DefaultValues.objects.get_or_create(user=instance.owner, client=None)
+ account_defaults, _ = FinanceDefaultValues.objects.get_or_create(user=instance.owner, client=None)
else:
- account_defaults, _ = DefaultValues.objects.get_or_create(organization=instance.owner, client=None)
+ account_defaults, _ = FinanceDefaultValues.objects.get_or_create(organization=instance.owner, client=None)
- defaults = DefaultValues.objects.create(client=instance, owner=instance.owner) # type: ignore[misc]
+ defaults = FinanceDefaultValues.objects.create(client=instance, owner=instance.owner) # type: ignore[misc]
defaults.invoice_date_value = account_defaults.invoice_date_value
defaults.invoice_date_type = account_defaults.invoice_date_type
diff --git a/backend/clients/models.py b/backend/clients/models.py
index 19e5dabbd..391471bce 100644
--- a/backend/clients/models.py
+++ b/backend/clients/models.py
@@ -7,101 +7,73 @@
recurring_invoices_invoice_overdue_default_email_template,
recurring_invoices_invoice_cancelled_default_email_template,
)
-from backend.models import OwnerBase, User, UserSettings, _private_storage
-
-
-class Client(OwnerBase):
- active = models.BooleanField(default=True)
- name = models.CharField(max_length=64)
- phone_number = models.CharField(max_length=100, blank=True, null=True)
- email = models.EmailField(blank=True, null=True)
- email_verified = models.BooleanField(default=False)
- company = models.CharField(max_length=100, blank=True, null=True)
- contact_method = models.CharField(max_length=100, blank=True, null=True)
- is_representative = models.BooleanField(default=False)
-
- address = models.TextField(max_length=100, blank=True, null=True)
- city = models.CharField(max_length=100, blank=True, null=True)
- country = models.CharField(max_length=100, blank=True, null=True)
-
- def __str__(self):
- return self.name
-
- def has_access(self, user: User) -> bool:
- if not user.is_authenticated:
- return False
-
- if user.logged_in_as_team:
- return self.organization == user.logged_in_as_team
- else:
- return self.user == user
-
-
-class DefaultValues(OwnerBase):
- class InvoiceDueDateType(models.TextChoices):
- days_after = "days_after" # days after issue
- date_following = "date_following" # date of following month
- date_current = "date_current" # date of current month
-
- class InvoiceDateType(models.TextChoices):
- day_of_month = "day_of_month"
- days_after = "days_after"
-
- client = models.OneToOneField(Client, on_delete=models.CASCADE, related_name="default_values", null=True, blank=True)
-
- currency = models.CharField(
- max_length=3,
- default="GBP",
- choices=[(code, info["name"]) for code, info in UserSettings.CURRENCIES.items()],
- )
-
- invoice_due_date_value = models.PositiveSmallIntegerField(default=7, null=False, blank=False)
- invoice_due_date_type = models.CharField(max_length=20, choices=InvoiceDueDateType.choices, default=InvoiceDueDateType.days_after)
-
- invoice_date_value = models.PositiveSmallIntegerField(default=15, null=False, blank=False)
- invoice_date_type = models.CharField(max_length=20, choices=InvoiceDateType.choices, default=InvoiceDateType.day_of_month)
-
- invoice_from_name = models.CharField(max_length=100, null=True, blank=True)
- invoice_from_company = models.CharField(max_length=100, null=True, blank=True)
- invoice_from_address = models.CharField(max_length=100, null=True, blank=True)
- invoice_from_city = models.CharField(max_length=100, null=True, blank=True)
- invoice_from_county = models.CharField(max_length=100, null=True, blank=True)
- invoice_from_country = models.CharField(max_length=100, null=True, blank=True)
- invoice_from_email = models.CharField(max_length=100, null=True, blank=True)
-
- invoice_account_number = models.CharField(max_length=100, null=True, blank=True)
- invoice_sort_code = models.CharField(max_length=100, null=True, blank=True)
- invoice_account_holder_name = models.CharField(max_length=100, null=True, blank=True)
-
- email_template_recurring_invoices_invoice_created = models.TextField(default=recurring_invoices_invoice_created_default_email_template)
- email_template_recurring_invoices_invoice_overdue = models.TextField(default=recurring_invoices_invoice_overdue_default_email_template)
- email_template_recurring_invoices_invoice_cancelled = models.TextField(
- default=recurring_invoices_invoice_cancelled_default_email_template
- )
-
- def get_issue_and_due_dates(self, issue_date: date | str | None = None) -> tuple[str, str]:
- due: date
- issue: date
-
- if isinstance(issue_date, str):
- issue = date.fromisoformat(issue_date) or date.today()
- else:
- issue = issue_date or date.today()
-
- match self.invoice_due_date_type:
- case self.InvoiceDueDateType.days_after:
- due = issue + timedelta(days=self.invoice_due_date_value)
- case self.InvoiceDueDateType.date_following:
- due = date(issue.year, issue.month + 1, self.invoice_due_date_value)
- case self.InvoiceDueDateType.date_current:
- due = date(issue.year, issue.month, self.invoice_due_date_value)
- case _:
- raise ValueError("Invalid invoice due date type")
- return date.isoformat(issue), date.isoformat(due)
-
- default_invoice_logo = models.ImageField(
- upload_to="invoice_logos/",
- storage=_private_storage,
- blank=True,
- null=True,
- )
+from backend.models import _private_storage, DefaultValuesBase
+
+# class FinanceDefaultValues(DefaultValuesBase):
+# class InvoiceDueDateType(models.TextChoices):
+# days_after = "days_after"
+# date_following = "date_following"
+# date_current = "date_current"
+#
+# class InvoiceDateType(models.TextChoices):
+# day_of_month = "day_of_month"
+# days_after = "days_after"
+#
+# invoice_due_date_value = models.PositiveSmallIntegerField(default=7, null=False, blank=False)
+# invoice_due_date_type = models.CharField(
+# max_length=20,
+# choices=InvoiceDueDateType.choices,
+# default=InvoiceDueDateType.days_after,
+# )
+#
+# invoice_date_value = models.PositiveSmallIntegerField(default=15, null=False, blank=False)
+# invoice_date_type = models.CharField(
+# max_length=20,
+# choices=InvoiceDateType.choices,
+# default=InvoiceDateType.day_of_month,
+# )
+#
+# invoice_from_name = models.CharField(max_length=100, null=True, blank=True)
+# invoice_from_company = models.CharField(max_length=100, null=True, blank=True)
+# invoice_from_address = models.CharField(max_length=100, null=True, blank=True)
+# invoice_from_city = models.CharField(max_length=100, null=True, blank=True)
+# invoice_from_county = models.CharField(max_length=100, null=True, blank=True)
+# invoice_from_country = models.CharField(max_length=100, null=True, blank=True)
+# invoice_from_email = models.CharField(max_length=100, null=True, blank=True)
+#
+# invoice_account_number = models.CharField(max_length=100, null=True, blank=True)
+# invoice_sort_code = models.CharField(max_length=100, null=True, blank=True)
+# invoice_account_holder_name = models.CharField(max_length=100, null=True, blank=True)
+#
+# email_template_recurring_invoices_invoice_created = models.TextField(default=recurring_invoices_invoice_created_default_email_template)
+# email_template_recurring_invoices_invoice_overdue = models.TextField(default=recurring_invoices_invoice_overdue_default_email_template)
+# email_template_recurring_invoices_invoice_cancelled = models.TextField(
+# default=recurring_invoices_invoice_cancelled_default_email_template
+# )
+#
+# default_invoice_logo = models.ImageField(
+# upload_to="invoice_logos/",
+# storage=_private_storage,
+# blank=True,
+# null=True,
+# )
+#
+# def get_issue_and_due_dates(self, issue_date: date | str | None = None) -> tuple[str, str]:
+# due: date
+# issue: date
+#
+# if isinstance(issue_date, str):
+# issue = date.fromisoformat(issue_date) or date.today()
+# else:
+# issue = issue_date or date.today()
+#
+# match self.invoice_due_date_type:
+# case self.InvoiceDueDateType.days_after:
+# due = issue + timedelta(days=self.invoice_due_date_value)
+# case self.InvoiceDueDateType.date_following:
+# due = date(issue.year, issue.month + 1, self.invoice_due_date_value)
+# case self.InvoiceDueDateType.date_current:
+# due = date(issue.year, issue.month, self.invoice_due_date_value)
+# case _:
+# raise ValueError("Invalid invoice due date type")
+# return date.isoformat(issue), date.isoformat(due)
diff --git a/backend/clients/views/create.py b/backend/clients/views/create.py
index b9caf81e9..8a2dec93b 100644
--- a/backend/clients/views/create.py
+++ b/backend/clients/views/create.py
@@ -1,7 +1,7 @@
from django.contrib import messages
from django.shortcuts import render, redirect
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.service.clients.create import create_client, CreateClientServiceResponse
from core.types.requests import WebRequest
diff --git a/backend/clients/views/dashboard.py b/backend/clients/views/dashboard.py
index eddaaa869..ab1ffd18b 100644
--- a/backend/clients/views/dashboard.py
+++ b/backend/clients/views/dashboard.py
@@ -1,6 +1,6 @@
from django.shortcuts import render
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from core.types.htmx import HtmxHttpRequest
diff --git a/backend/clients/views/detail.py b/backend/clients/views/detail.py
index 70e62fafd..574bce923 100644
--- a/backend/clients/views/detail.py
+++ b/backend/clients/views/detail.py
@@ -4,11 +4,11 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.service.clients.delete import delete_client, DeleteClientServiceResponse
from backend.finance.service.clients.validate import validate_client
from core.types.requests import WebRequest
-from backend.clients.models import Client
+from backend.models import Client
@require_http_methods(["GET"])
diff --git a/backend/context_processors.py b/backend/context_processors.py
index 4dbac686c..5922925aa 100644
--- a/backend/context_processors.py
+++ b/backend/context_processors.py
@@ -1,74 +1,12 @@
-from typing import List, Optional, Dict, Any
-
-from core.service.base.breadcrumbs import get_breadcrumbs
-from django.http import HttpRequest
-from django.urls import reverse
-
-import calendar
-
-from settings.helpers import get_var, BASE_DIR
+from typing import Dict, Any
+from core.types.requests import WebRequest
from backend import __version__
-from settings.settings import DEBUG
-
-
-## Context processors need to be put in SETTINGS TEMPLATES to be recognized
-def navbar(request):
- # cached_navbar_items = cache.get("navbar_items")
- # if cached_navbar_items is None:
- # navbar_items = load_navbar_items()
- #
- # # Cache the sidebar items for a certain time (e.g., 3600 seconds = 1 hr)
- # cache.set("navbar_items", navbar_items, 60 * 60 * 3) # 3 hrs
- # else:
- # navbar_items = cached_navbar_items
- # context = {"navbar_items": navbar_items}
- return {}
-
-def extras(request: HttpRequest):
- # import_method can be one of: "webpack", "public_cdn", "custom_cdn"
+def extras(request: WebRequest):
data: Dict[str, Any] = {}
- import pathlib
-
- def get_git_revision(base_path):
- if not DEBUG:
- return "prod"
-
- git_dir = pathlib.Path(base_path) / ".git"
-
- # check file exists
-
- if not git_dir.exists() or not git_dir.is_dir() or not (git_dir / "HEAD").exists():
- return "commit not found"
-
- with (git_dir / "HEAD").open("r") as head:
- ref = head.readline().split(" ")[-1].strip()
-
- if not (git_dir / ref).exists():
- return "commit not found"
-
- with (git_dir / ref).open("r") as git_hash:
- return git_hash.readline().strip()
-
- data["version"] = __version__
- data["git_branch"] = get_var("BRANCH")
- data["git_version"] = get_git_revision(BASE_DIR)
- data["import_method"] = get_var("IMPORT_METHOD", default="webpack")
- data["analytics"] = get_var("ANALYTICS_STRING")
- data["calendar_util"] = calendar
- data["day_names_sunday_first"] = [calendar.day_name[(i + 6) % 7] for i in range(7)]
- data["day_names_monday_first"] = [day for day in calendar.day_name]
-
- if hasattr(request, "htmx") and request.htmx.boosted:
- data["base"] = "base/htmx.html"
- else:
- data["base"] = "base/base.html"
+ data["finances_version"] = __version__
return data
-
-
-def breadcrumbs(request: HttpRequest):
- return get_breadcrumbs(request=request)
diff --git a/backend/decorators.py b/backend/decorators.py
deleted file mode 100644
index ec65eb6a2..000000000
--- a/backend/decorators.py
+++ /dev/null
@@ -1,272 +0,0 @@
-from __future__ import annotations
-
-import logging
-from functools import wraps
-from typing import TypedDict
-
-from django.contrib import messages
-from django.http import HttpResponse
-from django.http import HttpResponseRedirect
-from django.shortcuts import redirect
-from django.shortcuts import render
-from django.urls import reverse
-
-from backend.models import TeamMemberPermission
-from core.types.requests import WebRequest
-from core.utils.feature_flags import get_feature_status
-
-logger = logging.getLogger(__name__)
-
-
-def not_authenticated(view_func):
- def wrapper_func(request, *args, **kwargs):
- if request.user.is_authenticated:
- return redirect("dashboard")
- else:
- return view_func(request, *args, **kwargs)
-
- return wrapper_func
-
-
-def staff_only(view_func):
- def wrapper_func(request, *args, **kwargs):
- if request.user.is_staff and request.user.is_authenticated:
- return view_func(request, *args, **kwargs)
- else:
- messages.error(request, "You don't have permission to view this page.")
- return redirect("dashboard")
-
- return wrapper_func
-
-
-def superuser_only(view_func):
- def wrapper_func(request, *args, **kwargs):
- if request.user.is_authenticated and request.user.is_superuser:
- return view_func(request, *args, **kwargs)
- else:
- messages.error(request, "You don't have permission to view this page.")
- return redirect("dashboard")
-
- return wrapper_func
-
-
-def htmx_only(viewname: str = "dashboard"):
- def decorator(view_func):
- def wrapper_func(request, *args, **kwargs):
- if request.htmx:
- return view_func(request, *args, **kwargs)
- else:
- return redirect(viewname)
-
- return wrapper_func
-
- return decorator
-
-
-def hx_boost(view):
- """
- Decorator for HTMX requests.
-
- used by wrapping FBV in @hx_boost and adding **kwargs to param
- then you can use context = kwargs.get("context", {}) to continue and then it will handle HTMX boosts
- """
-
- @wraps(view)
- def wrapper(request, *args, **kwargs):
- if request.htmx.boosted:
- kwargs["context"] = kwargs.get("context", {}) | {"base": "base/htmx.html"}
- return view(request, *args, **kwargs)
-
- return wrapper
-
-
-def feature_flag_check(flag, status=True, api=False, htmx=False):
- def decorator(view_func):
- @wraps(view_func)
- def wrapper(request, *args, **kwargs):
- feat_status = get_feature_status(flag)
-
- if feat_status == status:
- return view_func(request, *args, **kwargs)
-
- if api and htmx:
- messages.error(request, "This feature is currently disabled.")
- return render(request, "base/toasts.html")
- elif api:
- return HttpResponse(status=403, content="This feature is currently disabled.")
- messages.error(request, "This feature is currently disabled.")
- try:
- last_visited_url = request.session["last_visited"]
- current_url = request.build_absolute_uri()
- if last_visited_url != current_url:
- return HttpResponseRedirect(last_visited_url)
- except KeyError:
- pass
- return HttpResponseRedirect(reverse("dashboard"))
-
- return wrapper
-
- return decorator
-
-
-class FlagItem(TypedDict):
- name: str
- desired: bool
-
-
-def feature_flag_check_multi(flag_list: list[FlagItem], api=False, htmx=False):
- """
- Checks if at least one of the flags in the list is the desired status
- """
-
- def decorator(view_func):
- @wraps(view_func)
- def wrapper(request, *args, **kwargs):
- if not any(get_feature_status(flag["name"]) == flag["desired"] for flag in flag_list):
- if api and htmx:
- messages.error(request, "This feature is currently disabled.")
- return render(request, "base/toasts.html")
- elif api:
- return HttpResponse(status=403, content="This feature is currently disabled.")
- messages.error(request, "This feature is currently disabled.")
- return HttpResponseRedirect(request.META.get("HTTP_REFERER"))
-
- return view_func(request, *args, **kwargs)
-
- return wrapper
-
- return decorator
-
-
-# def quota_usage_check(limit: str | QuotaLimit, extra_data: str | int | None = None, api=False, htmx=False):
-# def decorator(view_func):
-# @wraps(view_func)
-# def wrapper(request, *args, **kwargs):
-# try:
-# quota_limit = QuotaLimit.objects.get(slug=limit) if isinstance(limit, str) else limit
-# except QuotaLimit.DoesNotExist:
-# return view_func(request, *args, **kwargs)
-#
-# if not quota_limit.strict_goes_above_limit(request.user, extra=extra_data):
-# return view_func(request, *args, **kwargs)
-#
-# if api and htmx:
-# messages.error(request, f"You have reached the quota limit for this service '{quota_limit.slug}'")
-# return render(request, "partials/messages_list.html", {"autohide": False})
-# elif api:
-# return HttpResponse(status=403, content=f"You have reached the quota limit for this service '{quota_limit.slug}'")
-# messages.error(request, f"You have reached the quota limit for this service '{quota_limit.slug}'")
-# try:
-# last_visited_url = request.session["last_visited"]
-# current_url = request.build_absolute_uri()
-# if last_visited_url != current_url:
-# return HttpResponseRedirect(last_visited_url)
-# except KeyError:
-# pass
-# return HttpResponseRedirect(reverse("dashboard"))
-#
-# return wrapper
-#
-# return decorator
-
-
-not_logged_in = not_authenticated
-logged_out = not_authenticated
-
-
-def web_require_scopes(scopes: str | list[str], htmx=False, api=False, redirect_url=None):
- """
- Only to be used by WebRequests (htmx or html) NOT PUBLIC API
- """
-
- def decorator(view_func):
- @wraps(view_func)
- def _wrapped_view(request: WebRequest, *args, **kwargs):
- if request.team_id and not request.team:
- return return_error(request, "Team not found")
-
- if request.team:
- # Check for team permissions based on team_id and scopes
- if not request.team.is_owner(request.user):
- team_permissions = TeamMemberPermission.objects.filter(team=request.team, user=request.user).first()
-
- if not team_permissions:
- return return_error(request, "You do not have permission to perform this action (no permissions for team)")
-
- # single scope
- if isinstance(scopes, str) and scopes not in team_permissions.scopes:
- return return_error(request, f"You do not have permission to perform this action ({scopes})")
-
- # scope list
- if isinstance(scopes, list):
- for scope in scopes:
- if scope not in team_permissions.scopes:
- return return_error(request, f"You do not have permission to perform this action ({scope})")
- return view_func(request, *args, **kwargs)
-
- _wrapped_view.required_scopes = scopes
- return _wrapped_view
-
- def return_error(request: WebRequest, msg: str):
- logging.info(f"User does not have permission to perform this action (User ID: {request.user.id}, Scopes: {scopes})")
- if api and htmx:
- messages.error(request, msg)
- return render(request, "base/toast.html", {"autohide": False})
- elif api:
- return HttpResponse(status=403, content=msg)
- elif request.htmx:
- messages.error(request, msg)
- resp = HttpResponse(status=200)
-
- try:
- last_visited_url = request.session["last_visited"]
- current_url = request.build_absolute_uri()
- if last_visited_url != current_url:
- resp["HX-Replace-Url"] = last_visited_url
- except KeyError:
- ...
- resp["HX-Refresh"] = "true"
- return resp
-
- messages.error(request, msg)
-
- try:
- last_visited_url = request.session["last_visited"]
- current_url = request.build_absolute_uri()
- if last_visited_url != current_url:
- return HttpResponseRedirect(last_visited_url)
- except KeyError:
- pass
-
- if not redirect_url:
- return HttpResponseRedirect(reverse("dashboard"))
-
- try:
- return HttpResponseRedirect(reverse(redirect_url))
- except KeyError:
- return HttpResponseRedirect(reverse("dashboard"))
-
- return decorator
-
-
-# wrapper around billing has_entitlements only load
-
-from django.conf import settings
-
-
-def has_entitlements(entitlements: list[str] | str, htmx_api: bool = False):
- def decorator(view_func):
- @wraps(view_func)
- def wrapper(request, *args, **kwargs):
- if settings.BILLING_ENABLED:
- from billing.decorators import has_entitlements_called_from_backend_handler
-
- wrapped_view_func = has_entitlements_called_from_backend_handler(
- entitlements if isinstance(entitlements, list) else [entitlements], htmx_api
- )(view_func)
- return wrapped_view_func(request, *args, **kwargs)
- return view_func(request, *args, **kwargs)
-
- return wrapper
-
- return decorator
diff --git a/backend/finance/api/invoices/delete.py b/backend/finance/api/invoices/delete.py
index 9f18fb340..2dd33e720 100644
--- a/backend/finance/api/invoices/delete.py
+++ b/backend/finance/api/invoices/delete.py
@@ -5,7 +5,7 @@
from django.urls.exceptions import Resolver404
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.models import Invoice
from core.types.htmx import HtmxHttpRequest
@@ -21,11 +21,11 @@ def delete_invoice(request: HtmxHttpRequest):
invoice = Invoice.objects.get(id=delete_items.get("invoice", ""))
except Invoice.DoesNotExist:
messages.error(request, "Invoice Not Found")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
if not invoice.has_access(request.user):
messages.error(request, "You do not have permission to delete this invoice")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
QuotaLimit.delete_quota_usage("invoices-count", request.user, invoice.id, invoice.date_created)
@@ -34,7 +34,7 @@ def delete_invoice(request: HtmxHttpRequest):
if request.htmx:
if not redirect:
messages.success(request, "Invoice deleted")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
try:
resolve(redirect)
diff --git a/backend/finance/api/invoices/edit.py b/backend/finance/api/invoices/edit.py
index c99bc08d7..4b2ff180c 100644
--- a/backend/finance/api/invoices/edit.py
+++ b/backend/finance/api/invoices/edit.py
@@ -6,7 +6,7 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods, require_POST
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import Invoice
from core.types.htmx import HtmxHttpRequest
@@ -61,14 +61,14 @@ def edit_invoice(request: HtmxHttpRequest):
new_value = datetime.strptime(new_value, "%Y-%m-%d").date() # type: ignore[assignment]
except ValueError:
messages.error(request, "Invalid date format for date_due")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
setattr(invoice, column_name, new_value)
invoice.save()
if request.htmx:
messages.success(request, "Invoice edited")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
return JsonResponse({"message": "Invoice successfully edited"}, status=200)
@@ -141,14 +141,14 @@ def edit_discount(request: HtmxHttpRequest, invoice_id: str):
messages.success(request, "Discount was applied successfully")
- response = render(request, "base/toasts.html")
+ response = render(request, "core/base/toasts.html")
response["HX-Trigger"] = "update_invoice"
return response
def return_message(request: HttpRequest, message: str, success: bool = True) -> HttpResponse:
send_message(request, message, success)
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
def send_message(request: HttpRequest, message: str, success: bool = False) -> None:
diff --git a/backend/finance/api/invoices/fetch.py b/backend/finance/api/invoices/fetch.py
index ccbdb0bb7..1dda518d9 100644
--- a/backend/finance/api/invoices/fetch.py
+++ b/backend/finance/api/invoices/fetch.py
@@ -1,7 +1,7 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import Invoice
from core.types.htmx import HtmxHttpRequest
from backend.finance.service.invoices.common.fetch import get_context
diff --git a/backend/finance/api/invoices/manage.py b/backend/finance/api/invoices/manage.py
index e917df75d..450a76dcf 100644
--- a/backend/finance/api/invoices/manage.py
+++ b/backend/finance/api/invoices/manage.py
@@ -6,7 +6,7 @@
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import Invoice
from core.types.htmx import HtmxHttpRequest
diff --git a/backend/finance/api/invoices/recurring/delete.py b/backend/finance/api/invoices/recurring/delete.py
index a5c857da8..7d9a6dbd1 100644
--- a/backend/finance/api/invoices/recurring/delete.py
+++ b/backend/finance/api/invoices/recurring/delete.py
@@ -5,7 +5,7 @@
from django.urls.exceptions import Resolver404
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import InvoiceRecurringProfile
from backend.boto3.async_tasks.tasks import Task
from backend.boto3.scheduler.delete_schedule import delete_boto_schedule
@@ -23,11 +23,11 @@ def delete_invoice_recurring_profile_endpoint(request: WebRequest):
invoice_profile = InvoiceRecurringProfile.objects.get(id=delete_items.get("invoice_profile", ""))
except InvoiceRecurringProfile.DoesNotExist:
messages.error(request, "Invoice recurring profile Not Found")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
if not invoice_profile.has_access(request.user):
messages.error(request, "You do not have permission to delete this Invoice recurring profile")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
# QuotaLimit.delete_quota_usage("invoices-count", request.user, invoice.id, invoice.date_created)
@@ -39,7 +39,7 @@ def delete_invoice_recurring_profile_endpoint(request: WebRequest):
if request.htmx:
if not redirect:
messages.success(request, "Invoice profile deleted")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
try:
resolve(redirect)
diff --git a/backend/finance/api/invoices/recurring/edit.py b/backend/finance/api/invoices/recurring/edit.py
index 228e0aeb2..bb75c23b6 100644
--- a/backend/finance/api/invoices/recurring/edit.py
+++ b/backend/finance/api/invoices/recurring/edit.py
@@ -5,7 +5,7 @@
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes, has_entitlements
+from core.decorators import web_require_scopes, has_entitlements
from backend.finance.models import InvoiceRecurringProfile
from backend.finance.service.invoices.recurring.get import get_invoice_profile
from backend.finance.service.invoices.recurring.validate.frequencies import validate_and_update_frequency
@@ -20,7 +20,7 @@ def edit_invoice_recurring_profile_endpoint(request: WebRequest, invoice_profile
if invoice_profile_response.failed:
messages.error(request, invoice_profile_response.error)
- return render(request, "base/toasts.html", {"autohide": False})
+ return render(request, "core/base/toasts.html", {"autohide": False})
invoice_profile: InvoiceRecurringProfile = invoice_profile_response.response
@@ -34,7 +34,7 @@ def edit_invoice_recurring_profile_endpoint(request: WebRequest, invoice_profile
if frequency_update_response.failed:
messages.error(request, frequency_update_response.error)
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
attributes_to_update = {
"date_due": request.POST.get("date_due"),
@@ -67,13 +67,13 @@ def edit_invoice_recurring_profile_endpoint(request: WebRequest, invoice_profile
new_value = datetime.strptime(new_value, "%Y-%m-%d").date() # type: ignore[assignment]
except ValueError:
messages.error(request, "Invalid date format for date_due")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
setattr(invoice_profile, column_name, new_value)
invoice_profile.save()
if request.htmx:
messages.success(request, "Successfully saved profile!")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
return JsonResponse({"message": "Invoice successfully edited"}, status=200)
diff --git a/backend/finance/api/invoices/recurring/fetch.py b/backend/finance/api/invoices/recurring/fetch.py
index ec5cc5619..e669694ba 100644
--- a/backend/finance/api/invoices/recurring/fetch.py
+++ b/backend/finance/api/invoices/recurring/fetch.py
@@ -2,7 +2,7 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import InvoiceRecurringProfile
from backend.finance.service.invoices.common.fetch import get_context
from core.types.requests import WebRequest
diff --git a/backend/finance/api/invoices/recurring/generate_next_invoice_now.py b/backend/finance/api/invoices/recurring/generate_next_invoice_now.py
index 4c85ed88c..cb03aff1a 100644
--- a/backend/finance/api/invoices/recurring/generate_next_invoice_now.py
+++ b/backend/finance/api/invoices/recurring/generate_next_invoice_now.py
@@ -2,7 +2,7 @@
from django.shortcuts import render
from django.views.decorators.http import require_POST
-from backend.decorators import web_require_scopes, htmx_only
+from core.decorators import web_require_scopes, htmx_only
from backend.finance.models import InvoiceRecurringProfile
from backend.finance.service.defaults.get import get_account_defaults
from backend.finance.service.invoices.recurring.generation.next_invoice import safe_generate_next_invoice_service
@@ -24,7 +24,7 @@ def generate_next_invoice_now_endpoint(request: WebRequest, invoice_profile_id):
if not invoice_recurring_profile:
messages.error(request, "Failed to fetch next invoice; cannot find Invoice recurring profile.")
- return render(request, "base/toast.html", {"autohide": False})
+ return render(request, "core/base/toast.html", {"autohide": False})
if invoice_recurring_profile.client_to:
account_defaults = get_account_defaults(invoice_recurring_profile.owner, invoice_recurring_profile.client_to)
@@ -33,7 +33,7 @@ def generate_next_invoice_now_endpoint(request: WebRequest, invoice_profile_id):
if not invoice_recurring_profile.has_access(request.user):
messages.error(request, "You do not have permission to modify this invoice recurring profile.")
- return render(request, "base/toast.html", {"autohide": False})
+ return render(request, "core/base/toast.html", {"autohide": False})
next_invoice_issue_date = invoice_recurring_profile.next_invoice_issue_date()
@@ -59,4 +59,4 @@ def generate_next_invoice_now_endpoint(request: WebRequest, invoice_profile_id):
else:
logger.info(svc_resp.error)
messages.error(request, f"Failed to fetch next invoice; {svc_resp.error}")
- return render(request, "base/toast.html", {"autohide": False})
+ return render(request, "core/base/toast.html", {"autohide": False})
diff --git a/backend/finance/api/invoices/recurring/poll.py b/backend/finance/api/invoices/recurring/poll.py
index 00e3bcc56..a668dbd97 100644
--- a/backend/finance/api/invoices/recurring/poll.py
+++ b/backend/finance/api/invoices/recurring/poll.py
@@ -5,7 +5,7 @@
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes, htmx_only
+from core.decorators import web_require_scopes, htmx_only
from backend.finance.models import InvoiceRecurringProfile
from backend.boto3.async_tasks.tasks import Task
from backend.boto3.scheduler.create_schedule import create_boto_schedule
diff --git a/backend/finance/api/invoices/recurring/update_status.py b/backend/finance/api/invoices/recurring/update_status.py
index 3c41c7da7..5e691c4de 100644
--- a/backend/finance/api/invoices/recurring/update_status.py
+++ b/backend/finance/api/invoices/recurring/update_status.py
@@ -4,7 +4,7 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_POST
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.boto3.async_tasks.tasks import Task
from backend.boto3.scheduler.create_schedule import create_boto_schedule
from backend.boto3.scheduler.get import get_boto_schedule
@@ -90,7 +90,7 @@ def recurring_profile_change_status_endpoint(request: WebRequest, invoice_profil
def return_message(request: HttpRequest, message: str, success: bool = True) -> HttpResponse:
send_message(request, message, success)
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
def send_message(request: HttpRequest, message: str, success: bool = False) -> None:
diff --git a/backend/finance/api/invoices/reminders/create.py b/backend/finance/api/invoices/reminders/create.py
index 4ef2a309e..a724a318b 100644
--- a/backend/finance/api/invoices/reminders/create.py
+++ b/backend/finance/api/invoices/reminders/create.py
@@ -6,7 +6,7 @@
# from django.shortcuts import render, redirect
# from django.utils import timezone
#
-# from backend.decorators import web_require_scopes
+# from core.decorators import web_require_scopes
# from backend.finance.models import Invoice, InvoiceReminder, QuotaUsage
# from backend.utils.quota_limit_ops import quota_usage_check_under
# from infrastructure.aws.schedules.create_reminder import CreateReminderInputData, create_reminder_schedule
@@ -47,17 +47,17 @@
# invoice = Invoice.objects.get(id=invoice_id)
# except Invoice.DoesNotExist:
# messages.error(request, "Invoice not found")
-# return render(request, "base/toast.html", {"autohide": False})
+# return render(request, "core/base/toast.html", {"autohide": False})
#
# # Check user permission
# if not invoice.has_access(user=request.user):
# messages.error(request, "You do not have permission to create schedules for this invoice")
-# return render(request, "base/toasts.html")
+# return render(request, "core/base/toasts.html")
#
# # Check reminder type
# if reminder_type not in InvoiceReminder.ReminderTypes.values:
# messages.error(request, "Invalid reminder type")
-# return render(request, "base/toasts.html")
+# return render(request, "core/base/toasts.html")
#
# # Ensure days is set for non-overdue reminders
# if reminder_type == "on_overdue":
@@ -70,7 +70,7 @@
# raise ValueError
# except ValueError:
# messages.error(request, "Invalid days value. Make sure it's an integer from 1-31")
-# return render(request, "base/toasts.html")
+# return render(request, "core/base/toasts.html")
#
# # Create reminder object
# reminder = InvoiceReminder(invoice=invoice, reminder_type=reminder_type)
@@ -96,4 +96,4 @@
# return render(request, "pages/invoices/single/schedules/reminders/_table_row.html", {"reminder": REMINDER.reminder})
# else:
# messages.error(request, REMINDER.message)
-# return render(request, "base/toasts.html")
+# return render(request, "core/base/toasts.html")
diff --git a/backend/finance/api/invoices/reminders/delete.py b/backend/finance/api/invoices/reminders/delete.py
index 8935b1c77..89b0178bc 100644
--- a/backend/finance/api/invoices/reminders/delete.py
+++ b/backend/finance/api/invoices/reminders/delete.py
@@ -4,7 +4,7 @@
# from django.shortcuts import render
# from django.views.decorators.http import require_http_methods
#
-# from backend.decorators import feature_flag_check, web_require_scopes
+# from core.decorators import feature_flag_check, web_require_scopes
# from backend.finance.models import InvoiceReminder
#
# from backend.types.htmx import HtmxHttpRequest
@@ -20,11 +20,11 @@
# reminder = InvoiceReminder.objects.get(id=reminder_id)
# except InvoiceReminder.DoesNotExist:
# messages.error(request, "Schedule not found!")
-# return render(request, "base/toasts.html")
+# return render(request, "core/base/toasts.html")
#
# if not reminder.invoice.has_access(request.user):
# messages.error(request, "You do not have access to this invoice.")
-# return render(request, "base/toasts.html")
+# return render(request, "core/base/toasts.html")
#
# original_status = reminder.status
# reminder.set_status("deleting")
@@ -40,7 +40,7 @@
# else:
# reminder.set_status(original_status)
# messages.error(request, f"Failed to delete schedule: {delete_status.message}")
-# return render(request, "base/toasts.html")
+# return render(request, "core/base/toasts.html")
#
# reminder.set_status("cancelled")
#
diff --git a/backend/finance/api/invoices/reminders/fetch.py b/backend/finance/api/invoices/reminders/fetch.py
index 49047d630..396f6703b 100644
--- a/backend/finance/api/invoices/reminders/fetch.py
+++ b/backend/finance/api/invoices/reminders/fetch.py
@@ -4,7 +4,7 @@
from django.views.decorators.http import require_GET
from django_ratelimit.core import is_ratelimited
-from backend.decorators import feature_flag_check, web_require_scopes
+from core.decorators import feature_flag_check, web_require_scopes
from backend.finance.models import Invoice
from core.types.htmx import HtmxHttpRequest
@@ -16,17 +16,17 @@ def fetch_reminders(request: HtmxHttpRequest, invoice_id: str):
ratelimit = is_ratelimited(request, group="fetch_reminders", key="user", rate="20/30s", increment=True)
if ratelimit:
messages.error(request, "Too many requests")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
try:
invoice = Invoice.objects.prefetch_related("invoice_reminders").get(id=invoice_id)
except Invoice.DoesNotExist:
messages.error(request, "Invoice not found")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
if not invoice.has_access(request.user):
messages.error(request, "You do not have permission to view this invoice")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
context: dict = {}
diff --git a/backend/finance/api/products/create.py b/backend/finance/api/products/create.py
index f8ee5321d..92f2fc063 100644
--- a/backend/finance/api/products/create.py
+++ b/backend/finance/api/products/create.py
@@ -1,7 +1,7 @@
from django.contrib import messages
from backend.finance.api.products.fetch import fetch_products
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import InvoiceProduct
from core.types.htmx import HtmxHttpRequest
diff --git a/backend/finance/api/products/fetch.py b/backend/finance/api/products/fetch.py
index 4586d8d49..78fa16eb9 100644
--- a/backend/finance/api/products/fetch.py
+++ b/backend/finance/api/products/fetch.py
@@ -1,7 +1,7 @@
from django.db.models import Q, QuerySet
from django.shortcuts import render
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import InvoiceProduct
from core.types.htmx import HtmxHttpRequest
diff --git a/backend/finance/api/receipts/delete.py b/backend/finance/api/receipts/delete.py
index d60bc963b..3ab2ed00a 100644
--- a/backend/finance/api/receipts/delete.py
+++ b/backend/finance/api/receipts/delete.py
@@ -4,7 +4,7 @@
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.models import Receipt
from core.types.requests import WebRequest
diff --git a/backend/finance/api/receipts/download.py b/backend/finance/api/receipts/download.py
index 70e199c25..525f0c320 100644
--- a/backend/finance/api/receipts/download.py
+++ b/backend/finance/api/receipts/download.py
@@ -2,7 +2,7 @@
from django.shortcuts import get_object_or_404
from django.urls import reverse
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.models import Receipt, ReceiptDownloadToken
diff --git a/backend/finance/api/receipts/edit.py b/backend/finance/api/receipts/edit.py
index c3ccf74eb..6771ce86f 100644
--- a/backend/finance/api/receipts/edit.py
+++ b/backend/finance/api/receipts/edit.py
@@ -7,7 +7,7 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods, require_POST
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.models import Receipt
@@ -23,7 +23,7 @@ def edit_receipt(request, receipt_id):
raise Receipt.DoesNotExist
except Receipt.DoesNotExist:
messages.error(request, "Receipt not found")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
file: InMemoryUploadedFile | None = request.FILES.get("receipt_image")
date = request.POST.get("receipt_date")
diff --git a/backend/finance/api/receipts/fetch.py b/backend/finance/api/receipts/fetch.py
index ca5e7d370..238d7dd4e 100644
--- a/backend/finance/api/receipts/fetch.py
+++ b/backend/finance/api/receipts/fetch.py
@@ -1,7 +1,7 @@
from django.db.models import Q, QuerySet
from django.shortcuts import render, redirect
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.models import Receipt
from core.types.htmx import HtmxHttpRequest
diff --git a/backend/finance/api/receipts/new.py b/backend/finance/api/receipts/new.py
index aa9076a7f..9775962d2 100644
--- a/backend/finance/api/receipts/new.py
+++ b/backend/finance/api/receipts/new.py
@@ -4,7 +4,7 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes, has_entitlements
+from core.decorators import web_require_scopes, has_entitlements
from backend.models import Receipt
from core.types.requests import WebRequest
diff --git a/backend/finance/api/reports/generate.py b/backend/finance/api/reports/generate.py
index c87e7c2c7..cdb750dac 100644
--- a/backend/finance/api/reports/generate.py
+++ b/backend/finance/api/reports/generate.py
@@ -1,7 +1,7 @@
from django.contrib import messages
from django.shortcuts import render
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.service.reports.generate import generate_report
from core.types.requests import WebRequest
@@ -16,10 +16,10 @@ def generate_report_endpoint(request: WebRequest):
if generated_report.failed:
messages.error(request, generated_report.error)
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
messages.success(request, f"Successfully generated report ({str(generated_report.response.uuid)[:4]})")
- resp = render(request, "base/toast.html")
+ resp = render(request, "core/base/toast.html")
resp["HX-Trigger"] = "refresh_reports_table"
return resp
diff --git a/backend/finance/models.py b/backend/finance/models.py
index 464010443..3e2c01695 100644
--- a/backend/finance/models.py
+++ b/backend/finance/models.py
@@ -3,12 +3,19 @@
from decimal import Decimal
from typing import Literal
from uuid import uuid4
+
+from core.models import DefaultValuesBase
from django.core.validators import MaxValueValidator
from django.db import models
from django.utils import timezone
from shortuuid.django_fields import ShortUUIDField
-from backend.clients.models import Client, DefaultValues
+from backend.data.default_email_templates import (
+ recurring_invoices_invoice_created_default_email_template,
+ recurring_invoices_invoice_overdue_default_email_template,
+ recurring_invoices_invoice_cancelled_default_email_template,
+)
+from backend.models import Client
from backend.managers import InvoiceRecurringProfile_WithItemsManager
from backend.models import OwnerBase, UserSettings, _private_storage, USER_OR_ORGANIZATION_CONSTRAINT, User, ExpiresBase, Organization
@@ -288,7 +295,7 @@ def next_invoice_issue_date(self) -> date:
case _:
return datetime.now().date()
- def next_invoice_due_date(self, account_defaults: DefaultValues, from_date: date = datetime.now().date()) -> date:
+ def next_invoice_due_date(self, account_defaults: FinanceDefaultValues, from_date: date = datetime.now().date()) -> date:
match account_defaults.invoice_due_date_type:
case account_defaults.InvoiceDueDateType.days_after:
return from_date + timedelta(days=account_defaults.invoice_due_date_value)
@@ -405,3 +412,76 @@ class ReceiptDownloadToken(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
file = models.ForeignKey(Receipt, on_delete=models.CASCADE)
token = models.UUIDField(default=uuid4, editable=False, unique=True)
+
+
+class FinanceDefaultValues(DefaultValuesBase):
+ class InvoiceDueDateType(models.TextChoices):
+ days_after = "days_after"
+ date_following = "date_following"
+ date_current = "date_current"
+
+ class InvoiceDateType(models.TextChoices):
+ day_of_month = "day_of_month"
+ days_after = "days_after"
+
+ invoice_due_date_value = models.PositiveSmallIntegerField(default=7, null=False, blank=False)
+ invoice_due_date_type = models.CharField(
+ max_length=20,
+ choices=InvoiceDueDateType.choices,
+ default=InvoiceDueDateType.days_after,
+ )
+
+ invoice_date_value = models.PositiveSmallIntegerField(default=15, null=False, blank=False)
+ invoice_date_type = models.CharField(
+ max_length=20,
+ choices=InvoiceDateType.choices,
+ default=InvoiceDateType.day_of_month,
+ )
+
+ invoice_from_name = models.CharField(max_length=100, null=True, blank=True)
+ invoice_from_company = models.CharField(max_length=100, null=True, blank=True)
+ invoice_from_address = models.CharField(max_length=100, null=True, blank=True)
+ invoice_from_city = models.CharField(max_length=100, null=True, blank=True)
+ invoice_from_county = models.CharField(max_length=100, null=True, blank=True)
+ invoice_from_country = models.CharField(max_length=100, null=True, blank=True)
+ invoice_from_email = models.CharField(max_length=100, null=True, blank=True)
+
+ invoice_account_number = models.CharField(max_length=100, null=True, blank=True)
+ invoice_sort_code = models.CharField(max_length=100, null=True, blank=True)
+ invoice_account_holder_name = models.CharField(max_length=100, null=True, blank=True)
+
+ email_template_recurring_invoices_invoice_created = models.TextField(default=recurring_invoices_invoice_created_default_email_template)
+ email_template_recurring_invoices_invoice_overdue = models.TextField(default=recurring_invoices_invoice_overdue_default_email_template)
+ email_template_recurring_invoices_invoice_cancelled = models.TextField(
+ default=recurring_invoices_invoice_cancelled_default_email_template
+ )
+
+ default_invoice_logo = models.ImageField(
+ upload_to="invoice_logos/",
+ storage=_private_storage,
+ blank=True,
+ null=True,
+ )
+
+ def get_issue_and_due_dates(self, issue_date: date | str | None = None) -> tuple[str, str]:
+ due: date
+ issue: date
+
+ if isinstance(issue_date, str):
+ issue = date.fromisoformat(issue_date) or date.today()
+ else:
+ issue = issue_date or date.today()
+
+ match self.invoice_due_date_type:
+ case self.InvoiceDueDateType.days_after:
+ due = issue + timedelta(days=self.invoice_due_date_value)
+ case self.InvoiceDueDateType.date_following:
+ due = date(issue.year, issue.month + 1, self.invoice_due_date_value)
+ case self.InvoiceDueDateType.date_current:
+ due = date(issue.year, issue.month, self.invoice_due_date_value)
+ case _:
+ raise ValueError("Invalid invoice due date type")
+ return date.isoformat(issue), date.isoformat(due)
+
+ class Meta:
+ constraints: list = [USER_OR_ORGANIZATION_CONSTRAINT()]
diff --git a/backend/finance/service/clients/create.py b/backend/finance/service/clients/create.py
index 341aeb180..2a37fc104 100644
--- a/backend/finance/service/clients/create.py
+++ b/backend/finance/service/clients/create.py
@@ -1,4 +1,4 @@
-from backend.clients.models import Client
+from backend.models import Client
from backend.finance.service.clients.validate import validate_client_create
from core.utils.dataclasses import BaseServiceResponse
diff --git a/backend/finance/service/defaults/get.py b/backend/finance/service/defaults/get.py
index 813c47675..7cde191c1 100644
--- a/backend/finance/service/defaults/get.py
+++ b/backend/finance/service/defaults/get.py
@@ -1,12 +1,12 @@
from backend.models import User, Organization
-from backend.clients.models import DefaultValues, Client
+from backend.models import FinanceDefaultValues, Client
-def get_account_defaults(actor: User | Organization, client: Client | None = None) -> DefaultValues:
+def get_account_defaults(actor: User | Organization, client: Client | None = None) -> FinanceDefaultValues:
if not client:
- account_defaults = DefaultValues.filter_by_owner(owner=actor).filter(client__isnull=True).first()
+ account_defaults = FinanceDefaultValues.filter_by_owner(owner=actor).filter(client__isnull=True).first()
if account_defaults:
return account_defaults
- return DefaultValues.objects.create(owner=actor, client=None) # type: ignore[misc]
- return DefaultValues.filter_by_owner(owner=actor).get(client=client)
+ return FinanceDefaultValues.objects.create(owner=actor, client=None) # type: ignore[misc]
+ return FinanceDefaultValues.filter_by_owner(owner=actor).get(client=client)
diff --git a/backend/finance/service/defaults/update.py b/backend/finance/service/defaults/update.py
index 56cc304ae..4428c839c 100644
--- a/backend/finance/service/defaults/update.py
+++ b/backend/finance/service/defaults/update.py
@@ -1,15 +1,14 @@
from PIL import Image
-from backend.models import DefaultValues
+from backend.models import FinanceDefaultValues
from core.types.requests import WebRequest
from core.utils.dataclasses import BaseServiceResponse
-class ClientDefaultsServiceResponse(BaseServiceResponse[DefaultValues]): ...
+class ClientDefaultsServiceResponse(BaseServiceResponse[FinanceDefaultValues]): ...
-def change_client_defaults(request: WebRequest, defaults: DefaultValues) -> ClientDefaultsServiceResponse:
-
+def change_client_defaults(request: WebRequest, defaults: FinanceDefaultValues) -> ClientDefaultsServiceResponse:
# put = QueryDict(request.body)
invoice_due_date_option = request.POST.get("invoice_due_date_option", "")
invoice_due_date_value = request.POST.get("invoice_due_date_value", "")
diff --git a/backend/finance/service/invoices/common/create/create.py b/backend/finance/service/invoices/common/create/create.py
index 546655604..e45884423 100644
--- a/backend/finance/service/invoices/common/create/create.py
+++ b/backend/finance/service/invoices/common/create/create.py
@@ -1,6 +1,6 @@
from django.contrib import messages
-from backend.models import Invoice, InvoiceRecurringProfile, InvoiceItem, Client, DefaultValues
+from backend.models import Invoice, InvoiceRecurringProfile, InvoiceItem, Client, FinanceDefaultValues
from backend.finance.service.defaults.get import get_account_defaults
from core.types.requests import WebRequest
@@ -55,7 +55,7 @@ def save_invoice_common(request: WebRequest, invoice_items, invoice: Invoice | I
if invoice.client_to is not None and invoice.client_to.default_values.default_invoice_logo:
invoice.logo = invoice.client_to.default_values.default_invoice_logo
else:
- defaults: DefaultValues = get_account_defaults(request.actor)
+ defaults: FinanceDefaultValues = get_account_defaults(request.actor)
if defaults:
invoice.logo = defaults.default_invoice_logo
invoice.sort_code = request.POST.get("sort_code")
diff --git a/backend/finance/service/invoices/common/create/get_page.py b/backend/finance/service/invoices/common/create/get_page.py
index b2d60b475..0d0034d58 100644
--- a/backend/finance/service/invoices/common/create/get_page.py
+++ b/backend/finance/service/invoices/common/create/get_page.py
@@ -2,7 +2,7 @@
from typing import NamedTuple
from django.core.exceptions import PermissionDenied, ValidationError
-from backend.models import Client, InvoiceProduct, DefaultValues
+from backend.models import Client, InvoiceProduct, FinanceDefaultValues
from backend.finance.service.clients.validate import validate_client
from backend.finance.service.defaults.get import get_account_defaults
from core.types.requests import WebRequest
@@ -10,7 +10,7 @@
class CreateInvoiceContextTuple(NamedTuple):
- defaults: DefaultValues
+ defaults: FinanceDefaultValues
context: dict
@@ -23,7 +23,7 @@ def global_get_invoice_context(request: WebRequest) -> CreateInvoiceContextServi
"existing_products": InvoiceProduct.objects.filter(user=request.user),
}
- defaults: DefaultValues
+ defaults: FinanceDefaultValues
if client_id := request.GET.get("client"):
try:
diff --git a/backend/finance/service/invoices/recurring/generation/next_invoice.py b/backend/finance/service/invoices/recurring/generation/next_invoice.py
index de94c7115..56fb3a2aa 100644
--- a/backend/finance/service/invoices/recurring/generation/next_invoice.py
+++ b/backend/finance/service/invoices/recurring/generation/next_invoice.py
@@ -2,7 +2,7 @@
from django.db import transaction, IntegrityError
-from backend.models import Invoice, InvoiceRecurringProfile, DefaultValues, AuditLog
+from backend.models import Invoice, InvoiceRecurringProfile, FinanceDefaultValues, AuditLog
from backend.finance.service.defaults.get import get_account_defaults
from backend.finance.service.invoices.common.emails.on_create import on_create_invoice_email_service
from core.utils.dataclasses import BaseServiceResponse
@@ -19,7 +19,7 @@ class GenerateNextInvoiceServiceResponse(BaseServiceResponse[Invoice]): ...
def generate_next_invoice_service(
invoice_recurring_profile: InvoiceRecurringProfile,
issue_date: date = date.today(),
- account_defaults: DefaultValues | None = None,
+ account_defaults: FinanceDefaultValues | None = None,
) -> GenerateNextInvoiceServiceResponse:
"""
This will generate the next single invoice based on the invoice recurring profile
@@ -114,7 +114,7 @@ def handle_invoice_generation_failure(invoice_recurring_profile, error_message):
def safe_generate_next_invoice_service(
invoice_recurring_profile: InvoiceRecurringProfile,
issue_date: date = date.today(),
- account_defaults: DefaultValues | None = None,
+ account_defaults: FinanceDefaultValues | None = None,
) -> GenerateNextInvoiceServiceResponse:
"""
Safe wrapper to generate the next invoice with transaction rollback and error logging.
diff --git a/backend/finance/service/invoices/single/create/create.py b/backend/finance/service/invoices/single/create/create.py
index fd6e178a8..e16be54dc 100644
--- a/backend/finance/service/invoices/single/create/create.py
+++ b/backend/finance/service/invoices/single/create/create.py
@@ -3,7 +3,7 @@
from django.contrib import messages
from django.core.exceptions import PermissionDenied, ValidationError
-from backend.finance.models import Invoice, InvoiceItem, Client, InvoiceProduct, DefaultValues
+from backend.models import Invoice, InvoiceItem, Client, InvoiceProduct, FinanceDefaultValues
# from backend.models import QuotaUsage
from backend.finance.service.clients.validate import validate_client
@@ -18,7 +18,7 @@ def get_invoice_context(request: WebRequest) -> dict:
"existing_products": InvoiceProduct.objects.filter(user=request.user),
}
- defaults: DefaultValues
+ defaults: FinanceDefaultValues
if client_id := request.GET.get("client"):
try:
diff --git a/backend/finance/views/invoices/recurring/create.py b/backend/finance/views/invoices/recurring/create.py
index c0e215362..d6bcdfdef 100644
--- a/backend/finance/views/invoices/recurring/create.py
+++ b/backend/finance/views/invoices/recurring/create.py
@@ -3,7 +3,7 @@
from django.views.decorators.http import require_http_methods
from backend.finance.models import InvoiceRecurringProfile
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.boto3.handler import BOTO3_HANDLER
from backend.boto3.async_tasks.tasks import Task
from backend.boto3.scheduler.create_schedule import create_boto_schedule
diff --git a/backend/finance/views/invoices/recurring/dashboard.py b/backend/finance/views/invoices/recurring/dashboard.py
index 04bf31d2f..564c8ae89 100644
--- a/backend/finance/views/invoices/recurring/dashboard.py
+++ b/backend/finance/views/invoices/recurring/dashboard.py
@@ -1,6 +1,6 @@
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes, has_entitlements
+from core.decorators import web_require_scopes, has_entitlements
from core.types.requests import WebRequest
from backend.finance.views.invoices.handler import invoices_core_handler
diff --git a/backend/finance/views/invoices/recurring/edit.py b/backend/finance/views/invoices/recurring/edit.py
index 026fcb8e8..496239b63 100644
--- a/backend/finance/views/invoices/recurring/edit.py
+++ b/backend/finance/views/invoices/recurring/edit.py
@@ -2,7 +2,7 @@
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes, has_entitlements
+from core.decorators import web_require_scopes, has_entitlements
from backend.finance.models import InvoiceRecurringProfile
from backend.finance.service.invoices.recurring.get import get_invoice_profile
from backend.finance.views.invoices.handler import invoices_core_handler
@@ -71,7 +71,7 @@ def invoice_edit_page_endpoint(request, invoice_profile_id):
if get_response.failed:
messages.error(request, get_response.error_message)
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
invoice_profile: InvoiceRecurringProfile = get_response.response
diff --git a/backend/finance/views/invoices/recurring/overview.py b/backend/finance/views/invoices/recurring/overview.py
index ec99f9ea0..ff5e77785 100644
--- a/backend/finance/views/invoices/recurring/overview.py
+++ b/backend/finance/views/invoices/recurring/overview.py
@@ -1,4 +1,4 @@
-from backend.decorators import *
+from core.decorators import *
from backend.models import *
from backend.finance.service.defaults.get import get_account_defaults
from backend.finance.views.invoices.handler import invoices_core_handler
diff --git a/backend/finance/views/invoices/single/create.py b/backend/finance/views/invoices/single/create.py
index 631bf675d..abe5d5ad0 100644
--- a/backend/finance/views/invoices/single/create.py
+++ b/backend/finance/views/invoices/single/create.py
@@ -1,7 +1,7 @@
from django.shortcuts import redirect
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes, has_entitlements
+from core.decorators import web_require_scopes, has_entitlements
from backend.finance.service.invoices.single.create.create import create_invoice_items, save_invoice
from backend.finance.service.invoices.single.create.get_page import get_invoice_context
from core.types.requests import WebRequest
diff --git a/backend/finance/views/invoices/single/dashboard.py b/backend/finance/views/invoices/single/dashboard.py
index 27a08887d..7f1badbc7 100644
--- a/backend/finance/views/invoices/single/dashboard.py
+++ b/backend/finance/views/invoices/single/dashboard.py
@@ -2,7 +2,7 @@
from django.shortcuts import render, redirect
from django.views.decorators.http import require_http_methods
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import Invoice
from core.types.requests import WebRequest
from backend.finance.views.invoices.handler import invoices_core_handler
diff --git a/backend/finance/views/invoices/single/edit.py b/backend/finance/views/invoices/single/edit.py
index f6d702d88..80d6790f9 100644
--- a/backend/finance/views/invoices/single/edit.py
+++ b/backend/finance/views/invoices/single/edit.py
@@ -6,7 +6,7 @@
from django.views.decorators.http import require_http_methods
from core.types.requests import WebRequest
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import Invoice, Client, InvoiceItem
from core.types.htmx import HtmxHttpRequest
@@ -140,7 +140,8 @@ def edit_invoice(request: WebRequest, invoice_id):
"client_city": request.POST.get("to_city"),
"client_county": request.POST.get("to_county"),
"client_country": request.POST.get("to_country"),
- "client_is_representative": True if request.POST.get("is_representative") == "on" else False, # type: ignore[dict-item]
+ "client_is_representative": True if request.POST.get("is_representative") == "on" else False,
+ # type: ignore[dict-item]
"client_to": None,
}
)
@@ -166,7 +167,7 @@ def edit_invoice(request: WebRequest, invoice_id):
messages.success(request, "Invoice edited")
if request.htmx:
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
return invoice_edit_page_get(request, invoice_id)
diff --git a/backend/finance/views/invoices/single/manage_access.py b/backend/finance/views/invoices/single/manage_access.py
index a6e40af77..dc1d70fe4 100644
--- a/backend/finance/views/invoices/single/manage_access.py
+++ b/backend/finance/views/invoices/single/manage_access.py
@@ -2,7 +2,7 @@
from django.http import HttpResponse
from django.shortcuts import redirect, render
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import Invoice, InvoiceURL
from backend.finance.service.invoices.single.get_invoice import get_invoice_by_actor
from core.types.htmx import HtmxHttpRequest
@@ -65,7 +65,7 @@ def delete_code(request: HtmxHttpRequest, code):
raise InvoiceURL.DoesNotExist
except (Invoice.DoesNotExist, InvoiceURL.DoesNotExist):
messages.error(request, "Invalid URL")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
# QuotaLimit.delete_quota_usage("invoices-access_codes", request.user, invoice.id, code_obj.created_on)
diff --git a/backend/finance/views/invoices/single/overview.py b/backend/finance/views/invoices/single/overview.py
index f7ca164ac..90b509df1 100644
--- a/backend/finance/views/invoices/single/overview.py
+++ b/backend/finance/views/invoices/single/overview.py
@@ -1,6 +1,6 @@
from urllib.parse import urlencode
-from backend.decorators import *
+from core.decorators import *
from backend.models import *
from backend.finance.views.invoices.handler import invoices_core_handler
diff --git a/backend/finance/views/invoices/single/schedule.py b/backend/finance/views/invoices/single/schedule.py
index 39317965c..e4777d67c 100644
--- a/backend/finance/views/invoices/single/schedule.py
+++ b/backend/finance/views/invoices/single/schedule.py
@@ -2,7 +2,7 @@
# from django.http import HttpResponse
# from django.shortcuts import render, redirect
#
-# from backend.decorators import feature_flag_check, web_require_scopes
+# from core.decorators import feature_flag_check, web_require_scopes
# from backend.finance.models import Invoice, QuotaLimit
# from backend.types.htmx import HtmxHttpRequest
#
diff --git a/backend/finance/views/invoices/single/view.py b/backend/finance/views/invoices/single/view.py
index db49d9a68..3f9eec81f 100644
--- a/backend/finance/views/invoices/single/view.py
+++ b/backend/finance/views/invoices/single/view.py
@@ -7,7 +7,7 @@
from login_required import login_not_required
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from backend.finance.models import Invoice, InvoiceURL
from core.types.htmx import HtmxHttpRequest
diff --git a/backend/finance/views/receipts/dashboard.py b/backend/finance/views/receipts/dashboard.py
index 23cf1510b..47109b46d 100644
--- a/backend/finance/views/receipts/dashboard.py
+++ b/backend/finance/views/receipts/dashboard.py
@@ -1,7 +1,7 @@
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
-from backend.decorators import web_require_scopes
+from core.decorators import web_require_scopes
from core.types.htmx import HtmxHttpRequest
diff --git a/backend/middleware.py b/backend/middleware.py
deleted file mode 100644
index 43bb6158c..000000000
--- a/backend/middleware.py
+++ /dev/null
@@ -1,74 +0,0 @@
-from django.contrib.auth.models import AnonymousUser
-from django.utils.deprecation import MiddlewareMixin
-from django.contrib.auth import get_user
-from django.db import connection, OperationalError
-from django.http import HttpResponse
-
-from backend.models import User
-from core.types.htmx import HtmxAnyHttpRequest
-from core.types.requests import WebRequest
-
-
-class HealthCheckMiddleware:
- def __init__(self, get_response):
- self.get_response = get_response
-
- def __call__(self, request):
- if request.path == "/api/hc/healthcheck/":
- try:
- status = connection.ensure_connection()
- except OperationalError:
- status = "error"
-
- if not status: # good
- return HttpResponse(status=200, content="All operations are up and running!")
- return HttpResponse(status=503, content="Service Unavailable")
- return self.get_response(request)
-
-
-class HTMXPartialLoadMiddleware:
- def __init__(self, get_response):
- self.get_response = get_response
-
- def __call__(self, request: HtmxAnyHttpRequest):
- response: HttpResponse = self.get_response(request)
-
- if hasattr(response, "retarget"):
- response.headers["HX-Retarget"] = response.retarget
- elif request.htmx.boosted and not response.headers.get("HX-Retarget") and not hasattr(response, "no_retarget"):
- response.headers["HX-Retarget"] = "#main_content"
- response.headers["HX-Reswap"] = "innerHTML"
- # if 'data-layout="breadcrumbs"' not in str(response.content):
- response.headers["HX-Trigger"] = "update_breadcrumbs"
- return response
-
-
-class LastVisitedMiddleware:
- def __init__(self, get_response):
- self.get_response = get_response
-
- def __call__(self, request):
- if request.method == "GET" and "text/html" in request.headers.get("Accept", ""):
- try:
- request.session["last_visited"] = request.session["currently_visiting"]
- except KeyError:
- pass
- current_url = request.build_absolute_uri()
- request.session["currently_visiting"] = current_url
- return self.get_response(request)
-
-
-class CustomUserMiddleware(MiddlewareMixin):
- def process_request(self, request: WebRequest):
- user = get_user(request)
-
- # Replace request.user with CustomUser instance if authenticated
- if user.is_authenticated:
- request.user = User.objects.get(pk=user.pk)
- request.team = request.user.logged_in_as_team or None
- request.team_id = request.team.id if request.team else None
- request.actor = request.team or request.user
- else:
- # If user is not authenticated, set request.user to AnonymousUser
- request.user = AnonymousUser() # type: ignore[assignment]
- request.actor = request.user
diff --git a/backend/migrations/0001_initial.py b/backend/migrations/0001_initial.py
index a7107828c..02e7fd0e9 100644
--- a/backend/migrations/0001_initial.py
+++ b/backend/migrations/0001_initial.py
@@ -1,11 +1,13 @@
-# Generated by Django 5.1.4 on 2024-12-21 22:26
+# Generated by Django 5.1.4 on 2025-01-01 17:51
import backend.data.default_email_templates
import core.models
import django.core.validators
+import django.db.models.deletion
import django.db.models.manager
import shortuuid.django_fields
import uuid
+from django.conf import settings
from django.db import migrations, models
@@ -13,55 +15,60 @@ class Migration(migrations.Migration):
initial = True
- dependencies = []
+ dependencies = [
+ ("core", "0002_client"),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
operations = [
migrations.CreateModel(
- name="Client",
+ name="InvoiceItem",
+ fields=[
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("name", models.CharField(max_length=50)),
+ ("description", models.CharField(max_length=100)),
+ ("is_service", models.BooleanField(default=True)),
+ ("hours", models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)),
+ ("price_per_hour", models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)),
+ ("price", models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)),
+ ],
+ ),
+ migrations.CreateModel(
+ name="FileStorageFile",
fields=[
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("file", models.FileField(storage=core.models._private_storage, upload_to=core.models.upload_to_user_separate_folder)),
+ ("file_uri_path", models.CharField(max_length=500)),
+ ("created_at", models.DateTimeField(auto_now_add=True)),
+ ("updated_at", models.DateTimeField(auto_now=True)),
(
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
+ "last_edited_by",
+ models.ForeignKey(
+ blank=True,
+ editable=False,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="files_edited",
+ to=settings.AUTH_USER_MODEL,
),
),
- ("active", models.BooleanField(default=True)),
- ("name", models.CharField(max_length=64)),
(
- "phone_number",
- models.CharField(blank=True, max_length=100, null=True),
+ "organization",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.organization"),
),
- ("email", models.EmailField(blank=True, max_length=254, null=True)),
- ("email_verified", models.BooleanField(default=False)),
- ("company", models.CharField(blank=True, max_length=100, null=True)),
(
- "contact_method",
- models.CharField(blank=True, max_length=100, null=True),
+ "user",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
- ("is_representative", models.BooleanField(default=False)),
- ("address", models.TextField(blank=True, max_length=100, null=True)),
- ("city", models.CharField(blank=True, max_length=100, null=True)),
- ("country", models.CharField(blank=True, max_length=100, null=True)),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
- name="DefaultValues",
+ name="FinanceDefaultValues",
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
(
"currency",
models.CharField(
@@ -82,11 +89,7 @@ class Migration(migrations.Migration):
(
"invoice_due_date_type",
models.CharField(
- choices=[
- ("days_after", "Days After"),
- ("date_following", "Date Following"),
- ("date_current", "Date Current"),
- ],
+ choices=[("days_after", "Days After"), ("date_following", "Date Following"), ("date_current", "Date Current")],
default="days_after",
max_length=20,
),
@@ -95,54 +98,19 @@ class Migration(migrations.Migration):
(
"invoice_date_type",
models.CharField(
- choices=[
- ("day_of_month", "Day Of Month"),
- ("days_after", "Days After"),
- ],
- default="day_of_month",
- max_length=20,
+ choices=[("day_of_month", "Day Of Month"), ("days_after", "Days After")], default="day_of_month", max_length=20
),
),
- (
- "invoice_from_name",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_from_company",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_from_address",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_from_city",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_from_county",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_from_country",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_from_email",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_account_number",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_sort_code",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "invoice_account_holder_name",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("invoice_from_name", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_from_company", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_from_address", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_from_city", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_from_county", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_from_country", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_from_email", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_account_number", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_sort_code", models.CharField(blank=True, max_length=100, null=True)),
+ ("invoice_account_holder_name", models.CharField(blank=True, max_length=100, null=True)),
(
"email_template_recurring_invoices_invoice_created",
models.TextField(
@@ -163,123 +131,87 @@ class Migration(migrations.Migration):
),
(
"default_invoice_logo",
- models.ImageField(
- blank=True,
- null=True,
- storage=core.models._private_storage,
- upload_to="invoice_logos/",
+ models.ImageField(blank=True, null=True, storage=core.models._private_storage, upload_to="invoice_logos/"),
+ ),
+ (
+ "client",
+ models.OneToOneField(
+ blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name="default_values", to="core.client"
),
),
+ (
+ "organization",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.organization"),
+ ),
+ (
+ "user",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
],
- options={
- "abstract": False,
- },
),
migrations.CreateModel(
- name="FileStorageFile",
+ name="InvoiceProduct",
fields=[
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("name", models.CharField(max_length=50)),
+ ("description", models.CharField(max_length=100)),
+ ("quantity", models.IntegerField()),
+ ("rate", models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True)),
(
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
+ "organization",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.organization"),
),
(
- "file",
- models.FileField(
- storage=core.models._private_storage,
- upload_to=core.models.upload_to_user_separate_folder,
- ),
+ "user",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
- ("file_uri_path", models.CharField(max_length=500)),
- ("created_at", models.DateTimeField(auto_now_add=True)),
- ("updated_at", models.DateTimeField(auto_now=True)),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
- name="Invoice",
+ name="InvoiceRecurringProfile",
fields=[
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("boto_schedule_arn", models.CharField(blank=True, max_length=2048, null=True)),
+ ("boto_schedule_uuid", models.UUIDField(blank=True, default=None, null=True)),
+ ("boto_last_updated", models.DateTimeField(auto_now=True)),
+ ("received", models.BooleanField(default=False)),
(
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
+ "boto_schedule_status",
+ models.CharField(
+ choices=[
+ ("pending", "Pending"),
+ ("creating", "Creating"),
+ ("completed", "Completed"),
+ ("failed", "Failed"),
+ ("deleting", "Deleting"),
+ ("cancelled", "Cancelled"),
+ ],
+ default="pending",
+ max_length=100,
),
),
- (
- "client_name",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_email",
- models.EmailField(blank=True, max_length=254, null=True),
- ),
- (
- "client_company",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_address",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_city",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_county",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_country",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("client_name", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_email", models.EmailField(blank=True, max_length=254, null=True)),
+ ("client_company", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_address", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_city", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_county", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_country", models.CharField(blank=True, max_length=100, null=True)),
("client_is_representative", models.BooleanField(default=False)),
("self_name", models.CharField(blank=True, max_length=100, null=True)),
- (
- "self_company",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "self_address",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("self_company", models.CharField(blank=True, max_length=100, null=True)),
+ ("self_address", models.CharField(blank=True, max_length=100, null=True)),
("self_city", models.CharField(blank=True, max_length=100, null=True)),
- (
- "self_county",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "self_country",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("self_county", models.CharField(blank=True, max_length=100, null=True)),
+ ("self_country", models.CharField(blank=True, max_length=100, null=True)),
("sort_code", models.CharField(blank=True, max_length=8, null=True)),
- (
- "account_holder_name",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "account_number",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("account_holder_name", models.CharField(blank=True, max_length=100, null=True)),
+ ("account_number", models.CharField(blank=True, max_length=100, null=True)),
("vat_number", models.CharField(blank=True, max_length=100, null=True)),
- (
- "logo",
- models.ImageField(
- blank=True,
- null=True,
- storage=core.models._private_storage,
- upload_to="invoice_logos",
- ),
- ),
+ ("logo", models.ImageField(blank=True, null=True, storage=core.models._private_storage, upload_to="invoice_logos")),
("notes", models.TextField(blank=True, null=True)),
(
"currency",
@@ -299,197 +231,74 @@ class Migration(migrations.Migration):
),
("date_created", models.DateTimeField(auto_now_add=True)),
("date_issued", models.DateField(blank=True, null=True)),
- (
- "discount_amount",
- models.DecimalField(decimal_places=2, default=0, max_digits=15),
- ),
+ ("discount_amount", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
(
"discount_percentage",
models.DecimalField(
- decimal_places=2,
- default=0,
- max_digits=5,
- validators=[django.core.validators.MaxValueValidator(100)],
+ decimal_places=2, default=0, max_digits=5, validators=[django.core.validators.MaxValueValidator(100)]
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
- ("reference", models.CharField(blank=True, max_length=16, null=True)),
- ("date_due", models.DateField()),
+ ("active", models.BooleanField(default=True)),
(
"status",
models.CharField(
- choices=[
- ("draft", "Draft"),
- ("pending", "Pending"),
- ("paid", "Paid"),
- ],
- default="draft",
- max_length=10,
+ choices=[("ongoing", "Ongoing"), ("paused", "paused"), ("cancelled", "cancelled")], default="paused", max_length=10
),
),
- ("status_updated_at", models.DateTimeField(auto_now_add=True)),
- ],
- options={
- "abstract": False,
- },
- ),
- migrations.CreateModel(
- name="InvoiceItem",
- fields=[
(
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
+ "frequency",
+ models.CharField(
+ choices=[("weekly", "Weekly"), ("monthly", "Monthly"), ("yearly", "Yearly")], default="monthly", max_length=20
),
),
- ("name", models.CharField(max_length=50)),
- ("description", models.CharField(max_length=100)),
- ("is_service", models.BooleanField(default=True)),
- (
- "hours",
- models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True),
- ),
- (
- "price_per_hour",
- models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True),
- ),
- (
- "price",
- models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True),
- ),
- ],
- ),
- migrations.CreateModel(
- name="InvoiceProduct",
- fields=[
+ ("end_date", models.DateField(blank=True, null=True)),
+ ("due_after_days", models.PositiveSmallIntegerField(default=7)),
+ ("day_of_week", models.PositiveSmallIntegerField(blank=True, null=True)),
+ ("day_of_month", models.PositiveSmallIntegerField(blank=True, null=True)),
+ ("month_of_year", models.PositiveSmallIntegerField(blank=True, null=True)),
+ ("client_to", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="core.client")),
+ ("items", models.ManyToManyField(blank=True, to="backend.invoiceitem")),
(
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
+ "organization",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.organization"),
),
- ("name", models.CharField(max_length=50)),
- ("description", models.CharField(max_length=100)),
- ("quantity", models.IntegerField()),
(
- "rate",
- models.DecimalField(blank=True, decimal_places=2, max_digits=15, null=True),
+ "user",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
],
options={
"abstract": False,
},
+ managers=[
+ ("with_items", django.db.models.manager.Manager()),
+ ],
),
migrations.CreateModel(
- name="InvoiceRecurringProfile",
+ name="Invoice",
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- (
- "boto_schedule_arn",
- models.CharField(blank=True, max_length=2048, null=True),
- ),
- (
- "boto_schedule_uuid",
- models.UUIDField(blank=True, default=None, null=True),
- ),
- ("boto_last_updated", models.DateTimeField(auto_now=True)),
- ("received", models.BooleanField(default=False)),
- (
- "boto_schedule_status",
- models.CharField(
- choices=[
- ("pending", "Pending"),
- ("creating", "Creating"),
- ("completed", "Completed"),
- ("failed", "Failed"),
- ("deleting", "Deleting"),
- ("cancelled", "Cancelled"),
- ],
- default="pending",
- max_length=100,
- ),
- ),
- (
- "client_name",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_email",
- models.EmailField(blank=True, max_length=254, null=True),
- ),
- (
- "client_company",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_address",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_city",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_county",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "client_country",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("client_name", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_email", models.EmailField(blank=True, max_length=254, null=True)),
+ ("client_company", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_address", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_city", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_county", models.CharField(blank=True, max_length=100, null=True)),
+ ("client_country", models.CharField(blank=True, max_length=100, null=True)),
("client_is_representative", models.BooleanField(default=False)),
("self_name", models.CharField(blank=True, max_length=100, null=True)),
- (
- "self_company",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "self_address",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("self_company", models.CharField(blank=True, max_length=100, null=True)),
+ ("self_address", models.CharField(blank=True, max_length=100, null=True)),
("self_city", models.CharField(blank=True, max_length=100, null=True)),
- (
- "self_county",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "self_country",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("self_county", models.CharField(blank=True, max_length=100, null=True)),
+ ("self_country", models.CharField(blank=True, max_length=100, null=True)),
("sort_code", models.CharField(blank=True, max_length=8, null=True)),
- (
- "account_holder_name",
- models.CharField(blank=True, max_length=100, null=True),
- ),
- (
- "account_number",
- models.CharField(blank=True, max_length=100, null=True),
- ),
+ ("account_holder_name", models.CharField(blank=True, max_length=100, null=True)),
+ ("account_number", models.CharField(blank=True, max_length=100, null=True)),
("vat_number", models.CharField(blank=True, max_length=100, null=True)),
- (
- "logo",
- models.ImageField(
- blank=True,
- null=True,
- storage=core.models._private_storage,
- upload_to="invoice_logos",
- ),
- ),
+ ("logo", models.ImageField(blank=True, null=True, storage=core.models._private_storage, upload_to="invoice_logos")),
("notes", models.TextField(blank=True, null=True)),
(
"currency",
@@ -509,89 +318,56 @@ class Migration(migrations.Migration):
),
("date_created", models.DateTimeField(auto_now_add=True)),
("date_issued", models.DateField(blank=True, null=True)),
- (
- "discount_amount",
- models.DecimalField(decimal_places=2, default=0, max_digits=15),
- ),
+ ("discount_amount", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
(
"discount_percentage",
models.DecimalField(
- decimal_places=2,
- default=0,
- max_digits=5,
- validators=[django.core.validators.MaxValueValidator(100)],
+ decimal_places=2, default=0, max_digits=5, validators=[django.core.validators.MaxValueValidator(100)]
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
- ("active", models.BooleanField(default=True)),
+ ("reference", models.CharField(blank=True, max_length=16, null=True)),
+ ("date_due", models.DateField()),
(
"status",
models.CharField(
- choices=[
- ("ongoing", "Ongoing"),
- ("paused", "paused"),
- ("cancelled", "cancelled"),
- ],
- default="paused",
- max_length=10,
+ choices=[("draft", "Draft"), ("pending", "Pending"), ("paid", "Paid")], default="draft", max_length=10
),
),
+ ("status_updated_at", models.DateTimeField(auto_now_add=True)),
+ ("client_to", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="core.client")),
(
- "frequency",
- models.CharField(
- choices=[
- ("weekly", "Weekly"),
- ("monthly", "Monthly"),
- ("yearly", "Yearly"),
- ],
- default="monthly",
- max_length=20,
- ),
- ),
- ("end_date", models.DateField(blank=True, null=True)),
- ("due_after_days", models.PositiveSmallIntegerField(default=7)),
- (
- "day_of_week",
- models.PositiveSmallIntegerField(blank=True, null=True),
+ "organization",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.organization"),
),
(
- "day_of_month",
- models.PositiveSmallIntegerField(blank=True, null=True),
+ "user",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
+ ("items", models.ManyToManyField(blank=True, to="backend.invoiceitem")),
(
- "month_of_year",
- models.PositiveSmallIntegerField(blank=True, null=True),
+ "invoice_recurring_profile",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ related_name="generated_invoices",
+ to="backend.invoicerecurringprofile",
+ ),
),
],
options={
"abstract": False,
},
- managers=[
- ("with_items", django.db.models.manager.Manager()),
- ],
),
migrations.CreateModel(
name="InvoiceReminder",
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created_at", models.DateTimeField(auto_now_add=True)),
- (
- "boto_schedule_arn",
- models.CharField(blank=True, max_length=2048, null=True),
- ),
- (
- "boto_schedule_uuid",
- models.UUIDField(blank=True, default=None, null=True),
- ),
+ ("boto_schedule_arn", models.CharField(blank=True, max_length=2048, null=True)),
+ ("boto_schedule_uuid", models.UUIDField(blank=True, default=None, null=True)),
("boto_last_updated", models.DateTimeField(auto_now=True)),
("received", models.BooleanField(default=False)),
(
@@ -613,15 +389,15 @@ class Migration(migrations.Migration):
(
"reminder_type",
models.CharField(
- choices=[
- ("before_due", "Before Due"),
- ("after_due", "After Due"),
- ("on_overdue", "On Overdue"),
- ],
+ choices=[("before_due", "Before Due"), ("after_due", "After Due"), ("on_overdue", "On Overdue")],
default="before_due",
max_length=100,
),
),
+ (
+ "invoice",
+ models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="invoice_reminders", to="backend.invoice"),
+ ),
],
options={
"verbose_name": "Invoice Reminder",
@@ -631,68 +407,56 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name="InvoiceURL",
fields=[
- (
- "expires",
- models.DateTimeField(
- blank=True,
- help_text="When the item will expire",
- null=True,
- verbose_name="Expires",
- ),
- ),
+ ("expires", models.DateTimeField(blank=True, help_text="When the item will expire", null=True, verbose_name="Expires")),
("active", models.BooleanField(default=True)),
(
"uuid",
shortuuid.django_fields.ShortUUIDField(
- alphabet=None,
- length=8,
- max_length=8,
- prefix="",
- primary_key=True,
- serialize=False,
+ alphabet=None, length=8, max_length=8, prefix="", primary_key=True, serialize=False
),
),
("system_created", models.BooleanField(default=False)),
("created_on", models.DateTimeField(auto_now_add=True)),
+ (
+ "created_by",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ (
+ "invoice",
+ models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name="invoice_urls", to="backend.invoice"),
+ ),
],
options={
"verbose_name": "Invoice URL",
"verbose_name_plural": "Invoice URLs",
},
),
+ migrations.CreateModel(
+ name="MonthlyReportRow",
+ fields=[
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("date", models.DateField()),
+ ("reference_number", models.CharField(max_length=100)),
+ ("item_type", models.CharField(max_length=100)),
+ ("client_name", models.CharField(blank=True, max_length=64, null=True)),
+ ("paid_in", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
+ ("paid_out", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
+ ("client", models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.client")),
+ ],
+ ),
migrations.CreateModel(
name="MonthlyReport",
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- (
- "uuid",
- models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
- ),
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("uuid", models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
("name", models.CharField(blank=True, max_length=100, null=True)),
- (
- "profit",
- models.DecimalField(decimal_places=2, default=0, max_digits=15),
- ),
+ ("profit", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
("invoices_sent", models.PositiveIntegerField(default=0)),
("start_date", models.DateField()),
("end_date", models.DateField()),
("recurring_customers", models.PositiveIntegerField(default=0)),
- (
- "payments_in",
- models.DecimalField(decimal_places=2, default=0, max_digits=15),
- ),
- (
- "payments_out",
- models.DecimalField(decimal_places=2, default=0, max_digits=15),
- ),
+ ("payments_in", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
+ ("payments_out", models.DecimalField(decimal_places=2, default=0, max_digits=15)),
(
"currency",
models.CharField(
@@ -709,58 +473,36 @@ class Migration(migrations.Migration):
max_length=3,
),
),
- ],
- options={
- "abstract": False,
- },
- ),
- migrations.CreateModel(
- name="MonthlyReportRow",
- fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- ("date", models.DateField()),
- ("reference_number", models.CharField(max_length=100)),
- ("item_type", models.CharField(max_length=100)),
- ("client_name", models.CharField(blank=True, max_length=64, null=True)),
(
- "paid_in",
- models.DecimalField(decimal_places=2, default=0, max_digits=15),
+ "organization",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.organization"),
),
(
- "paid_out",
- models.DecimalField(decimal_places=2, default=0, max_digits=15),
+ "user",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
+ ("items", models.ManyToManyField(blank=True, to="backend.monthlyreportrow")),
],
+ options={
+ "abstract": False,
+ },
),
migrations.CreateModel(
name="MultiFileUpload",
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("started_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
+ ("finished_at", models.DateTimeField(blank=True, editable=False, null=True)),
+ ("uuid", models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ("files", models.ManyToManyField(related_name="multi_file_uploads", to="backend.filestoragefile")),
(
- "finished_at",
- models.DateTimeField(blank=True, editable=False, null=True),
+ "organization",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.organization"),
),
(
- "uuid",
- models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
+ "user",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
],
options={
@@ -770,31 +512,22 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name="Receipt",
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("name", models.CharField(max_length=100)),
- (
- "image",
- models.ImageField(storage=core.models._private_storage, upload_to="receipts"),
- ),
+ ("image", models.ImageField(storage=core.models._private_storage, upload_to="receipts")),
("total_price", models.FloatField(blank=True, null=True)),
("date", models.DateField(blank=True, null=True)),
("date_uploaded", models.DateTimeField(auto_now_add=True)),
("receipt_parsed", models.JSONField(blank=True, null=True)),
+ ("merchant_store", models.CharField(blank=True, max_length=255, null=True)),
+ ("purchase_category", models.CharField(blank=True, max_length=200, null=True)),
(
- "merchant_store",
- models.CharField(blank=True, max_length=255, null=True),
+ "organization",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="core.organization"),
),
(
- "purchase_category",
- models.CharField(blank=True, max_length=200, null=True),
+ "user",
+ models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
],
options={
@@ -804,19 +537,98 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name="ReceiptDownloadToken",
fields=[
- (
- "id",
- models.BigAutoField(
- auto_created=True,
- primary_key=True,
- serialize=False,
- verbose_name="ID",
- ),
- ),
- (
- "token",
- models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
- ),
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("token", models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ("file", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="backend.receipt")),
+ ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
+ migrations.AddConstraint(
+ model_name="filestoragefile",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_filestoragefile_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="financedefaultvalues",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_financedefaultvalues_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="invoiceproduct",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_invoiceproduct_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="invoicerecurringprofile",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_invoicerecurringprofile_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="invoice",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_invoice_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="monthlyreport",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_monthlyreport_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="multifileupload",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_multifileupload_check_user_or_organization",
+ ),
+ ),
+ migrations.AddConstraint(
+ model_name="receipt",
+ constraint=models.CheckConstraint(
+ condition=models.Q(
+ models.Q(("organization__isnull", False), ("user__isnull", True)),
+ models.Q(("organization__isnull", True), ("user__isnull", False)),
+ _connector="OR",
+ ),
+ name="backend_receipt_check_user_or_organization",
+ ),
+ ),
]
diff --git a/backend/migrations/0002_initial.py b/backend/migrations/0002_initial.py
deleted file mode 100644
index f121c1a32..000000000
--- a/backend/migrations/0002_initial.py
+++ /dev/null
@@ -1,420 +0,0 @@
-# Generated by Django 5.1.4 on 2024-12-21 22:26
-
-import django.db.models.deletion
-from django.conf import settings
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ("backend", "0001_initial"),
- ("core", "0001_initial"),
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ]
-
- operations = [
- migrations.AddField(
- model_name="client",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="client",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="client",
- field=models.OneToOneField(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- related_name="default_values",
- to="backend.client",
- ),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="defaultvalues",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="filestoragefile",
- name="last_edited_by",
- field=models.ForeignKey(
- blank=True,
- editable=False,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- related_name="files_edited",
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="filestoragefile",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="filestoragefile",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="invoice",
- name="client_to",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- to="backend.client",
- ),
- ),
- migrations.AddField(
- model_name="invoice",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="invoice",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="invoice",
- name="items",
- field=models.ManyToManyField(blank=True, to="backend.invoiceitem"),
- ),
- migrations.AddField(
- model_name="invoiceproduct",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="invoiceproduct",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="invoicerecurringprofile",
- name="client_to",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- to="backend.client",
- ),
- ),
- migrations.AddField(
- model_name="invoicerecurringprofile",
- name="items",
- field=models.ManyToManyField(blank=True, to="backend.invoiceitem"),
- ),
- migrations.AddField(
- model_name="invoicerecurringprofile",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="invoicerecurringprofile",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="invoice",
- name="invoice_recurring_profile",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.SET_NULL,
- related_name="generated_invoices",
- to="backend.invoicerecurringprofile",
- ),
- ),
- migrations.AddField(
- model_name="invoicereminder",
- name="invoice",
- field=models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="invoice_reminders",
- to="backend.invoice",
- ),
- ),
- migrations.AddField(
- model_name="invoiceurl",
- name="created_by",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="invoiceurl",
- name="invoice",
- field=models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- related_name="invoice_urls",
- to="backend.invoice",
- ),
- ),
- migrations.AddField(
- model_name="monthlyreport",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="monthlyreport",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="monthlyreportrow",
- name="client",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="backend.client",
- ),
- ),
- migrations.AddField(
- model_name="monthlyreport",
- name="items",
- field=models.ManyToManyField(blank=True, to="backend.monthlyreportrow"),
- ),
- migrations.AddField(
- model_name="multifileupload",
- name="files",
- field=models.ManyToManyField(related_name="multi_file_uploads", to="backend.filestoragefile"),
- ),
- migrations.AddField(
- model_name="multifileupload",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="multifileupload",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="receipt",
- name="organization",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to="core.organization",
- ),
- ),
- migrations.AddField(
- model_name="receipt",
- name="user",
- field=models.ForeignKey(
- blank=True,
- null=True,
- on_delete=django.db.models.deletion.CASCADE,
- to=settings.AUTH_USER_MODEL,
- ),
- ),
- migrations.AddField(
- model_name="receiptdownloadtoken",
- name="file",
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="backend.receipt"),
- ),
- migrations.AddField(
- model_name="receiptdownloadtoken",
- name="user",
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
- ),
- migrations.AddConstraint(
- model_name="client",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_client_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="defaultvalues",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_defaultvalues_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="filestoragefile",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_filestoragefile_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="invoiceproduct",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_invoiceproduct_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="invoicerecurringprofile",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_invoicerecurringprofile_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="invoice",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_invoice_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="monthlyreport",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_monthlyreport_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="multifileupload",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_multifileupload_check_user_or_organization",
- ),
- ),
- migrations.AddConstraint(
- model_name="receipt",
- constraint=models.CheckConstraint(
- condition=models.Q(
- models.Q(("organization__isnull", False), ("user__isnull", True)),
- models.Q(("organization__isnull", True), ("user__isnull", False)),
- _connector="OR",
- ),
- name="backend_receipt_check_user_or_organization",
- ),
- ),
- ]
diff --git a/backend/modals.py b/backend/modals.py
index 53c56a8e6..1b469f30d 100644
--- a/backend/modals.py
+++ b/backend/modals.py
@@ -36,7 +36,7 @@ class SendSingleEmailModal(Modal, EmailContext):
def get(self, request: WebRequest):
if not get_feature_status("areUserEmailsAllowed"):
messages.error(request, "Emails are disabled")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
context = self.get_context(request)
@@ -45,7 +45,7 @@ def get(self, request: WebRequest):
if not invoice_url or not invoice_url.invoice.has_access(request.user):
messages.error(request, "You don't have access to this invoice")
- return render(request, "base/toast.html", {"autohide": False})
+ return render(request, "core/base/toast.html", {"autohide": False})
context["invoice"] = invoice_url.invoice
context["selected_clients"] = [
@@ -224,7 +224,7 @@ def get(self, request: WebRequest):
context["invoice"] = invoice
else:
messages.error(request, "You don't have access to this invoice")
- return render(request, "base/toasts.html")
+ return render(request, "core/base/toasts.html")
except Invoice.DoesNotExist:
return self.Response(request, context)
@@ -235,7 +235,7 @@ class SendEmailContext:
def get_context(self, request: WebRequest) -> dict:
if not get_feature_status("areUserEmailsAllowed"):
messages.error(request, "Emails are disabled")
- return render(request, "base/toast.html")
+ return render(request, "core/base/toast.html")
context = {}
@@ -259,7 +259,7 @@ def get(self, request: WebRequest):
if not invoice_url or not invoice_url.invoice.has_access(request.user):
messages.error(request, "You don't have access to this invoice")
- return render(request, "base/toast.html", {"autohide": False})
+ return render(request, "core/base/toast.html", {"autohide": False})
context["invoice"] = invoice_url.invoice
context["selected_clients"] = [
diff --git a/backend/models.py b/backend/models.py
index fcc2196aa..233555ea8 100644
--- a/backend/models.py
+++ b/backend/models.py
@@ -23,6 +23,8 @@
_public_storage,
upload_to_user_separate_folder,
RandomAPICode,
+ Client,
+ DefaultValuesBase,
)
from backend.finance.models import (
@@ -36,8 +38,7 @@
ReceiptDownloadToken,
MonthlyReport,
MonthlyReportRow,
+ FinanceDefaultValues,
)
-from backend.clients.models import Client, DefaultValues
-
from backend.storage.models import FileStorageFile, MultiFileUpload
diff --git a/backend/storage/api/delete.py b/backend/storage/api/delete.py
index 822a562ec..262be1105 100644
--- a/backend/storage/api/delete.py
+++ b/backend/storage/api/delete.py
@@ -4,7 +4,7 @@
from django.shortcuts import render
from django.views.decorators.http import require_http_methods
-from backend.decorators import htmx_only
+from core.decorators import htmx_only
from backend.models import FileStorageFile
from core.types.requests import WebRequest
@@ -31,7 +31,7 @@ def recursive_file_delete_endpoint(request: WebRequest) -> HttpResponse:
if failed_files:
messages.error(request, f"Failed to delete: {', '.join([file for file in failed_files])}")
- resp = render(request, "base/toast.html")
+ resp = render(request, "core/base/toast.html")
else:
resp = HttpResponse(status=200)
diff --git a/backend/storage/api/fetch.py b/backend/storage/api/fetch.py
index efdf23125..d6f6915e4 100644
--- a/backend/storage/api/fetch.py
+++ b/backend/storage/api/fetch.py
@@ -1,7 +1,7 @@
from django.utils.html import escape
from django.views.decorators.http import require_GET
-from backend.decorators import htmx_only
+from core.decorators import htmx_only
from backend.models import FileStorageFile
# from backend.core.service.billing.calculate.test import generate_monthly_billing_summary
diff --git a/frontend/templates/base/htmx.html b/frontend/templates/base/htmx.html
deleted file mode 100644
index 5d4b7d1a3..000000000
--- a/frontend/templates/base/htmx.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "core/base/htmx.html" %}
-{##}
-{% block left_drawer %}
- {% include "base/+left_drawer.html" with swap=True %}
-{% endblock %}
-{#{% include "base/breadcrumbs.html" with swap=True %}#}
-{#{% include "base/toasts.html" %}#}
diff --git a/frontend/templates/core/auth/auth.html b/frontend/templates/core/auth/auth.html
index 1fda5b9d3..170e420ff 100644
--- a/frontend/templates/core/auth/auth.html
+++ b/frontend/templates/core/auth/auth.html
@@ -1,6 +1,6 @@
{% extends "core/auth/auth.html" %}
{% block head %}
- {% include 'base/_head.html' %}
+ {% include 'core/base/_head.html' %}
{% endblock %}
{% block details %}
What do you get to manage?
diff --git a/frontend/templates/base/+left_drawer.html b/frontend/templates/core/base/+left_drawer.html
similarity index 100%
rename from frontend/templates/base/+left_drawer.html
rename to frontend/templates/core/base/+left_drawer.html
diff --git a/frontend/templates/base/_head.html b/frontend/templates/core/base/_head.html
similarity index 100%
rename from frontend/templates/base/_head.html
rename to frontend/templates/core/base/_head.html
diff --git a/frontend/templates/base/base.html b/frontend/templates/core/base/base.html
similarity index 53%
rename from frontend/templates/base/base.html
rename to frontend/templates/core/base/base.html
index 62456e1af..7a46e8c7a 100644
--- a/frontend/templates/base/base.html
+++ b/frontend/templates/core/base/base.html
@@ -1,7 +1,7 @@
{% extends "core/base/base.html" %}
{% block topbar %}
- {% include 'base/topbar/_topbar.html' %}
+ {% include 'core/base/topbar/_topbar.html' %}
{% endblock %}
{% block drawer %}
- {% include "base/+left_drawer.html" %}
+ {% include "core/base/+left_drawer.html" %}
{% endblock drawer %}
diff --git a/frontend/templates/base/breadcrumbs.html b/frontend/templates/core/base/breadcrumbs.html
similarity index 90%
rename from frontend/templates/base/breadcrumbs.html
rename to frontend/templates/core/base/breadcrumbs.html
index f35fa4ab7..e8c55a02d 100644
--- a/frontend/templates/base/breadcrumbs.html
+++ b/frontend/templates/core/base/breadcrumbs.html
@@ -8,7 +8,7 @@
hx-swap="innerHTML"
{% if swap %}hx-swap-oob='outerHTML:div[data-layout="breadcrumbs"]'{% endif %}>
{% if breadcrumb %}
- {% include "base/breadcrumbs_ul.html" %}
+ {% include "core/base/breadcrumbs_ul.html" %}
{% endif %}
{% endif %}
diff --git a/frontend/templates/base/breadcrumbs_ul.html b/frontend/templates/core/base/breadcrumbs_ul.html
similarity index 100%
rename from frontend/templates/base/breadcrumbs_ul.html
rename to frontend/templates/core/base/breadcrumbs_ul.html
diff --git a/frontend/templates/core/base/htmx.html b/frontend/templates/core/base/htmx.html
new file mode 100644
index 000000000..15c065300
--- /dev/null
+++ b/frontend/templates/core/base/htmx.html
@@ -0,0 +1,7 @@
+{% extends "core/base/htmx.html" %}
+{##}
+{% block left_drawer %}
+ {% include "core/base/+left_drawer.html" with swap=True %}
+{% endblock %}
+{#{% include "core/base/breadcrumbs.html" with swap=True %}#}
+{#{% include "core/base/toasts.html" %}#}
diff --git a/frontend/templates/base/toast.html b/frontend/templates/core/base/toast.html
similarity index 100%
rename from frontend/templates/base/toast.html
rename to frontend/templates/core/base/toast.html
diff --git a/frontend/templates/base/toasts.html b/frontend/templates/core/base/toasts.html
similarity index 100%
rename from frontend/templates/base/toasts.html
rename to frontend/templates/core/base/toasts.html
diff --git a/frontend/templates/base/topbar/+icon_dropdown.html b/frontend/templates/core/base/topbar/+icon_dropdown.html
similarity index 96%
rename from frontend/templates/base/topbar/+icon_dropdown.html
rename to frontend/templates/core/base/topbar/+icon_dropdown.html
index cc726821d..542588256 100644
--- a/frontend/templates/base/topbar/+icon_dropdown.html
+++ b/frontend/templates/core/base/topbar/+icon_dropdown.html
@@ -37,7 +37,7 @@
{% endif %}
- Current Version: {{ version }}
+ Current Version: {{ finances_version }}
{% if git_version and git_version != "prod" %}
diff --git a/frontend/templates/base/topbar/_notification_count.html b/frontend/templates/core/base/topbar/_notification_count.html
similarity index 100%
rename from frontend/templates/base/topbar/_notification_count.html
rename to frontend/templates/core/base/topbar/_notification_count.html
diff --git a/frontend/templates/base/topbar/_notification_dropdown_items.html b/frontend/templates/core/base/topbar/_notification_dropdown_items.html
similarity index 98%
rename from frontend/templates/base/topbar/_notification_dropdown_items.html
rename to frontend/templates/core/base/topbar/_notification_dropdown_items.html
index 9e20b20da..6f1b4ce77 100644
--- a/frontend/templates/base/topbar/_notification_dropdown_items.html
+++ b/frontend/templates/core/base/topbar/_notification_dropdown_items.html
@@ -85,5 +85,5 @@
{% endif %}
{% if not initial_load %}
- {% include "base/topbar/_notification_count.html" %}
+ {% include "core/base/topbar/_notification_count.html" %}
{% endif %}
diff --git a/frontend/templates/base/topbar/_organizations_list.html b/frontend/templates/core/base/topbar/_organizations_list.html
similarity index 100%
rename from frontend/templates/base/topbar/_organizations_list.html
rename to frontend/templates/core/base/topbar/_organizations_list.html
diff --git a/frontend/templates/base/topbar/_topbar.html b/frontend/templates/core/base/topbar/_topbar.html
similarity index 100%
rename from frontend/templates/base/topbar/_topbar.html
rename to frontend/templates/core/base/topbar/_topbar.html
diff --git a/frontend/templates/base/topbar/team_selector/selector.html b/frontend/templates/core/base/topbar/team_selector/selector.html
similarity index 100%
rename from frontend/templates/base/topbar/team_selector/selector.html
rename to frontend/templates/core/base/topbar/team_selector/selector.html
diff --git a/frontend/templates/pages/clients/create/create.html b/frontend/templates/pages/clients/create/create.html
index 05711b8ce..eb0de8c7e 100644
--- a/frontend/templates/pages/clients/create/create.html
+++ b/frontend/templates/pages/clients/create/create.html
@@ -1,4 +1,4 @@
-{% extends base|default:"base/base.html" %}
+{% extends base|default:"core/base/base.html" %}
{% load static %}
{% block content %}
@@ -10,7 +10,7 @@
Single
+ class="toggle toggle-primary"/>
Representative
@@ -29,7 +29,7 @@
minlength="3"
type="text"
class="peer input input-bordered w-full"
- placeholder="Bob Smith" />
+ placeholder="Bob Smith"/>
@@ -49,7 +49,7 @@
minlength="4"
type="email"
class="peer input input-bordered w-full"
- placeholder="bsmith@example.com" />
+ placeholder="bsmith@example.com"/>
@@ -71,7 +71,7 @@
minlength="3"
type="text"
class="peer input input-bordered w-full"
- placeholder="Google" />
+ placeholder="Google"/>
@@ -113,7 +113,7 @@
+ placeholder="07000000000"/>
@@ -130,7 +130,7 @@
+ placeholder="Enter email, phone number, etc."/>
diff --git a/frontend/templates/pages/clients/dashboard/dashboard.html b/frontend/templates/pages/clients/dashboard/dashboard.html
index 5adee025b..2711e29df 100644
--- a/frontend/templates/pages/clients/dashboard/dashboard.html
+++ b/frontend/templates/pages/clients/dashboard/dashboard.html
@@ -1,4 +1,4 @@
-{% extends base|default:"base/base.html" %}
+{% extends base|default:"core/base/base.html" %}
{% block content %}
Clients
diff --git a/frontend/templates/pages/clients/detail/dashboard.html b/frontend/templates/pages/clients/detail/dashboard.html
index 853b95676..0a46e4987 100644
--- a/frontend/templates/pages/clients/detail/dashboard.html
+++ b/frontend/templates/pages/clients/detail/dashboard.html
@@ -1,4 +1,4 @@
-{% extends base|default:"base/base.html" %}
+{% extends base|default:"core/base/base.html" %}
{% block content %}
@@ -33,11 +33,11 @@
{{ client.name }}
-
+ hx-trigger="load" {# click once #}
+ hx-target='find div[data-htmx="details_container"]'
+ hx-swap="innerHTML"
+ hx-get="{% url 'api:settings:client_defaults' client_id=client.id %}">
+
Customer Defaults
diff --git a/frontend/templates/pages/create_account.html b/frontend/templates/pages/create_account.html
index 0f3de62f1..85ac36295 100644
--- a/frontend/templates/pages/create_account.html
+++ b/frontend/templates/pages/create_account.html
@@ -1,97 +1,99 @@
{% load static %}
- {% include 'partials/base/_head.html' %}
-
-
-
-
-
-

-

-
-
-
- {% include 'base/toasts.html' %}
-
Create account
-
+
diff --git a/frontend/templates/pages/dashboard.html b/frontend/templates/pages/dashboard.html
index f3dd59102..31ccd60b5 100644
--- a/frontend/templates/pages/dashboard.html
+++ b/frontend/templates/pages/dashboard.html
@@ -1,4 +1,4 @@
-{% extends base|default:"base/base.html" %}
+{% extends base|default:"core/base/base.html" %}
{% block content %}
diff --git a/frontend/templates/pages/emails/dashboard.html b/frontend/templates/pages/emails/dashboard.html
index 0b388d526..884d015f3 100644
--- a/frontend/templates/pages/emails/dashboard.html
+++ b/frontend/templates/pages/emails/dashboard.html
@@ -1,4 +1,4 @@
-{% extends base|default:"base/base.html" %}
+{% extends base|default:"core/base/base.html" %}
{% block content %}
@@ -58,18 +58,18 @@
-
- ID |
- Sent Date |
- Recipient |
- Status |
-
+
+ ID |
+ Sent Date |
+ Recipient |
+ Status |
+
- {% include 'components/table/skeleton_rows.html' with rows=3 cols=5 %}
+ {% include 'components/table/skeleton_rows.html' with rows=3 cols=5 %}
{#