mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-02-23 18:22:43 +01:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e6e203f8a | ||
|
|
2192149b5a | ||
|
|
a4e19196a7 | ||
|
|
0c744c5444 | ||
|
|
e0e5fb3d5a | ||
|
|
1125096e5a | ||
|
|
fc1d2269d0 | ||
|
|
cc033d5be7 | ||
|
|
7eee3de965 | ||
|
|
0c6245fe8e | ||
|
|
342ed382e3 | ||
|
|
aaf6c37871 | ||
|
|
65e1346a11 | ||
|
|
7f9307feec | ||
|
|
036eaf3bae | ||
|
|
2717d7d311 | ||
|
|
577b841ee0 | ||
|
|
857eb0517c | ||
|
|
ec50197b40 | ||
|
|
4ace7dd370 | ||
|
|
0eea7f8d4d | ||
|
|
80c7680d17 | ||
|
|
3edc0a7f53 | ||
|
|
29af14f588 | ||
|
|
5f2408b791 | ||
|
|
5b5e8a4fd5 | ||
|
|
71b0c2d83e | ||
|
|
363b7bc314 | ||
|
|
448032c5b7 | ||
|
|
2af1234cfd | ||
|
|
d258235430 | ||
|
|
c060d6ebb1 | ||
|
|
72dab2bc4e | ||
|
|
b0d2a22f62 | ||
|
|
bcda71cb25 | ||
|
|
d32e902d17 | ||
|
|
f91b719542 | ||
|
|
8bccab258a | ||
|
|
6443d8e2bf | ||
|
|
286759f232 | ||
|
|
0dba32fdf2 | ||
|
|
54c6757bc7 | ||
|
|
c91a6640ff | ||
|
|
80ef617949 | ||
|
|
72dd3f92f9 | ||
|
|
5330476dbe | ||
|
|
69fdc85c99 | ||
|
|
f7293508ff | ||
|
|
4aedce9668 | ||
|
|
9244fe5944 | ||
|
|
35710b17d1 | ||
|
|
fb78ce5679 | ||
|
|
749e7dbdf9 | ||
|
|
ccae58cb2f | ||
|
|
64199b91d5 | ||
|
|
c8218f6891 | ||
|
|
8e2f297839 | ||
|
|
0feb9661df | ||
|
|
1acceae81e | ||
|
|
a7ff690891 | ||
|
|
447b54fa4b | ||
|
|
5f5541ca12 | ||
|
|
f101e1b184 | ||
|
|
065417038c | ||
|
|
047c82791b | ||
|
|
f1672c7076 | ||
|
|
e7e57fa412 | ||
|
|
5536fcce00 | ||
|
|
8a3ce36c65 | ||
|
|
325812fe95 | ||
|
|
421a5d27dd | ||
|
|
27b43041f9 | ||
|
|
a7ea12d07d | ||
|
|
927f570283 | ||
|
|
66c1eff79f | ||
|
|
4cb1313a77 | ||
|
|
52bdde40a1 | ||
|
|
8295ed716b | ||
|
|
d84ee57354 | ||
|
|
0ae57b8b7b | ||
|
|
a4e68ea2d6 | ||
|
|
a48b4ccaa8 | ||
|
|
bcaf8e9912 | ||
|
|
ae438f1650 | ||
|
|
563d6bccd3 | ||
|
|
7220d752ac | ||
|
|
46beb21ba7 | ||
|
|
c972f0ac59 | ||
|
|
21c74fbcc8 | ||
|
|
1ca839ab26 | ||
|
|
34aefd32e8 | ||
|
|
fce32e70b9 | ||
|
|
0550c045c7 | ||
|
|
69b1c062f5 | ||
|
|
4713b2f079 | ||
|
|
6fe907a13d | ||
|
|
45ce4ac1ba |
@@ -26,7 +26,7 @@
|
||||
|
||||
# Pass the configuration from the docker env to the PHP environment (here you should list all .env options)
|
||||
PassEnv APP_ENV APP_DEBUG APP_SECRET
|
||||
PassEnv DATABASE_URL
|
||||
PassEnv DATABASE_URL ENFORCE_CHANGE_COMMENTS_FOR
|
||||
PassEnv DEFAULT_LANG DEFAULT_TIMEZONE BASE_CURRENCY INSTANCE_NAME ALLOW_ATTACHMENT_DOWNLOADS USE_GRAVATAR MAX_ATTACHMENT_FILE_SIZE DEFAULT_URI
|
||||
PassEnv MAILER_DSN ALLOW_EMAIL_PW_RESET EMAIL_SENDER_EMAIL EMAIL_SENDER_NAME
|
||||
PassEnv HISTORY_SAVE_CHANGED_FIELDS HISTORY_SAVE_CHANGED_DATA HISTORY_SAVE_REMOVED_DATA
|
||||
|
||||
5
.env
5
.env
@@ -39,6 +39,11 @@ MAX_ATTACHMENT_FILE_SIZE="100M"
|
||||
# This must end with a slash!
|
||||
DEFAULT_URI="https://partdb.changeme.invalid/"
|
||||
|
||||
# With this option you can configure, where users are enforced to give a change reason, which will be logged
|
||||
# This is a comma separated list of values, see documentation for available values
|
||||
# Leave this empty, to make all change reasons optional
|
||||
ENFORCE_CHANGE_COMMENTS_FOR=""
|
||||
|
||||
###################################################################################
|
||||
# Email settings
|
||||
###################################################################################
|
||||
|
||||
@@ -96,6 +96,8 @@ const PLACEHOLDERS = [
|
||||
['[[AMOUNT]]', 'Lot amount'],
|
||||
['[[LOCATION]]', 'Storage location'],
|
||||
['[[LOCATION_FULL]]', 'Storage location (Full path)'],
|
||||
['[[OWNER]]', 'Full name of the lot owner'],
|
||||
['[[OWNER_USERNAME]]', 'Username of the lot owner'],
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -110,6 +112,8 @@ const PLACEHOLDERS = [
|
||||
['[[COMMENT_T]]', 'Comment (plain text)'],
|
||||
['[[LAST_MODIFIED]]', 'Last modified datetime'],
|
||||
['[[CREATION_DATE]]', 'Creation datetime'],
|
||||
['[[OWNER]]', 'Full name of the location owner'],
|
||||
['[[OWNER_USERNAME]]', 'Username of the location owner'],
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -55,6 +55,8 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, {
|
||||
'Lot amount': 'Lot Menge',
|
||||
'Storage location': 'Lagerort',
|
||||
'Storage location (Full path)': 'Lagerort (Vollständiger Pfad)',
|
||||
'Full name of the lot owner': 'Name des Besitzers des Lots',
|
||||
'Username of the lot owner': 'Benutzername des Besitzers des Lots',
|
||||
|
||||
|
||||
'Barcodes': 'Barcodes',
|
||||
@@ -69,6 +71,8 @@ Object.assign( window.CKEDITOR_TRANSLATIONS[ 'de' ].dictionary, {
|
||||
'Full path': 'Vollständiger Pfad',
|
||||
'Parent name': 'Name des Übergeordneten Elements',
|
||||
'Parent full path': 'Ganzer Pfad des Übergeordneten Elements',
|
||||
'Full name of the location owner': 'Name des Besitzers des Lagerorts',
|
||||
'Username of the location owner': 'Benutzername des Besitzers des Lagerorts',
|
||||
|
||||
'Username': 'Benutzername',
|
||||
'Username (including name)': 'Benutzername (inklusive Name)',
|
||||
|
||||
@@ -27,7 +27,9 @@ class ErrorHandlerHelper {
|
||||
constructor() {
|
||||
console.log('Error Handler registered');
|
||||
|
||||
const content = document.getElementById('content');
|
||||
//const content = document.getElementById('content');
|
||||
//It seems that the content element is unreliable for these events, so we use the document instead
|
||||
const content = document;
|
||||
//content.addEventListener('turbo:before-fetch-response', (event) => this.handleError(event));
|
||||
content.addEventListener('turbo:fetch-request-error', (event) => this.handleError(event));
|
||||
content.addEventListener('turbo:frame-missing', (event) => this.handleError(event));
|
||||
@@ -37,6 +39,55 @@ class ErrorHandlerHelper {
|
||||
|
||||
_showAlert(statusText, statusCode, location, responseHTML)
|
||||
{
|
||||
const httpStatusToText = {
|
||||
'200': 'OK',
|
||||
'201': 'Created',
|
||||
'202': 'Accepted',
|
||||
'203': 'Non-Authoritative Information',
|
||||
'204': 'No Content',
|
||||
'205': 'Reset Content',
|
||||
'206': 'Partial Content',
|
||||
'300': 'Multiple Choices',
|
||||
'301': 'Moved Permanently',
|
||||
'302': 'Found',
|
||||
'303': 'See Other',
|
||||
'304': 'Not Modified',
|
||||
'305': 'Use Proxy',
|
||||
'306': 'Unused',
|
||||
'307': 'Temporary Redirect',
|
||||
'400': 'Bad Request',
|
||||
'401': 'Unauthorized',
|
||||
'402': 'Payment Required',
|
||||
'403': 'Forbidden',
|
||||
'404': 'Not Found',
|
||||
'405': 'Method Not Allowed',
|
||||
'406': 'Not Acceptable',
|
||||
'407': 'Proxy Authentication Required',
|
||||
'408': 'Request Timeout',
|
||||
'409': 'Conflict',
|
||||
'410': 'Gone',
|
||||
'411': 'Length Required',
|
||||
'412': 'Precondition Required',
|
||||
'413': 'Request Entry Too Large',
|
||||
'414': 'Request-URI Too Long',
|
||||
'415': 'Unsupported Media Type',
|
||||
'416': 'Requested Range Not Satisfiable',
|
||||
'417': 'Expectation Failed',
|
||||
'418': 'I\'m a teapot',
|
||||
'429': 'Too Many Requests',
|
||||
'500': 'Internal Server Error',
|
||||
'501': 'Not Implemented',
|
||||
'502': 'Bad Gateway',
|
||||
'503': 'Service Unavailable',
|
||||
'504': 'Gateway Timeout',
|
||||
'505': 'HTTP Version Not Supported',
|
||||
};
|
||||
|
||||
//If the statusText is empty, we use the status code as text
|
||||
if (!statusText) {
|
||||
statusText = httpStatusToText[statusCode];
|
||||
}
|
||||
|
||||
//Create error text
|
||||
const title = statusText + ' (Status ' + statusCode + ')';
|
||||
|
||||
@@ -104,6 +155,12 @@ class ErrorHandlerHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
//Skip 404 errors, on admin pages (as this causes a popup on deletion in firefox)
|
||||
if (response.status == 404 && event.target.id === 'admin-content-frame') {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if(!response.ok) {
|
||||
response.text().then(responseHTML => {
|
||||
this._showAlert(response.statusText, response.status, response.url, responseHTML);
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
import {Tab} from "bootstrap";
|
||||
import {Tab, Dropdown} from "bootstrap";
|
||||
import tab from "bootstrap/js/src/tab";
|
||||
|
||||
/**
|
||||
@@ -63,6 +63,16 @@ class TabRememberHelper {
|
||||
*/
|
||||
onInvalid(event) {
|
||||
this.revealElementOnTab(event.target);
|
||||
this.revealElementInDropdown(event.target);
|
||||
}
|
||||
|
||||
revealElementInDropdown(element) {
|
||||
let dropdown = element.closest('.dropdown-menu');
|
||||
|
||||
if(dropdown) {
|
||||
let bs_dropdown = Dropdown.getOrCreateInstance(dropdown);
|
||||
bs_dropdown.show();
|
||||
}
|
||||
}
|
||||
|
||||
revealElementOnTab(element) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-dom": "*",
|
||||
"beberlei/doctrineextensions": "^1.2",
|
||||
"brick/math": "^0.8.15",
|
||||
"composer/package-versions-deprecated": "1.11.99.4",
|
||||
|
||||
652
composer.lock
generated
652
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,8 @@ doctrine:
|
||||
class: App\Doctrine\Types\UTCDateTimeType
|
||||
big_decimal:
|
||||
class: App\Doctrine\Types\BigDecimalType
|
||||
tinyint:
|
||||
class: App\Doctrine\Types\TinyIntType
|
||||
|
||||
schema_filter: ~^(?!internal)~
|
||||
# Only enable this when needed
|
||||
|
||||
@@ -3,9 +3,12 @@ liip_imagine:
|
||||
# valid drivers options include "gd" or "gmagick" or "imagick"
|
||||
driver: "gd"
|
||||
|
||||
default_filter_set_settings:
|
||||
format: webp
|
||||
|
||||
filter_sets:
|
||||
thumbnail_sm:
|
||||
quality: 90
|
||||
quality: 65
|
||||
filters:
|
||||
thumbnail:
|
||||
size: [150, 150]
|
||||
@@ -20,7 +23,7 @@ liip_imagine:
|
||||
mode: inset
|
||||
|
||||
thumbnail_xs:
|
||||
quality: 90
|
||||
quality: 60
|
||||
filters:
|
||||
thumbnail:
|
||||
size: [50, 50]
|
||||
|
||||
@@ -12,6 +12,7 @@ parameters:
|
||||
partdb.default_currency: '%env(string:BASE_CURRENCY)%' # The currency that is used inside the DB (and is assumed when no currency is set). This can not be changed later, so be sure to set it the currency used in your country
|
||||
partdb.global_theme: '' # The theme to use globally (see public/build/themes/ for choices, use name without .css). Set to '' for default bootstrap theme
|
||||
partdb.locale_menu: ['en', 'de', 'fr', 'ru', 'ja'] # The languages that are shown in user drop down menu
|
||||
partdb.enforce_change_comments_for: '%env(csv:ENFORCE_CHANGE_COMMENTS_FOR)%' # The actions for which a change comment is required (e.g. "part_edit", "part_create", etc.). If this is empty, change comments are not required at all.
|
||||
|
||||
partdb.default_uri: '%env(string:DEFAULT_URI)%' # The default URI to use for the Part-DB instance (e.g. https://part-db.example.com/). This is used for generating links in emails
|
||||
|
||||
@@ -105,6 +106,8 @@ parameters:
|
||||
env(USE_GRAVATAR): '0'
|
||||
env(MAX_ATTACHMENT_FILE_SIZE): '100M'
|
||||
|
||||
env(ENFORCE_CHANGE_COMMENTS_FOR): ''
|
||||
|
||||
env(ERROR_PAGE_ADMIN_EMAIL): ''
|
||||
env(ERROR_PAGE_SHOW_HELP): 1
|
||||
|
||||
|
||||
@@ -102,6 +102,10 @@ services:
|
||||
event: 'Symfony\Component\Security\Http\Event\LogoutEvent'
|
||||
dispatcher: security.event_dispatcher.main
|
||||
|
||||
App\Services\LogSystem\EventCommentNeededHelper:
|
||||
arguments:
|
||||
$enforce_change_comments_for: '%partdb.enforce_change_comments_for%'
|
||||
|
||||
####################################################################################################################
|
||||
# Attachment system
|
||||
####################################################################################################################
|
||||
|
||||
@@ -28,6 +28,14 @@ The following configuration options can only be changed by the server administra
|
||||
* `USE_GRAVATAR`: Set to `1` to use [gravatar.com](gravatar.com) images for user avatars (as long as they have not set their own picture). The users browsers have to download the pictures from a third-party (gravatars) server, so this might be a privacy risk.
|
||||
* `MAX_ATTACHMENT_FILE_SIZE`: The maximum file size (in bytes) for attachments. You can use the suffix `K`, `M` or `G` to specify the size in kilobytes, megabytes or gigabytes. By default `100M` (100 megabytes). Please note that this only the limit of Part-DB. You still need to configure the php.ini `upload_max_filesize` and `post_max_size` to allow bigger files to be uploaded.
|
||||
* `DEFAULT_URI`: The default URI base to use for the Part-DB, when no URL can be determined from the browser request. This should be the primary URL/Domain, which is used to access Part-DB. This value is used to create correct links in emails and other places, where the URL is needed. It is also used, when SAML is enabled.s If you are using a reverse proxy, you should set this to the URL of the reverse proxy (e.g. `https://part-db.example.com`). **This value must end with a slash**.
|
||||
* `ENFORCE_CHANGE_COMMENTS_FOR`: With this option you can configure, where users are enforced to give a change reason, which will be written to the log. This is a comma separated list of values (e.g. `part_edit,part_delete`). Leave empty to make change comments optional everywhere. Possible values are:
|
||||
* `part_edit`: Edit operation of a existing part
|
||||
* `part_delete`: Delete operation of a existing part
|
||||
* `part_create`: Creation of a new part
|
||||
* `part_stock_operation`: Stock operation on a part (therefore withdraw, add or move stock)
|
||||
* `datastructure_edit`: Edit operation of a existing datastructure (e.g. category, manufacturer, ...)
|
||||
* `datastructure_delete`: Delete operation of a existing datastructure (e.g. category, manufacturer, ...)
|
||||
* `datastructure_create`: Creation of a new datastructure (e.g. category, manufacturer, ...)
|
||||
|
||||
### E-Mail settings
|
||||
* `MAILER_DSN`: You can configure the mail provider which should be used for email delivery (see https://symfony.com/doc/current/components/mailer.html for full documentation). If you just want to use an SMTP mail account, you can use the following syntax `MAILER_DSN=smtp://user:password@smtp.mailserver.invalid:587`
|
||||
|
||||
@@ -28,7 +28,7 @@ It is installed on a web server and so can be accessed with any browser without
|
||||
* User system with groups and detailed (fine granular) permissions.
|
||||
Two-factor authentication is supported (Google Authenticator and Webauthn/U2F keys) and can be enforced for groups. Password reset via email can be setuped.
|
||||
* Optional support for single sign-on (SSO) via SAML (using an intermediate service like [Keycloak](https://www.keycloak.org/) you can connect Part-DB to an existing LDAP or Active Directory server)
|
||||
* Import/Export system (partial working)
|
||||
* Import/Export system
|
||||
* Project management: Create projects and assign parts to the bill of material (BOM), to show how often you could build this project and directly withdraw all components needed from DB
|
||||
* Event log: Track what changes happens to your inventory, track which user does what. Revert your parts to older versions.
|
||||
* Responsive design: You can use Part-DB on your PC, your tablet and your smartphone using the same interface.
|
||||
@@ -36,7 +36,7 @@ It is installed on a web server and so can be accessed with any browser without
|
||||
* Support for rich text descriptions and comments in parts
|
||||
* Support for multiple currencies and automatic update of exchange rates supported
|
||||
* Powerful search and filter function, including parametric search (search for parts according to some specifications)
|
||||
|
||||
* Easy migration from an existing PartKeepr instance (see [here]({%link partkeepr_migration.md %}))
|
||||
|
||||
With these features Part-DB is useful to hobbyists, who want to keep track of their private electronic parts inventory,
|
||||
or makerspaces, where many users have should have (controlled) access to the shared inventory.
|
||||
|
||||
@@ -199,7 +199,7 @@ sudo nano config/parameters.yaml
|
||||
sudo -u www-data php bin/console doctrine:migrations:migrate
|
||||
|
||||
# Clear Part-DB cache
|
||||
sudo u www-data php bin/console cache:clear
|
||||
sudo -u www-data php bin/console cache:clear
|
||||
```
|
||||
|
||||
## MySQL/MariaDB database
|
||||
|
||||
51
docs/partkeepr_migration.md
Normal file
51
docs/partkeepr_migration.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
layout: default
|
||||
title: Migrate from PartKeepr to Part-DB
|
||||
nav_order: 101
|
||||
---
|
||||
|
||||
# Migrate from PartKeepr to Part-DB
|
||||
|
||||
{: .warning }
|
||||
> This feature is currently in beta. Please report any bugs you find.
|
||||
|
||||
This guide describes how to migrate from [PartKeepr](https://partkeepr.org/) to Part-DB.
|
||||
|
||||
Part-DB has a built-in migration tool, which can be used to migrate the data from an existing PartKeepr instance to
|
||||
a new Part-DB instance. Most of the data can be migrated, however there are some limitations, you can find below.
|
||||
|
||||
## What can be imported
|
||||
* Datastructures (Categories, Footprints, Storage Locations, Manufacturers, Distributors, Part Measurement Units)
|
||||
* Basic part informations (Name, Description, Comment, etc.)
|
||||
* Attachments and images of parts, projects, footprints, manufacturers and storage locations
|
||||
* Part prices (distributor infos)
|
||||
* Part parameters
|
||||
* Projects (including parts and attachments)
|
||||
* Users (optional): Passwords however will be not migrated, and need to be reset later
|
||||
|
||||
## What can't be imported
|
||||
* Metaparts (A dummy version of the metapart will be created in Part-DB, however it will not function as metapart)
|
||||
* Multiple manufacturers per part (only the last manufacturer of a part will be migrated)
|
||||
* Overage information for project parts (the overage info will be set as comment in the project BOM, but will have no effect)
|
||||
* Batch Jobs
|
||||
* Parameter Units (the units will be written into the parameters)
|
||||
* Project Reports and Project Runs
|
||||
* Stock history
|
||||
* Any kind of PartKeepr preferences
|
||||
|
||||
## How to migrate
|
||||
1. Install Part-DB like described in the installation guide. You can use any database backend you want (mysql or sqlite). Run the database migration, but do not create any new data yet.
|
||||
2. Export your PartKeepr database as XML file using [mysqldump](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html):
|
||||
When the MySQL database is running on the local computer and you are root you can just run the command `mysqldump --xml PARTKEEPR_DATABASE --result-file pk.xml`.
|
||||
If your server is remote or your MySQL authentication is different, you need to run `mysqldump --xml -h PARTKEEPR_HOST -u PARTKEEPR_USER -p PARTKEEPR_DATABASE`, where you replace `PARTKEEPR_HOST`
|
||||
with the hostname of your MySQL database and `PARTKEEPR_USER` with the username of MySQL user which has access to the PartKeepr database. You will be asked for the MySQL user password.
|
||||
3. Go the Part-DB main folder and run the command `php bin/console partdb:migrations:import-partkeepr path/to/pk.xml`. This step will delete all existing data in the Part-DB database and import the contents of PartKeepr.
|
||||
4. Copy the contents of `data/files/` from your PartKeepr installation to the `uploads/` folder of your Part-DB installation and the contents of `data/images` from PartKeepr to `public/media/` of Part-DB.
|
||||
5. Clear the cache of Part-DB by running: `php bin/console cache:clear`
|
||||
6. Go to the Part-DB web interface. You can login with the username `admin` and the password, which is shown during the installation process of Part-DB (step 1). You should be able to see all the data from PartKeepr.
|
||||
|
||||
## Import users
|
||||
If you want to import the users (mostly the username and email address) from PartKeepr, you can add the `--import-users` option on the database import command (step 3): `php bin/console partdb:migrations:import-partkeepr --import-users path/to/pk.xml`.
|
||||
|
||||
All imported users of PartKeepr will be assigned to a new group "PartKeepr Users", which has normal user permissions (so editing data, but no administrative tasks). You can change the group and permissions later in Part-DB users managment.
|
||||
Passwords can not be imported from PartKeepr and all imported users get marked as disabled user. So to allow users to login, you need to enable them in the user management and assign a password.
|
||||
@@ -22,6 +22,11 @@ php bin/console doctrine:migrations:migrate
|
||||
|
||||
If this does not help, please [open an issue on GitHub](https://github.com/Part-DB/Part-DB-symfony).
|
||||
|
||||
## Search for user and reset password:
|
||||
You can list all users with the following command: `php bin/console partdb:users:list`
|
||||
To reset the password of a user you can use the following command: `php bin/console partdb:users:set-password [username]`
|
||||
|
||||
|
||||
## Error logs
|
||||
Detailed error logs can be found in the `var/log` directory.
|
||||
When Part-DB is installed directly, the errors are written to the `var/log/prod.log` file.
|
||||
@@ -35,4 +40,4 @@ docker-compose logs -f
|
||||
Please include the error logs in your issue on GitHub, if you open an issue.
|
||||
|
||||
## Report Issue
|
||||
If an error occurs, or you found a bug, please [open an issue on GitHub](https://github.com/Part-DB/Part-DB-symfony).
|
||||
If an error occurs, or you found a bug, please [open an issue on GitHub](https://github.com/Part-DB/Part-DB-symfony).
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
---
|
||||
layout: default
|
||||
title: Upgrade from legacy Part-DB version (<1.0)
|
||||
nav_order: 100
|
||||
---
|
||||
|
||||
# Upgrade from legacy Part-DB version
|
||||
|
||||
@@ -31,6 +31,7 @@ You can get help for every command with the parameter `--help`. See `php bin/con
|
||||
* `partdb:migrations:convert-bbcode`: Migrate the old BBCode markup codes used in legacy Part-DB versions (< 1.0.0) to the new markdown syntax
|
||||
* `partdb:attachments:clean-unused`: Remove all attachments which are not used by any database entry (e.g. orphaned attachments)
|
||||
* `partdb:cache:clear`: Clears all caches, so the next page load will be slower, but the cache will be rebuild. This can maybe fix some issues, when the cache were corrupted. This command is also needed after changing things in the `parameters.yaml` file or upgrading Part-DB.
|
||||
* `partdb:migrations:import-partkeepr`: Imports an mysqldump XML dump of a PartKeepr database into Part-DB. This is only needed for users, which want to migrate from PartKeepr to Part-DB. *All existing data in the Part-DB database is deleted!*
|
||||
|
||||
## Database commands
|
||||
* `php bin/console doctrine:migrations:migrate`: Migrate the database to the latest version
|
||||
|
||||
@@ -16,6 +16,8 @@ Part-DB offers the possibility to import existing data (parts, datastructures, e
|
||||
> administrators. If you want to allow other users to import data, or can not import data, check the permissions of the user. You can enable import for each data structure
|
||||
> individually in the permissions settings.
|
||||
|
||||
If you want to import data from PartKeepr you might want to look into the [PartKeepr migration guide]({% link upgrade_legacy.md %}).
|
||||
|
||||
### Import parts
|
||||
|
||||
Part-DB supports the import of parts from CSV files and other formats. This can be used to import existing parts from other databases or datasources into Part-DB. The import can be done via the "Tools -> Import parts" page, which you can find in the "Tools" sidebar panel.
|
||||
|
||||
@@ -62,4 +62,14 @@ by calling the `php bin/console partdb:currencies:update-exchange-rates`.
|
||||
If you call this command regularly (e.g. with a cronjob), you can keep the exchange rates up-to-date.
|
||||
|
||||
Please note that if you use a base currency, which is not the Euro, you have to configure an exchange rate API, as the
|
||||
free API used by default only supports the Euro as base currency.
|
||||
free API used by default only supports the Euro as base currency.
|
||||
|
||||
## Enforce log comments
|
||||
On almost any editing operation it is possible to add a comment describing, what or why you changed something.
|
||||
This comment will be written to change log and can be viewed later.
|
||||
If you want to enforce your users to add comments to certain operations, you can do this by setting the `ENFORCE_CHANGE_COMMENTS_FOR` option.
|
||||
See the configuration reference for more information.
|
||||
|
||||
## Personal stocks and stock locations
|
||||
For makerspaces and universities with a lot of users, where each user can have his own stock, which only he should be able to access, you can assign
|
||||
the user as "owner" of a part lot. This way, only him is allowed to add or remove parts from this lot.
|
||||
@@ -100,11 +100,17 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration
|
||||
$this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES `storelocations` (id)');
|
||||
$this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES `parts` (id) ON DELETE CASCADE');
|
||||
$this->addSql('ALTER TABLE parts DROP INDEX parts_order_orderdetails_id_k, ADD UNIQUE INDEX UNIQ_6940A7FE81081E9B (order_orderdetails_id)');
|
||||
$this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_storelocation_fk');
|
||||
if ($this->doesFKExists('parts', 'parts_id_storelocation_fk')) {
|
||||
$this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_storelocation_fk');
|
||||
}
|
||||
$this->addSql('DROP INDEX favorite ON parts');
|
||||
$this->addSql('DROP INDEX parts_id_storelocation_k ON parts');
|
||||
$this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_footprint_fk');
|
||||
$this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_manufacturer_fk');
|
||||
if ($this->doesFKExists('parts', 'parts_id_footprint_fk')) {
|
||||
$this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_footprint_fk');
|
||||
}
|
||||
if ($this->doesFKExists('parts', 'parts_id_manufacturer_fk')) {
|
||||
$this->addSql('ALTER TABLE parts DROP FOREIGN KEY parts_id_manufacturer_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE parts CHANGE mininstock minamount DOUBLE PRECISION NOT NULL, ADD id_part_unit INT DEFAULT NULL, ADD manufacturer_product_number VARCHAR(255) NOT NULL, ADD manufacturing_status VARCHAR(255) DEFAULT NULL, ADD needs_review TINYINT(1) NOT NULL, ADD tags LONGTEXT NOT NULL, ADD mass DOUBLE PRECISION DEFAULT NULL, DROP instock, CHANGE id_category id_category INT NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE order_quantity order_quantity INT NOT NULL, CHANGE manual_order manual_order TINYINT(1) NOT NULL, CHANGE manufacturer_product_url manufacturer_product_url VARCHAR(255) NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CHANGE favorite favorite TINYINT(1) NOT NULL, DROP id_storelocation');
|
||||
$this->addSql('ALTER TABLE parts ADD CONSTRAINT FK_6940A7FE5697F554 FOREIGN KEY (id_category) REFERENCES `categories` (id)');
|
||||
$this->addSql('ALTER TABLE parts ADD CONSTRAINT FK_6940A7FEEBBCC786 FOREIGN KEY (id_master_picture_attachement) REFERENCES `attachments` (id)');
|
||||
@@ -119,39 +125,53 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration
|
||||
$this->addSql('CREATE INDEX IDX_6940A7FE1ECB93AE ON parts (id_manufacturer)');
|
||||
$this->addSql('ALTER TABLE parts ADD CONSTRAINT parts_id_footprint_fk FOREIGN KEY (id_footprint) REFERENCES footprints (id)');
|
||||
$this->addSql('ALTER TABLE parts ADD CONSTRAINT parts_id_manufacturer_fk FOREIGN KEY (id_manufacturer) REFERENCES manufacturers (id)');
|
||||
$this->addSql('ALTER TABLE attachment_types DROP FOREIGN KEY attachement_types_parent_id_fk');
|
||||
if ($this->doesFKExists('attachment_types', 'attachement_types_parent_id_fk')) {
|
||||
$this->addSql('ALTER TABLE attachment_types DROP FOREIGN KEY attachement_types_parent_id_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE attachment_types ADD filetype_filter LONGTEXT NOT NULL, ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL');
|
||||
$this->addSql('DROP INDEX attachement_types_parent_id_k ON attachment_types');
|
||||
$this->addSql('CREATE INDEX IDX_EFAED719727ACA70 ON attachment_types (parent_id)');
|
||||
$this->addSql('ALTER TABLE attachment_types ADD CONSTRAINT attachement_types_parent_id_fk FOREIGN KEY (parent_id) REFERENCES attachment_types (id)');
|
||||
$this->addSql('ALTER TABLE categories DROP FOREIGN KEY categories_parent_id_fk');
|
||||
if ($this->doesFKExists('categories', 'categories_parent_id_fk')) {
|
||||
$this->addSql('ALTER TABLE categories DROP FOREIGN KEY categories_parent_id_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE categories ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE disable_footprints disable_footprints TINYINT(1) NOT NULL, CHANGE disable_manufacturers disable_manufacturers TINYINT(1) NOT NULL, CHANGE disable_autodatasheets disable_autodatasheets TINYINT(1) NOT NULL, CHANGE disable_properties disable_properties TINYINT(1) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL');
|
||||
$this->addSql('DROP INDEX categories_parent_id_k ON categories');
|
||||
$this->addSql('CREATE INDEX IDX_3AF34668727ACA70 ON categories (parent_id)');
|
||||
$this->addSql('ALTER TABLE categories ADD CONSTRAINT categories_parent_id_fk FOREIGN KEY (parent_id) REFERENCES categories (id)');
|
||||
$this->addSql('ALTER TABLE devices DROP FOREIGN KEY devices_parent_id_fk');
|
||||
if ($this->doesFKExists('devices', 'devices_parent_id_fk')) {
|
||||
$this->addSql('ALTER TABLE devices DROP FOREIGN KEY devices_parent_id_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE devices ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE order_quantity order_quantity INT NOT NULL, CHANGE order_only_missing_parts order_only_missing_parts TINYINT(1) NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CHANGE comment comment LONGTEXT NOT NULL');
|
||||
$this->addSql('DROP INDEX devices_parent_id_k ON devices');
|
||||
$this->addSql('CREATE INDEX IDX_11074E9A727ACA70 ON devices (parent_id)');
|
||||
$this->addSql('ALTER TABLE devices ADD CONSTRAINT devices_parent_id_fk FOREIGN KEY (parent_id) REFERENCES devices (id)');
|
||||
$this->addSql('ALTER TABLE footprints DROP FOREIGN KEY footprints_parent_id_fk');
|
||||
if ($this->doesFKExists('footprints', 'footprints_parent_id_fk')) {
|
||||
$this->addSql('ALTER TABLE footprints DROP FOREIGN KEY footprints_parent_id_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE footprints ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL');
|
||||
$this->addSql('DROP INDEX footprints_parent_id_k ON footprints');
|
||||
$this->addSql('CREATE INDEX IDX_A34D68A2727ACA70 ON footprints (parent_id)');
|
||||
$this->addSql('ALTER TABLE footprints ADD CONSTRAINT footprints_parent_id_fk FOREIGN KEY (parent_id) REFERENCES footprints (id)');
|
||||
$this->addSql('ALTER TABLE manufacturers DROP FOREIGN KEY manufacturers_parent_id_fk');
|
||||
if ($this->doesFKExists('manufacturers', 'manufacturers_parent_id_fk')) {
|
||||
$this->addSql('ALTER TABLE manufacturers DROP FOREIGN KEY manufacturers_parent_id_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE manufacturers ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE address address VARCHAR(255) NOT NULL, CHANGE phone_number phone_number VARCHAR(255) NOT NULL, CHANGE fax_number fax_number VARCHAR(255) NOT NULL, CHANGE email_address email_address VARCHAR(255) NOT NULL, CHANGE website website VARCHAR(255) NOT NULL, CHANGE auto_product_url auto_product_url VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL');
|
||||
$this->addSql('DROP INDEX manufacturers_parent_id_k ON manufacturers');
|
||||
$this->addSql('CREATE INDEX IDX_94565B12727ACA70 ON manufacturers (parent_id)');
|
||||
$this->addSql('ALTER TABLE manufacturers ADD CONSTRAINT manufacturers_parent_id_fk FOREIGN KEY (parent_id) REFERENCES manufacturers (id)');
|
||||
$this->addSql('ALTER TABLE storelocations DROP FOREIGN KEY storelocations_parent_id_fk');
|
||||
if ($this->doesFKExists('storelocations', 'storelocations_parent_id_fk')) {
|
||||
$this->addSql('ALTER TABLE storelocations DROP FOREIGN KEY storelocations_parent_id_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE storelocations ADD storage_type_id INT DEFAULT NULL, ADD only_single_part TINYINT(1) NOT NULL, ADD limit_to_existing_parts TINYINT(1) NOT NULL, ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE is_full is_full TINYINT(1) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL');
|
||||
$this->addSql('ALTER TABLE storelocations ADD CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES `measurement_units` (id)');
|
||||
$this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON storelocations (storage_type_id)');
|
||||
$this->addSql('DROP INDEX storelocations_parent_id_k ON storelocations');
|
||||
$this->addSql('CREATE INDEX IDX_7517020727ACA70 ON storelocations (parent_id)');
|
||||
$this->addSql('ALTER TABLE storelocations ADD CONSTRAINT storelocations_parent_id_fk FOREIGN KEY (parent_id) REFERENCES storelocations (id)');
|
||||
$this->addSql('ALTER TABLE suppliers DROP FOREIGN KEY suppliers_parent_id_fk');
|
||||
if ($this->doesFKExists('suppliers', 'suppliers_parent_id_fk')) {
|
||||
$this->addSql('ALTER TABLE suppliers DROP FOREIGN KEY suppliers_parent_id_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE suppliers ADD default_currency_id INT DEFAULT NULL, ADD shipping_costs NUMERIC(11, 5) DEFAULT NULL, ADD not_selectable TINYINT(1) NOT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE address address VARCHAR(255) NOT NULL, CHANGE phone_number phone_number VARCHAR(255) NOT NULL, CHANGE fax_number fax_number VARCHAR(255) NOT NULL, CHANGE email_address email_address VARCHAR(255) NOT NULL, CHANGE website website VARCHAR(255) NOT NULL, CHANGE auto_product_url auto_product_url VARCHAR(255) NOT NULL, CHANGE comment comment LONGTEXT NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL');
|
||||
$this->addSql('ALTER TABLE suppliers ADD CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)');
|
||||
@@ -159,7 +179,9 @@ final class Version20190902140506 extends AbstractMultiPlatformMigration
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)');
|
||||
$this->addSql('ALTER TABLE suppliers ADD CONSTRAINT suppliers_parent_id_fk FOREIGN KEY (parent_id) REFERENCES suppliers (id)');
|
||||
$this->addSql('DROP INDEX attachements_class_name_k ON attachments');
|
||||
$this->addSql('ALTER TABLE attachments DROP FOREIGN KEY attachements_type_id_fk');
|
||||
if ($this->doesFKExists('attachments', 'attachements_type_id_fk')) {
|
||||
$this->addSql('ALTER TABLE attachments DROP FOREIGN KEY attachements_type_id_fk');
|
||||
}
|
||||
$this->addSql('ALTER TABLE attachments ADD datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CHANGE type_id type_id INT DEFAULT NULL, CHANGE name name VARCHAR(255) NOT NULL, CHANGE filename filename VARCHAR(255) NOT NULL, CHANGE show_in_table show_in_table TINYINT(1) NOT NULL, CHANGE last_modified last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL');
|
||||
$this->addSql('ALTER TABLE attachments ADD CONSTRAINT FK_47C4FAD61F1F2A24 FOREIGN KEY (element_id) REFERENCES `parts` (id) ON DELETE CASCADE');
|
||||
$this->addSql('DROP INDEX attachements_type_id_fk ON attachments');
|
||||
|
||||
@@ -66,8 +66,6 @@ final class Version20190913141126 extends AbstractMultiPlatformMigration
|
||||
';
|
||||
|
||||
$this->addSql($sql);
|
||||
|
||||
$this->printPermissionUpdateMessage();
|
||||
}
|
||||
|
||||
public function mySQLDown(Schema $schema): void
|
||||
|
||||
@@ -34,8 +34,6 @@ final class Version20200311204104 extends AbstractMultiPlatformMigration
|
||||
$sql = 'UPDATE `groups`'.
|
||||
'SET perms_parts_parameters = 681 WHERE (id = 2 AND name = "readonly");';
|
||||
$this->addSql($sql);
|
||||
|
||||
$this->printPermissionUpdateMessage();
|
||||
}
|
||||
|
||||
public function mySQLDown(Schema $schema): void
|
||||
|
||||
@@ -134,8 +134,6 @@ EOD;
|
||||
(2, 1, 'admin', '{$admin_pw}', 1, '', '', '', '', NULL, NULL, NULL, '', '', '2020-06-13 23:39:26', '2020-06-13 23:39:26', 21845, 21845, 21845, 21, 85, 21, 349525, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 21845, 0, NULL, '', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, 0, NULL, '', NULL, 0, 0);
|
||||
EOD;
|
||||
$this->addSql($sql);
|
||||
|
||||
$this->printPermissionUpdateMessage();
|
||||
}
|
||||
|
||||
public function sqLiteDown(Schema $schema): void
|
||||
|
||||
@@ -83,9 +83,9 @@ final class Version20221114193325 extends AbstractMultiPlatformMigration impleme
|
||||
//Reset the permissions of the admin user, to allow admin permissions (like the admins group)
|
||||
$this->addSql("UPDATE `users` SET permissions_data = '$admin' WHERE id = 2;");
|
||||
|
||||
$this->warnIf(true, "\x1b[1;37;43m\n!!! All permissions were reset! Please change them to the desired state, immediately !!!\x1b[0;39;49m");
|
||||
$this->warnIf(true, "\x1b[1;37;43m\n!!! For security reasons all users (except the admin user) were disabled. Login with admin user and reenable other users after checking their permissions !!!\x1b[0;39;49m");
|
||||
$this->warnIf(true, "\x1b[1;37;43m\n!!! For more infos see: https://github.com/Part-DB/Part-DB-symfony/discussions/193 !!!\x1b[0;39;49m");
|
||||
$this->logger->warning('<bg=cyan;fg=black>!!! All permissions were reset! Please change them to the desired state, immediately !!!</>');
|
||||
$this->logger->warning('<bg=cyan;fg=black>!!! For security reasons all users (except the admin user) were disabled. Login with admin user and reenable other users after checking their permissions !!!</>');
|
||||
$this->logger->warning('<bg=cyan;fg=black>!!! For more infos see: https://github.com/Part-DB/Part-DB-symfony/discussions/193 !!!</>');
|
||||
}
|
||||
|
||||
public function mySQLUp(Schema $schema): void
|
||||
|
||||
@@ -281,7 +281,7 @@ final class Version20230219225340 extends AbstractMultiPlatformMigration
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachement, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM projects');
|
||||
$this->addSql('DROP TABLE projects');
|
||||
$this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description DEFAULT \'\', CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description CLOB DEFAULT \'\', CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description) SELECT id, parent_id, id_preview_attachement, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM __temp__projects');
|
||||
$this->addSql('DROP TABLE __temp__projects');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)');
|
||||
|
||||
300
migrations/Version20230402170923.php
Normal file
300
migrations/Version20230402170923.php
Normal file
@@ -0,0 +1,300 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use App\Migration\AbstractMultiPlatformMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20230402170923 extends AbstractMultiPlatformMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Create database schema for user aboutMe and part lot owners';
|
||||
}
|
||||
|
||||
public function mySQLUp(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE part_lots ADD id_owner INT DEFAULT NULL');
|
||||
$this->addSql('ALTER TABLE part_lots ADD CONSTRAINT FK_EBC8F94321E5A74C FOREIGN KEY (id_owner) REFERENCES `users` (id) ON DELETE SET NULL');
|
||||
$this->addSql('CREATE INDEX IDX_EBC8F94321E5A74C ON part_lots (id_owner)');
|
||||
$this->addSql('ALTER TABLE projects ADD CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id)');
|
||||
$this->addSql('ALTER TABLE storelocations ADD id_owner INT DEFAULT NULL, ADD part_owner_must_match TINYINT(1) DEFAULT 0 NOT NULL');
|
||||
$this->addSql('ALTER TABLE storelocations ADD CONSTRAINT FK_751702021E5A74C FOREIGN KEY (id_owner) REFERENCES `users` (id) ON DELETE SET NULL');
|
||||
$this->addSql('CREATE INDEX IDX_751702021E5A74C ON storelocations (id_owner)');
|
||||
$this->addSql('ALTER TABLE `users` ADD about_me LONGTEXT NOT NULL');
|
||||
|
||||
$this->addSql('ALTER TABLE projects CHANGE description description LONGTEXT NOT NULL');
|
||||
}
|
||||
|
||||
public function mySQLDown(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE log CHANGE level level TINYINT(1) NOT NULL');
|
||||
$this->addSql('ALTER TABLE part_lots DROP FOREIGN KEY FK_EBC8F94321E5A74C');
|
||||
$this->addSql('DROP INDEX IDX_EBC8F94321E5A74C ON part_lots');
|
||||
$this->addSql('ALTER TABLE part_lots DROP id_owner');
|
||||
$this->addSql('ALTER TABLE projects DROP FOREIGN KEY FK_5C93B3A4727ACA70');
|
||||
$this->addSql('ALTER TABLE `storelocations` DROP FOREIGN KEY FK_751702021E5A74C');
|
||||
$this->addSql('DROP INDEX IDX_751702021E5A74C ON `storelocations`');
|
||||
$this->addSql('ALTER TABLE `storelocations` DROP id_owner, DROP part_owner_must_match');
|
||||
$this->addSql('ALTER TABLE `users` DROP about_me');
|
||||
}
|
||||
|
||||
public function sqLiteUp(Schema $schema): void
|
||||
{
|
||||
//In Version20230219225340 the type of project description was set to an empty string, which caused problems with doctrine migrations, fix it here
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM projects');
|
||||
$this->addSql('DROP TABLE projects');
|
||||
$this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description CLOB DEFAULT \'\', CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description) SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM __temp__projects');
|
||||
$this->addSql('DROP TABLE __temp__projects');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)');
|
||||
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies');
|
||||
$this->addSql('DROP TABLE currencies');
|
||||
$this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies');
|
||||
$this->addSql('DROP TABLE __temp__currencies');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)');
|
||||
$this->addSql('CREATE INDEX currency_idx_name ON currencies (name)');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM groups');
|
||||
$this->addSql('DROP TABLE groups');
|
||||
$this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json)
|
||||
, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups');
|
||||
$this->addSql('DROP TABLE __temp__groups');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)');
|
||||
$this->addSql('CREATE INDEX group_idx_name ON groups (name)');
|
||||
$this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log');
|
||||
$this->addSql('DROP TABLE log');
|
||||
$this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level TINYINT(4) NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json)
|
||||
, type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log');
|
||||
$this->addSql('DROP TABLE __temp__log');
|
||||
$this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)');
|
||||
$this->addSql('CREATE INDEX log_idx_type ON log (type)');
|
||||
$this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)');
|
||||
$this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__part_lots AS SELECT id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM part_lots');
|
||||
$this->addSql('DROP TABLE part_lots');
|
||||
$this->addSql('CREATE TABLE part_lots (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_store_location INTEGER DEFAULT NULL, id_part INTEGER NOT NULL, id_owner INTEGER DEFAULT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, expiration_date DATETIME DEFAULT NULL, instock_unknown BOOLEAN NOT NULL, amount DOUBLE PRECISION NOT NULL, needs_refill BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES storelocations (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F94321E5A74C FOREIGN KEY (id_owner) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO part_lots (id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added) SELECT id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM __temp__part_lots');
|
||||
$this->addSql('DROP TABLE __temp__part_lots');
|
||||
$this->addSql('CREATE INDEX part_lots_idx_needs_refill ON part_lots (needs_refill)');
|
||||
$this->addSql('CREATE INDEX part_lots_idx_instock_un_expiration_id_part ON part_lots (instock_unknown, expiration_date, id_part)');
|
||||
$this->addSql('CREATE INDEX IDX_EBC8F9435D8F4B37 ON part_lots (id_store_location)');
|
||||
$this->addSql('CREATE INDEX IDX_EBC8F943C22F6CC4 ON part_lots (id_part)');
|
||||
$this->addSql('CREATE INDEX IDX_EBC8F94321E5A74C ON part_lots (id_owner)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails');
|
||||
$this->addSql('DROP TABLE pricedetails');
|
||||
$this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal)
|
||||
, price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails');
|
||||
$this->addSql('DROP TABLE __temp__pricedetails');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)');
|
||||
$this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)');
|
||||
$this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries');
|
||||
$this->addSql('DROP TABLE project_bom_entries');
|
||||
$this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries');
|
||||
$this->addSql('DROP TABLE __temp__project_bom_entries');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM projects');
|
||||
$this->addSql('DROP TABLE projects');
|
||||
$this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description CLOB DEFAULT \'\' NOT NULL, CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description) SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM __temp__projects');
|
||||
$this->addSql('DROP TABLE __temp__projects');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__storelocations AS SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM storelocations');
|
||||
$this->addSql('DROP TABLE storelocations');
|
||||
$this->addSql('CREATE TABLE storelocations (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, storage_type_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, id_owner INTEGER DEFAULT NULL, is_full BOOLEAN NOT NULL, only_single_part BOOLEAN NOT NULL, limit_to_existing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, part_owner_must_match BOOLEAN DEFAULT 0 NOT NULL, CONSTRAINT FK_7517020727ACA70 FOREIGN KEY (parent_id) REFERENCES storelocations (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES measurement_units (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_751702021E5A74C FOREIGN KEY (id_owner) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO storelocations (id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM __temp__storelocations');
|
||||
$this->addSql('DROP TABLE __temp__storelocations');
|
||||
$this->addSql('CREATE INDEX IDX_7517020EA7100A1 ON storelocations (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON storelocations (storage_type_id)');
|
||||
$this->addSql('CREATE INDEX IDX_7517020727ACA70 ON storelocations (parent_id)');
|
||||
$this->addSql('CREATE INDEX location_idx_name ON storelocations (name)');
|
||||
$this->addSql('CREATE INDEX location_idx_parent_name ON storelocations (parent_id, name)');
|
||||
$this->addSql('CREATE INDEX IDX_751702021E5A74C ON storelocations (id_owner)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM suppliers');
|
||||
$this->addSql('DROP TABLE suppliers');
|
||||
$this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers');
|
||||
$this->addSql('DROP TABLE __temp__suppliers');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user FROM users');
|
||||
$this->addSql('DROP TABLE users');
|
||||
$this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json)
|
||||
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json)
|
||||
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json)
|
||||
, saml_user BOOLEAN NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user FROM __temp__users');
|
||||
$this->addSql('DROP TABLE __temp__users');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)');
|
||||
$this->addSql('CREATE INDEX user_idx_username ON users (name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys');
|
||||
$this->addSql('DROP TABLE webauthn_keys');
|
||||
$this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64)
|
||||
, type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array)
|
||||
, attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path)
|
||||
, aaguid CLOB NOT NULL --(DC2Type:aaguid)
|
||||
, credential_public_key CLOB NOT NULL --(DC2Type:base64)
|
||||
, user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys');
|
||||
$this->addSql('DROP TABLE __temp__webauthn_keys');
|
||||
$this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)');
|
||||
}
|
||||
|
||||
public function sqLiteDown(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies');
|
||||
$this->addSql('DROP TABLE currencies');
|
||||
$this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies');
|
||||
$this->addSql('DROP TABLE __temp__currencies');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX currency_idx_name ON currencies (name)');
|
||||
$this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM "groups"');
|
||||
$this->addSql('DROP TABLE "groups"');
|
||||
$this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --
|
||||
(DC2Type:json)
|
||||
, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups');
|
||||
$this->addSql('DROP TABLE __temp__groups');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX group_idx_name ON "groups" (name)');
|
||||
$this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM log');
|
||||
$this->addSql('DROP TABLE log');
|
||||
$this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level BOOLEAN NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO log (id, id_user, username, datetime, level, target_id, target_type, extra, type) SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM __temp__log');
|
||||
$this->addSql('DROP TABLE __temp__log');
|
||||
$this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)');
|
||||
$this->addSql('CREATE INDEX log_idx_type ON log (type)');
|
||||
$this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)');
|
||||
$this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__part_lots AS SELECT id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM part_lots');
|
||||
$this->addSql('DROP TABLE part_lots');
|
||||
$this->addSql('CREATE TABLE part_lots (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_store_location INTEGER DEFAULT NULL, id_part INTEGER NOT NULL, description CLOB NOT NULL, comment CLOB NOT NULL, expiration_date DATETIME DEFAULT NULL, instock_unknown BOOLEAN NOT NULL, amount DOUBLE PRECISION NOT NULL, needs_refill BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_EBC8F9435D8F4B37 FOREIGN KEY (id_store_location) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_EBC8F943C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO part_lots (id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added) SELECT id, id_store_location, id_part, description, comment, expiration_date, instock_unknown, amount, needs_refill, last_modified, datetime_added FROM __temp__part_lots');
|
||||
$this->addSql('DROP TABLE __temp__part_lots');
|
||||
$this->addSql('CREATE INDEX IDX_EBC8F9435D8F4B37 ON part_lots (id_store_location)');
|
||||
$this->addSql('CREATE INDEX IDX_EBC8F943C22F6CC4 ON part_lots (id_part)');
|
||||
$this->addSql('CREATE INDEX part_lots_idx_instock_un_expiration_id_part ON part_lots (instock_unknown, expiration_date, id_part)');
|
||||
$this->addSql('CREATE INDEX part_lots_idx_needs_refill ON part_lots (needs_refill)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM "pricedetails"');
|
||||
$this->addSql('DROP TABLE "pricedetails"');
|
||||
$this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "pricedetails" (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails');
|
||||
$this->addSql('DROP TABLE __temp__pricedetails');
|
||||
$this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)');
|
||||
$this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries');
|
||||
$this->addSql('DROP TABLE project_bom_entries');
|
||||
$this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries');
|
||||
$this->addSql('DROP TABLE __temp__project_bom_entries');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM projects');
|
||||
$this->addSql('DROP TABLE projects');
|
||||
$this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts BOOLEAN NOT NULL, description CLOB DEFAULT \'\', comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM __temp__projects');
|
||||
$this->addSql('DROP TABLE __temp__projects');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__storelocations AS SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM "storelocations"');
|
||||
$this->addSql('DROP TABLE "storelocations"');
|
||||
$this->addSql('CREATE TABLE "storelocations" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, storage_type_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, is_full BOOLEAN NOT NULL, only_single_part BOOLEAN NOT NULL, limit_to_existing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_7517020727ACA70 FOREIGN KEY (parent_id) REFERENCES "storelocations" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020B270BFF1 FOREIGN KEY (storage_type_id) REFERENCES "measurement_units" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_7517020EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "storelocations" (id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, storage_type_id, id_preview_attachment, is_full, only_single_part, limit_to_existing_parts, comment, not_selectable, name, last_modified, datetime_added FROM __temp__storelocations');
|
||||
$this->addSql('DROP TABLE __temp__storelocations');
|
||||
$this->addSql('CREATE INDEX IDX_7517020727ACA70 ON "storelocations" (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_7517020B270BFF1 ON "storelocations" (storage_type_id)');
|
||||
$this->addSql('CREATE INDEX IDX_7517020EA7100A1 ON "storelocations" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX location_idx_name ON "storelocations" (name)');
|
||||
$this->addSql('CREATE INDEX location_idx_parent_name ON "storelocations" (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "suppliers"');
|
||||
$this->addSql('DROP TABLE "suppliers"');
|
||||
$this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "suppliers" (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers');
|
||||
$this->addSql('DROP TABLE __temp__suppliers');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"');
|
||||
$this->addSql('DROP TABLE "users"');
|
||||
$this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN DEFAULT 0 NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --
|
||||
(DC2Type:json)
|
||||
, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users');
|
||||
$this->addSql('DROP TABLE __temp__users');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX user_idx_username ON "users" (name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys');
|
||||
$this->addSql('DROP TABLE webauthn_keys');
|
||||
$this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --
|
||||
(DC2Type:base64)
|
||||
, type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --
|
||||
(DC2Type:array)
|
||||
, attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --
|
||||
(DC2Type:trust_path)
|
||||
, aaguid CLOB NOT NULL --
|
||||
(DC2Type:aaguid)
|
||||
, credential_public_key CLOB NOT NULL --
|
||||
(DC2Type:base64)
|
||||
, user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys');
|
||||
$this->addSql('DROP TABLE __temp__webauthn_keys');
|
||||
$this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)');
|
||||
}
|
||||
}
|
||||
52
migrations/Version20230408170059.php
Normal file
52
migrations/Version20230408170059.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use App\Migration\AbstractMultiPlatformMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20230408170059 extends AbstractMultiPlatformMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Add show_email_on_profile option';
|
||||
}
|
||||
|
||||
public function mySQLUp(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE `users` ADD show_email_on_profile TINYINT(1) DEFAULT 0 NOT NULL');
|
||||
}
|
||||
|
||||
public function mySQLDown(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE `users` DROP show_email_on_profile');
|
||||
}
|
||||
|
||||
public function sqLiteUp(Schema $schema): void
|
||||
{
|
||||
$this->addSql('ALTER TABLE users ADD COLUMN show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL');
|
||||
}
|
||||
|
||||
public function sqLiteDown(Schema $schema): void
|
||||
{
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"');
|
||||
$this->addSql('DROP TABLE "users"');
|
||||
$this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json)
|
||||
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json)
|
||||
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json)
|
||||
, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users');
|
||||
$this->addSql('DROP TABLE __temp__users');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX user_idx_username ON "users" (name)');
|
||||
}
|
||||
}
|
||||
437
migrations/Version20230408213957.php
Normal file
437
migrations/Version20230408213957.php
Normal file
@@ -0,0 +1,437 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use App\Migration\AbstractMultiPlatformMigration;
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20230408213957 extends AbstractMultiPlatformMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Fixed some minor issues in the database schema and indifference between new and legacy-migrated databases';
|
||||
}
|
||||
|
||||
public function mySQLUp(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE `groups` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\'');
|
||||
$this->addSql('ALTER TABLE `log` CHANGE level level TINYINT(4) NOT NULL');
|
||||
if ($this->doesFKExists('projects', 'FK_11074E9A727ACA70')){
|
||||
$this->addSql('ALTER TABLE projects DROP FOREIGN KEY FK_11074E9A727ACA70');
|
||||
}
|
||||
$this->addSql('ALTER TABLE projects CHANGE description description LONGTEXT NOT NULL');
|
||||
$this->addSql('ALTER TABLE `users` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', CHANGE saml_user saml_user TINYINT(1) NOT NULL, CHANGE about_me about_me LONGTEXT NOT NULL');
|
||||
$this->addSql('ALTER TABLE `log` CHANGE level level TINYINT NOT NULL COMMENT \'(DC2Type:tinyint)\'');
|
||||
}
|
||||
|
||||
public function mySQLDown(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('ALTER TABLE `groups` CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\'');
|
||||
$this->addSql('ALTER TABLE log CHANGE level level TINYINT(1) NOT NULL');
|
||||
$this->addSql('ALTER TABLE projects CHANGE description description LONGTEXT NOT NULL');
|
||||
$this->addSql('ALTER TABLE `users` CHANGE about_me about_me LONGTEXT NOT NULL, CHANGE saml_user saml_user TINYINT(1) DEFAULT 0 NOT NULL, CHANGE permissions_data permissions_data LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\'');
|
||||
$this->addSql('ALTER TABLE log CHANGE level level TINYINT(1) NOT NULL');
|
||||
}
|
||||
|
||||
public function sqLiteUp(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies');
|
||||
$this->addSql('DROP TABLE currencies');
|
||||
$this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies');
|
||||
$this->addSql('DROP TABLE __temp__currencies');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)');
|
||||
$this->addSql('CREATE INDEX currency_idx_name ON currencies (name)');
|
||||
$this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM groups');
|
||||
$this->addSql('DROP TABLE groups');
|
||||
$this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json)
|
||||
, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups');
|
||||
$this->addSql('DROP TABLE __temp__groups');
|
||||
$this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)');
|
||||
$this->addSql('CREATE INDEX group_idx_name ON groups (name)');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log');
|
||||
$this->addSql('DROP TABLE log');
|
||||
$this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level TINYINT NOT NULL --(DC2Type:tinyint)
|
||||
, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json)
|
||||
, type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log');
|
||||
$this->addSql('DROP TABLE __temp__log');
|
||||
$this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)');
|
||||
$this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)');
|
||||
$this->addSql('CREATE INDEX log_idx_type ON log (type)');
|
||||
$this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails');
|
||||
$this->addSql('DROP TABLE pricedetails');
|
||||
$this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal)
|
||||
, price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails');
|
||||
$this->addSql('DROP TABLE __temp__pricedetails');
|
||||
$this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)');
|
||||
$this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries');
|
||||
$this->addSql('DROP TABLE project_bom_entries');
|
||||
$this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries');
|
||||
$this->addSql('DROP TABLE __temp__project_bom_entries');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM suppliers');
|
||||
$this->addSql('DROP TABLE suppliers');
|
||||
$this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers');
|
||||
$this->addSql('DROP TABLE __temp__suppliers');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM users');
|
||||
$this->addSql('DROP TABLE users');
|
||||
$this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json)
|
||||
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json)
|
||||
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --(DC2Type:json)
|
||||
, saml_user BOOLEAN NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM __temp__users');
|
||||
$this->addSql('DROP TABLE __temp__users');
|
||||
$this->addSql('CREATE INDEX user_idx_username ON users (name)');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys');
|
||||
$this->addSql('DROP TABLE webauthn_keys');
|
||||
$this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64)
|
||||
, type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array)
|
||||
, attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path)
|
||||
, aaguid CLOB NOT NULL --(DC2Type:aaguid)
|
||||
, credential_public_key CLOB NOT NULL --(DC2Type:base64)
|
||||
, user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys');
|
||||
$this->addSql('DROP TABLE __temp__webauthn_keys');
|
||||
$this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)');
|
||||
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies');
|
||||
$this->addSql('DROP TABLE currencies');
|
||||
$this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies');
|
||||
$this->addSql('DROP TABLE __temp__currencies');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)');
|
||||
$this->addSql('CREATE INDEX currency_idx_name ON currencies (name)');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM groups');
|
||||
$this->addSql('DROP TABLE groups');
|
||||
$this->addSql('CREATE TABLE groups (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL --(DC2Type:json)
|
||||
, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO groups (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups');
|
||||
$this->addSql('DROP TABLE __temp__groups');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON groups (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON groups (parent_id)');
|
||||
$this->addSql('CREATE INDEX group_idx_name ON groups (name)');
|
||||
$this->addSql('CREATE INDEX group_idx_parent_name ON groups (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM log');
|
||||
$this->addSql('DROP TABLE log');
|
||||
$this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, datetime DATETIME NOT NULL, level TINYINT NOT NULL --(DC2Type:tinyint)
|
||||
, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --(DC2Type:json)
|
||||
, type SMALLINT NOT NULL, username VARCHAR(255) NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO log (id, id_user, datetime, level, target_id, target_type, extra, type, username) SELECT id, id_user, datetime, level, target_id, target_type, extra, type, username FROM __temp__log');
|
||||
$this->addSql('DROP TABLE __temp__log');
|
||||
$this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)');
|
||||
$this->addSql('CREATE INDEX log_idx_type ON log (type)');
|
||||
$this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)');
|
||||
$this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM pricedetails');
|
||||
$this->addSql('DROP TABLE pricedetails');
|
||||
$this->addSql('CREATE TABLE pricedetails (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --(DC2Type:big_decimal)
|
||||
, price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES orderdetails (id) ON UPDATE NO ACTION ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO pricedetails (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails');
|
||||
$this->addSql('DROP TABLE __temp__pricedetails');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON pricedetails (min_discount_quantity, price_related_quantity)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount ON pricedetails (min_discount_quantity)');
|
||||
$this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON pricedetails (id_currency)');
|
||||
$this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON pricedetails (orderdetails_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries');
|
||||
$this->addSql('DROP TABLE project_bom_entries');
|
||||
$this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AFC547992F180363 FOREIGN KEY (id_device) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AFC54799C22F6CC4 FOREIGN KEY (id_part) REFERENCES parts (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries');
|
||||
$this->addSql('DROP TABLE __temp__project_bom_entries');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM projects');
|
||||
$this->addSql('DROP TABLE projects');
|
||||
$this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, order_only_missing_parts BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, status VARCHAR(64) DEFAULT NULL, description CLOB NOT NULL, CONSTRAINT FK_11074E9A727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description) SELECT id, parent_id, id_preview_attachment, order_quantity, order_only_missing_parts, comment, not_selectable, name, last_modified, datetime_added, status, description FROM __temp__projects');
|
||||
$this->addSql('DROP TABLE __temp__projects');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM suppliers');
|
||||
$this->addSql('DROP TABLE suppliers');
|
||||
$this->addSql('CREATE TABLE suppliers (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --(DC2Type:big_decimal)
|
||||
, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES suppliers (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO suppliers (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers');
|
||||
$this->addSql('DROP TABLE __temp__suppliers');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON suppliers (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_parent_name ON suppliers (parent_id, name)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_name ON suppliers (name)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON suppliers (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON suppliers (default_currency_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM users');
|
||||
$this->addSql('DROP TABLE users');
|
||||
$this->addSql('CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --(DC2Type:json)
|
||||
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --(DC2Type:json)
|
||||
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB NOT NULL --(DC2Type:json)
|
||||
, saml_user BOOLEAN NOT NULL, about_me CLOB NOT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES attachments (id) ON UPDATE NO ACTION ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO users (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, last_modified, datetime_added, permissions_data, saml_user, about_me, show_email_on_profile FROM __temp__users');
|
||||
$this->addSql('DROP TABLE __temp__users');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON users (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON users (group_id)');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON users (name)');
|
||||
$this->addSql('CREATE INDEX user_idx_username ON users (name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys');
|
||||
$this->addSql('DROP TABLE webauthn_keys');
|
||||
$this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --(DC2Type:base64)
|
||||
, type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --(DC2Type:array)
|
||||
, attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --(DC2Type:trust_path)
|
||||
, aaguid CLOB NOT NULL --(DC2Type:aaguid)
|
||||
, credential_public_key CLOB NOT NULL --(DC2Type:base64)
|
||||
, user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES users (id) ON UPDATE NO ACTION ON DELETE NO ACTION NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys');
|
||||
$this->addSql('DROP TABLE __temp__webauthn_keys');
|
||||
$this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)');
|
||||
}
|
||||
|
||||
public function sqLiteDown(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies');
|
||||
$this->addSql('DROP TABLE currencies');
|
||||
$this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies');
|
||||
$this->addSql('DROP TABLE __temp__currencies');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX currency_idx_name ON currencies (name)');
|
||||
$this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM "groups"');
|
||||
$this->addSql('DROP TABLE "groups"');
|
||||
$this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --
|
||||
(DC2Type:json)
|
||||
, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups');
|
||||
$this->addSql('DROP TABLE __temp__groups');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX group_idx_name ON "groups" (name)');
|
||||
$this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM log');
|
||||
$this->addSql('DROP TABLE log');
|
||||
$this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level BOOLEAN NOT NULL, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO log (id, id_user, username, datetime, level, target_id, target_type, extra, type) SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM __temp__log');
|
||||
$this->addSql('DROP TABLE __temp__log');
|
||||
$this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)');
|
||||
$this->addSql('CREATE INDEX log_idx_type ON log (type)');
|
||||
$this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)');
|
||||
$this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM "pricedetails"');
|
||||
$this->addSql('DROP TABLE "pricedetails"');
|
||||
$this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "pricedetails" (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails');
|
||||
$this->addSql('DROP TABLE __temp__pricedetails');
|
||||
$this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)');
|
||||
$this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries');
|
||||
$this->addSql('DROP TABLE project_bom_entries');
|
||||
$this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries');
|
||||
$this->addSql('DROP TABLE __temp__project_bom_entries');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "suppliers"');
|
||||
$this->addSql('DROP TABLE "suppliers"');
|
||||
$this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "suppliers" (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers');
|
||||
$this->addSql('DROP TABLE __temp__suppliers');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"');
|
||||
$this->addSql('DROP TABLE "users"');
|
||||
$this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --
|
||||
(DC2Type:json)
|
||||
, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users');
|
||||
$this->addSql('DROP TABLE __temp__users');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX user_idx_username ON "users" (name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys');
|
||||
$this->addSql('DROP TABLE webauthn_keys');
|
||||
$this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --
|
||||
(DC2Type:base64)
|
||||
, type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --
|
||||
(DC2Type:array)
|
||||
, attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --
|
||||
(DC2Type:trust_path)
|
||||
, aaguid CLOB NOT NULL --
|
||||
(DC2Type:aaguid)
|
||||
, credential_public_key CLOB NOT NULL --
|
||||
(DC2Type:base64)
|
||||
, user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys');
|
||||
$this->addSql('DROP TABLE __temp__webauthn_keys');
|
||||
$this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)');
|
||||
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__currencies AS SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM currencies');
|
||||
$this->addSql('DROP TABLE currencies');
|
||||
$this->addSql('CREATE TABLE currencies (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, exchange_rate NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, iso_code VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_37C44693727ACA70 FOREIGN KEY (parent_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_37C44693EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO currencies (id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, exchange_rate, iso_code, comment, not_selectable, name, last_modified, datetime_added FROM __temp__currencies');
|
||||
$this->addSql('DROP TABLE __temp__currencies');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693727ACA70 ON currencies (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_37C44693EA7100A1 ON currencies (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX currency_idx_name ON currencies (name)');
|
||||
$this->addSql('CREATE INDEX currency_idx_parent_name ON currencies (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__groups AS SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM "groups"');
|
||||
$this->addSql('DROP TABLE "groups"');
|
||||
$this->addSql('CREATE TABLE "groups" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, enforce_2fa BOOLEAN NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --
|
||||
(DC2Type:json)
|
||||
, CONSTRAINT FK_F06D3970727ACA70 FOREIGN KEY (parent_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_F06D3970EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "groups" (id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data) SELECT id, parent_id, id_preview_attachment, enforce_2fa, comment, not_selectable, name, last_modified, datetime_added, permissions_data FROM __temp__groups');
|
||||
$this->addSql('DROP TABLE __temp__groups');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970727ACA70 ON "groups" (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_F06D3970EA7100A1 ON "groups" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX group_idx_name ON "groups" (name)');
|
||||
$this->addSql('CREATE INDEX group_idx_parent_name ON "groups" (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__log AS SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM log');
|
||||
$this->addSql('DROP TABLE log');
|
||||
$this->addSql('CREATE TABLE log (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_user INTEGER DEFAULT NULL, username VARCHAR(255) NOT NULL, datetime DATETIME NOT NULL, level TINYINT NOT NULL --
|
||||
(DC2Type:tinyint)
|
||||
, target_id INTEGER NOT NULL, target_type SMALLINT NOT NULL, extra CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, type SMALLINT NOT NULL, CONSTRAINT FK_8F3F68C56B3CA4B FOREIGN KEY (id_user) REFERENCES "users" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO log (id, id_user, username, datetime, level, target_id, target_type, extra, type) SELECT id, id_user, username, datetime, level, target_id, target_type, extra, type FROM __temp__log');
|
||||
$this->addSql('DROP TABLE __temp__log');
|
||||
$this->addSql('CREATE INDEX IDX_8F3F68C56B3CA4B ON log (id_user)');
|
||||
$this->addSql('CREATE INDEX log_idx_type ON log (type)');
|
||||
$this->addSql('CREATE INDEX log_idx_type_target ON log (type, target_type, target_id)');
|
||||
$this->addSql('CREATE INDEX log_idx_datetime ON log (datetime)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__pricedetails AS SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM "pricedetails"');
|
||||
$this->addSql('DROP TABLE "pricedetails"');
|
||||
$this->addSql('CREATE TABLE "pricedetails" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_currency INTEGER DEFAULT NULL, orderdetails_id INTEGER NOT NULL, price NUMERIC(11, 5) NOT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, price_related_quantity DOUBLE PRECISION NOT NULL, min_discount_quantity DOUBLE PRECISION NOT NULL, manual_input BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_C68C4459398D64AA FOREIGN KEY (id_currency) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_C68C44594A01DDC7 FOREIGN KEY (orderdetails_id) REFERENCES "orderdetails" (id) ON DELETE CASCADE NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "pricedetails" (id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added) SELECT id, id_currency, orderdetails_id, price, price_related_quantity, min_discount_quantity, manual_input, last_modified, datetime_added FROM __temp__pricedetails');
|
||||
$this->addSql('DROP TABLE __temp__pricedetails');
|
||||
$this->addSql('CREATE INDEX IDX_C68C4459398D64AA ON "pricedetails" (id_currency)');
|
||||
$this->addSql('CREATE INDEX IDX_C68C44594A01DDC7 ON "pricedetails" (orderdetails_id)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount ON "pricedetails" (min_discount_quantity)');
|
||||
$this->addSql('CREATE INDEX pricedetails_idx_min_discount_price_qty ON "pricedetails" (min_discount_quantity, price_related_quantity)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__project_bom_entries AS SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM project_bom_entries');
|
||||
$this->addSql('DROP TABLE project_bom_entries');
|
||||
$this->addSql('CREATE TABLE project_bom_entries (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, id_device INTEGER DEFAULT NULL, id_part INTEGER DEFAULT NULL, price_currency_id INTEGER DEFAULT NULL, quantity DOUBLE PRECISION NOT NULL, mountnames CLOB NOT NULL, name VARCHAR(255) DEFAULT NULL, comment CLOB NOT NULL, price NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_1AA2DD312F180363 FOREIGN KEY (id_device) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD31C22F6CC4 FOREIGN KEY (id_part) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1AA2DD313FFDCD60 FOREIGN KEY (price_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO project_bom_entries (id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added) SELECT id, id_device, id_part, price_currency_id, quantity, mountnames, name, comment, price, last_modified, datetime_added FROM __temp__project_bom_entries');
|
||||
$this->addSql('DROP TABLE __temp__project_bom_entries');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD312F180363 ON project_bom_entries (id_device)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD31C22F6CC4 ON project_bom_entries (id_part)');
|
||||
$this->addSql('CREATE INDEX IDX_1AA2DD313FFDCD60 ON project_bom_entries (price_currency_id)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__projects AS SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM projects');
|
||||
$this->addSql('DROP TABLE projects');
|
||||
$this->addSql('CREATE TABLE projects (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, order_quantity INTEGER NOT NULL, status VARCHAR(64) DEFAULT NULL, order_only_missing_parts BOOLEAN NOT NULL, description CLOB DEFAULT \'\' NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_5C93B3A4727ACA70 FOREIGN KEY (parent_id) REFERENCES projects (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_5C93B3A4EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO projects (id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, id_preview_attachment, order_quantity, status, order_only_missing_parts, description, comment, not_selectable, name, last_modified, datetime_added FROM __temp__projects');
|
||||
$this->addSql('DROP TABLE __temp__projects');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4727ACA70 ON projects (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_5C93B3A4EA7100A1 ON projects (id_preview_attachment)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__suppliers AS SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM "suppliers"');
|
||||
$this->addSql('DROP TABLE "suppliers"');
|
||||
$this->addSql('CREATE TABLE "suppliers" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, parent_id INTEGER DEFAULT NULL, default_currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, shipping_costs NUMERIC(11, 5) DEFAULT NULL --
|
||||
(DC2Type:big_decimal)
|
||||
, address VARCHAR(255) NOT NULL, phone_number VARCHAR(255) NOT NULL, fax_number VARCHAR(255) NOT NULL, email_address VARCHAR(255) NOT NULL, website VARCHAR(255) NOT NULL, auto_product_url VARCHAR(255) NOT NULL, comment CLOB NOT NULL, not_selectable BOOLEAN NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_AC28B95C727ACA70 FOREIGN KEY (parent_id) REFERENCES "suppliers" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CECD792C0 FOREIGN KEY (default_currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_AC28B95CEA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "suppliers" (id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added) SELECT id, parent_id, default_currency_id, id_preview_attachment, shipping_costs, address, phone_number, fax_number, email_address, website, auto_product_url, comment, not_selectable, name, last_modified, datetime_added FROM __temp__suppliers');
|
||||
$this->addSql('DROP TABLE __temp__suppliers');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95C727ACA70 ON "suppliers" (parent_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CECD792C0 ON "suppliers" (default_currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_AC28B95CEA7100A1 ON "suppliers" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_name ON "suppliers" (name)');
|
||||
$this->addSql('CREATE INDEX supplier_idx_parent_name ON "suppliers" (parent_id, name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__users AS SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM "users"');
|
||||
$this->addSql('DROP TABLE "users"');
|
||||
$this->addSql('CREATE TABLE "users" (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, group_id INTEGER DEFAULT NULL, currency_id INTEGER DEFAULT NULL, id_preview_attachment INTEGER DEFAULT NULL, disabled BOOLEAN NOT NULL, config_theme VARCHAR(255) DEFAULT NULL, pw_reset_token VARCHAR(255) DEFAULT NULL, config_instock_comment_a CLOB NOT NULL, config_instock_comment_w CLOB NOT NULL, about_me CLOB DEFAULT \'\' NOT NULL, trusted_device_cookie_version INTEGER NOT NULL, backup_codes CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, google_authenticator_secret VARCHAR(255) DEFAULT NULL, config_timezone VARCHAR(255) DEFAULT NULL, config_language VARCHAR(255) DEFAULT NULL, email VARCHAR(255) DEFAULT NULL, show_email_on_profile BOOLEAN DEFAULT 0 NOT NULL, department VARCHAR(255) DEFAULT NULL, last_name VARCHAR(255) DEFAULT NULL, first_name VARCHAR(255) DEFAULT NULL, need_pw_change BOOLEAN NOT NULL, password VARCHAR(255) DEFAULT NULL, name VARCHAR(180) NOT NULL, settings CLOB NOT NULL --
|
||||
(DC2Type:json)
|
||||
, backup_codes_generation_date DATETIME DEFAULT NULL, pw_reset_expires DATETIME DEFAULT NULL, saml_user BOOLEAN NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, permissions_data CLOB DEFAULT \'[]\' NOT NULL --
|
||||
(DC2Type:json)
|
||||
, CONSTRAINT FK_1483A5E9FE54D947 FOREIGN KEY (group_id) REFERENCES "groups" (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_1483A5E9EA7100A1 FOREIGN KEY (id_preview_attachment) REFERENCES "attachments" (id) ON DELETE SET NULL NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO "users" (id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data) SELECT id, group_id, currency_id, id_preview_attachment, disabled, config_theme, pw_reset_token, config_instock_comment_a, config_instock_comment_w, about_me, trusted_device_cookie_version, backup_codes, google_authenticator_secret, config_timezone, config_language, email, show_email_on_profile, department, last_name, first_name, need_pw_change, password, name, settings, backup_codes_generation_date, pw_reset_expires, saml_user, last_modified, datetime_added, permissions_data FROM __temp__users');
|
||||
$this->addSql('DROP TABLE __temp__users');
|
||||
$this->addSql('CREATE UNIQUE INDEX UNIQ_1483A5E95E237E06 ON "users" (name)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9FE54D947 ON "users" (group_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON "users" (currency_id)');
|
||||
$this->addSql('CREATE INDEX IDX_1483A5E9EA7100A1 ON "users" (id_preview_attachment)');
|
||||
$this->addSql('CREATE INDEX user_idx_username ON "users" (name)');
|
||||
$this->addSql('CREATE TEMPORARY TABLE __temp__webauthn_keys AS SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM webauthn_keys');
|
||||
$this->addSql('DROP TABLE webauthn_keys');
|
||||
$this->addSql('CREATE TABLE webauthn_keys (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, user_id INTEGER DEFAULT NULL, public_key_credential_id CLOB NOT NULL --
|
||||
(DC2Type:base64)
|
||||
, type VARCHAR(255) NOT NULL, transports CLOB NOT NULL --
|
||||
(DC2Type:array)
|
||||
, attestation_type VARCHAR(255) NOT NULL, trust_path CLOB NOT NULL --
|
||||
(DC2Type:trust_path)
|
||||
, aaguid CLOB NOT NULL --
|
||||
(DC2Type:aaguid)
|
||||
, credential_public_key CLOB NOT NULL --
|
||||
(DC2Type:base64)
|
||||
, user_handle VARCHAR(255) NOT NULL, counter INTEGER NOT NULL, name VARCHAR(255) NOT NULL, last_modified DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, datetime_added DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL, CONSTRAINT FK_799FD143A76ED395 FOREIGN KEY (user_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
|
||||
$this->addSql('INSERT INTO webauthn_keys (id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added) SELECT id, user_id, public_key_credential_id, type, transports, attestation_type, trust_path, aaguid, credential_public_key, user_handle, counter, name, last_modified, datetime_added FROM __temp__webauthn_keys');
|
||||
$this->addSql('DROP TABLE __temp__webauthn_keys');
|
||||
$this->addSql('CREATE INDEX IDX_799FD143A76ED395 ON webauthn_keys (user_id)');
|
||||
}
|
||||
}
|
||||
@@ -147,11 +147,21 @@ class ShowEventLogCommand extends Command
|
||||
$target_class = $this->elementTypeNameGenerator->getLocalizedTypeLabel($entry->getTargetClass());
|
||||
}
|
||||
|
||||
if ($entry->getUser()) {
|
||||
$user = $entry->getUser()->getFullName(true);
|
||||
} else {
|
||||
if ($entry->isCLIEntry()) {
|
||||
$user = $entry->getCLIUsername() . ' [CLI]';
|
||||
} else {
|
||||
$user = $entry->getUsername() . ' [deleted]';
|
||||
}
|
||||
}
|
||||
|
||||
$row = [
|
||||
$entry->getID(),
|
||||
$entry->getTimestamp()->format('Y-m-d H:i:s'),
|
||||
$entry->getType(),
|
||||
$entry->getUser()->getFullName(true),
|
||||
$user,
|
||||
$target_class,
|
||||
$target_name,
|
||||
];
|
||||
|
||||
158
src/Command/Migrations/ImportPartKeeprCommand.php
Normal file
158
src/Command/Migrations/ImportPartKeeprCommand.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Command\Migrations;
|
||||
|
||||
use App\Services\ImportExportSystem\PartKeeprImporter\PKDatastructureImporter;
|
||||
use App\Services\ImportExportSystem\PartKeeprImporter\MySQLDumpXMLConverter;
|
||||
use App\Services\ImportExportSystem\PartKeeprImporter\PKImportHelper;
|
||||
use App\Services\ImportExportSystem\PartKeeprImporter\PKPartImporter;
|
||||
use App\Services\ImportExportSystem\PartKeeprImporter\PKOptionalImporter;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
class ImportPartKeeprCommand extends Command
|
||||
{
|
||||
|
||||
protected static $defaultName = 'partdb:migrations:import-partkeepr';
|
||||
|
||||
protected EntityManagerInterface $em;
|
||||
protected MySQLDumpXMLConverter $xml_converter;
|
||||
protected PKDatastructureImporter $datastructureImporter;
|
||||
protected PKImportHelper $importHelper;
|
||||
protected PKPartImporter $partImporter;
|
||||
protected PKOptionalImporter $optionalImporter;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, MySQLDumpXMLConverter $xml_converter,
|
||||
PKDatastructureImporter $datastructureImporter, PKPartImporter $partImporter, PKImportHelper $importHelper,
|
||||
PKOptionalImporter $optionalImporter)
|
||||
{
|
||||
parent::__construct(self::$defaultName);
|
||||
$this->em = $em;
|
||||
$this->datastructureImporter = $datastructureImporter;
|
||||
$this->importHelper = $importHelper;
|
||||
$this->partImporter = $partImporter;
|
||||
$this->xml_converter = $xml_converter;
|
||||
$this->optionalImporter = $optionalImporter;
|
||||
}
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this->setDescription('Import a PartKeepr database XML dump into Part-DB');
|
||||
$this->setHelp('This command allows you to import a PartKeepr database exported by mysqldump as XML file into Part-DB');
|
||||
|
||||
$this->addArgument('file', InputArgument::REQUIRED, 'The file to which should be imported.');
|
||||
|
||||
$this->addOption('--no-projects', null, InputOption::VALUE_NONE, 'Do not import projects.');
|
||||
$this->addOption('--import-users', null, InputOption::VALUE_NONE, 'Import users (passwords will not be imported).');
|
||||
}
|
||||
|
||||
public function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$input_path = $input->getArgument('file');
|
||||
$no_projects_import = $input->getOption('no-projects');
|
||||
$import_users = $input->getOption('import-users');
|
||||
|
||||
$io->note('This command is still in development. If you encounter any problems, please report them to the issue tracker on GitHub.');
|
||||
$io->warning('This command will delete all existing data in the database (except users). Make sure that you have no important data in the database before you continue!');
|
||||
|
||||
$io->ask('Please type "DELETE ALL DATA" to continue.', '', function ($answer) {
|
||||
if (strtoupper($answer) !== 'DELETE ALL DATA') {
|
||||
throw new \RuntimeException('You did not type "DELETE ALL DATA"!');
|
||||
}
|
||||
return $answer;
|
||||
});
|
||||
|
||||
//Make more checks here
|
||||
//$io->confirm('This will delete all data in the database. Do you want to continue?', false);
|
||||
|
||||
//Purge the databse, so we will not have any conflicts
|
||||
$this->importHelper->purgeDatabaseForImport();
|
||||
|
||||
//Convert the XML file to an array
|
||||
$xml = file_get_contents($input_path);
|
||||
$data = $this->xml_converter->convertMySQLDumpXMLDataToArrayStructure($xml);
|
||||
|
||||
if (!$this->importHelper->checkVersion($data)) {
|
||||
$db_version = $this->importHelper->getDatabaseSchemaVersion($data);
|
||||
$io->error('The version of the imported database is not supported! (Version: '.$db_version.')');
|
||||
return 1;
|
||||
}
|
||||
|
||||
//Import the mandatory data
|
||||
$this->doImport($io, $data);
|
||||
|
||||
if (!$no_projects_import) {
|
||||
$io->info('Importing projects...');
|
||||
$count = $this->optionalImporter->importProjects($data);
|
||||
$io->success('Imported '.$count.' projects.');
|
||||
}
|
||||
|
||||
if ($import_users) {
|
||||
$io->info('Importing users...');
|
||||
$count = $this->optionalImporter->importUsers($data);
|
||||
$io->success('Imported '.$count.' users.');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function doImport(SymfonyStyle $io, array $data): void
|
||||
{
|
||||
//First import the distributors
|
||||
$io->info('Importing distributors...');
|
||||
$count = $this->datastructureImporter->importDistributors($data);
|
||||
$io->success('Imported '.$count.' distributors.');
|
||||
|
||||
//Import the measurement units
|
||||
$io->info('Importing part measurement units...');
|
||||
$count = $this->datastructureImporter->importPartUnits($data);
|
||||
$io->success('Imported '.$count.' measurement units.');
|
||||
|
||||
//Import manufacturers
|
||||
$io->info('Importing manufacturers...');
|
||||
$count = $this->datastructureImporter->importManufacturers($data);
|
||||
$io->success('Imported '.$count.' manufacturers.');
|
||||
|
||||
$io->info('Importing categories...');
|
||||
$count = $this->datastructureImporter->importCategories($data);
|
||||
$io->success('Imported '.$count.' categories.');
|
||||
|
||||
$io->info('Importing Footprints...');
|
||||
$count = $this->datastructureImporter->importFootprints($data);
|
||||
$io->success('Imported '.$count.' footprints.');
|
||||
|
||||
$io->info('Importing storage locations...');
|
||||
$count = $this->datastructureImporter->importStorelocations($data);
|
||||
$io->success('Imported '.$count.' storage locations.');
|
||||
|
||||
$io->info('Importing parts...');
|
||||
$count = $this->partImporter->importParts($data);
|
||||
$io->success('Imported '.$count.' parts.');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -39,7 +39,7 @@ use App\Repository\AbstractPartsContainingRepository;
|
||||
use App\Services\Attachments\AttachmentSubmitHandler;
|
||||
use App\Services\ImportExportSystem\EntityExporter;
|
||||
use App\Services\ImportExportSystem\EntityImporter;
|
||||
use App\Services\LabelSystem\Barcodes\BarcodeExampleElementsGenerator;
|
||||
use App\Services\LabelSystem\LabelExampleElementsGenerator;
|
||||
use App\Services\LabelSystem\LabelGenerator;
|
||||
use App\Services\LogSystem\EventCommentHelper;
|
||||
use App\Services\LogSystem\HistoryHelper;
|
||||
@@ -86,14 +86,14 @@ abstract class BaseAdminController extends AbstractController
|
||||
*/
|
||||
protected $eventDispatcher;
|
||||
protected LabelGenerator $labelGenerator;
|
||||
protected BarcodeExampleElementsGenerator $barcodeExampleGenerator;
|
||||
protected LabelExampleElementsGenerator $barcodeExampleGenerator;
|
||||
|
||||
protected EntityManagerInterface $entityManager;
|
||||
|
||||
public function __construct(TranslatorInterface $translator, UserPasswordHasherInterface $passwordEncoder,
|
||||
AttachmentSubmitHandler $attachmentSubmitHandler,
|
||||
EventCommentHelper $commentHelper, HistoryHelper $historyHelper, TimeTravel $timeTravel,
|
||||
DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, BarcodeExampleElementsGenerator $barcodeExampleGenerator,
|
||||
DataTableFactory $dataTableFactory, EventDispatcherInterface $eventDispatcher, LabelExampleElementsGenerator $barcodeExampleGenerator,
|
||||
LabelGenerator $labelGenerator, EntityManagerInterface $entityManager)
|
||||
{
|
||||
if ('' === $this->entity_class || '' === $this->form_class || '' === $this->twig_template || '' === $this->route_base) {
|
||||
|
||||
@@ -31,7 +31,7 @@ use App\Services\Attachments\AttachmentSubmitHandler;
|
||||
use App\Services\ImportExportSystem\EntityExporter;
|
||||
use App\Services\ImportExportSystem\EntityImporter;
|
||||
use App\Services\Tools\ExchangeRateUpdater;
|
||||
use App\Services\LabelSystem\Barcodes\BarcodeExampleElementsGenerator;
|
||||
use App\Services\LabelSystem\LabelExampleElementsGenerator;
|
||||
use App\Services\LabelSystem\LabelGenerator;
|
||||
use App\Services\LogSystem\EventCommentHelper;
|
||||
use App\Services\LogSystem\HistoryHelper;
|
||||
@@ -76,7 +76,7 @@ class CurrencyController extends BaseAdminController
|
||||
TimeTravel $timeTravel,
|
||||
DataTableFactory $dataTableFactory,
|
||||
EventDispatcherInterface $eventDispatcher,
|
||||
BarcodeExampleElementsGenerator $barcodeExampleGenerator,
|
||||
LabelExampleElementsGenerator $barcodeExampleGenerator,
|
||||
LabelGenerator $labelGenerator,
|
||||
EntityManagerInterface $entityManager,
|
||||
ExchangeRateUpdater $exchangeRateUpdater
|
||||
|
||||
@@ -55,8 +55,11 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||
|
||||
use function Symfony\Component\Translation\t;
|
||||
|
||||
/**
|
||||
* @Route("/part")
|
||||
*/
|
||||
@@ -360,23 +363,28 @@ class PartController extends AbstractController
|
||||
$action = $request->request->get('action');
|
||||
|
||||
|
||||
|
||||
switch ($action) {
|
||||
case "withdraw":
|
||||
case "remove":
|
||||
$this->denyAccessUnlessGranted('withdraw', $partLot);
|
||||
$withdrawAddHelper->withdraw($partLot, $amount, $comment);
|
||||
break;
|
||||
case "add":
|
||||
$this->denyAccessUnlessGranted('add', $partLot);
|
||||
$withdrawAddHelper->add($partLot, $amount, $comment);
|
||||
break;
|
||||
case "move":
|
||||
$this->denyAccessUnlessGranted('move', $partLot);
|
||||
$withdrawAddHelper->move($partLot, $targetLot, $amount, $comment);
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException("Unknown action!");
|
||||
try {
|
||||
switch ($action) {
|
||||
case "withdraw":
|
||||
case "remove":
|
||||
$this->denyAccessUnlessGranted('withdraw', $partLot);
|
||||
$withdrawAddHelper->withdraw($partLot, $amount, $comment);
|
||||
break;
|
||||
case "add":
|
||||
$this->denyAccessUnlessGranted('add', $partLot);
|
||||
$withdrawAddHelper->add($partLot, $amount, $comment);
|
||||
break;
|
||||
case "move":
|
||||
$this->denyAccessUnlessGranted('move', $partLot);
|
||||
$this->denyAccessUnlessGranted('move', $targetLot);
|
||||
$withdrawAddHelper->move($partLot, $targetLot, $amount, $comment);
|
||||
break;
|
||||
default:
|
||||
throw new \RuntimeException("Unknown action!");
|
||||
}
|
||||
} catch (AccessDeniedException $exception) {
|
||||
$this->addFlash('error', t('part.withdraw.access_denied'));
|
||||
goto err;
|
||||
}
|
||||
|
||||
//Save the changes to the DB
|
||||
@@ -387,6 +395,7 @@ class PartController extends AbstractController
|
||||
$this->addFlash('error', 'CSRF Token invalid!');
|
||||
}
|
||||
|
||||
err:
|
||||
//If an redirect was passed, then redirect there
|
||||
if($request->request->get('_redirect')) {
|
||||
return $this->redirect($request->request->get('_redirect'));
|
||||
|
||||
@@ -202,21 +202,24 @@ class UserController extends AdminPages\BaseAdminController
|
||||
$user = $tmp;
|
||||
} else {
|
||||
//Else we must check, if the current user is allowed to access $user
|
||||
$this->denyAccessUnlessGranted('read', $user);
|
||||
$this->denyAccessUnlessGranted('info', $user);
|
||||
}
|
||||
|
||||
$table = $this->dataTableFactory->createFromType(
|
||||
LogDataTable::class,
|
||||
[
|
||||
'filter_elements' => $user,
|
||||
'mode' => 'element_history',
|
||||
],
|
||||
['pageLength' => 10]
|
||||
)
|
||||
->handleRequest($request);
|
||||
//Only show the history table, if the user is the current user
|
||||
if ($user === $this->getUser()) {
|
||||
$table = $this->dataTableFactory->createFromType(
|
||||
LogDataTable::class,
|
||||
[
|
||||
'filter_elements' => $user,
|
||||
'mode' => 'element_history',
|
||||
],
|
||||
['pageLength' => 10]
|
||||
)
|
||||
->handleRequest($request);
|
||||
|
||||
if ($table->isCallback()) {
|
||||
return $table->getResponse();
|
||||
if ($table->isCallback()) {
|
||||
return $table->getResponse();
|
||||
}
|
||||
}
|
||||
|
||||
//Show permissions to user
|
||||
@@ -230,7 +233,7 @@ class UserController extends AdminPages\BaseAdminController
|
||||
return $this->renderForm('users/user_info.html.twig', [
|
||||
'user' => $user,
|
||||
'form' => $builder->getForm(),
|
||||
'datatable' => $table,
|
||||
'datatable' => $table ?? null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\DataTables\Filters\Constraints\Part;
|
||||
|
||||
use App\DataTables\Filters\Constraints\BooleanConstraint;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
|
||||
class LessThanDesiredConstraint extends BooleanConstraint
|
||||
{
|
||||
public function __construct(string $property = null, string $identifier = null, ?bool $default_value = null)
|
||||
{
|
||||
parent::__construct($property ?? 'amountSum', $identifier, $default_value);
|
||||
}
|
||||
|
||||
public function apply(QueryBuilder $queryBuilder): void
|
||||
{
|
||||
//Do not apply a filter if value is null (filter is set to ignore)
|
||||
if(!$this->isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//If value is true, we want to filter for parts with stock < desired stock
|
||||
if ($this->value) {
|
||||
$queryBuilder->andHaving('amountSum < part.minamount');
|
||||
} else {
|
||||
$queryBuilder->andHaving('amountSum >= part.minamount');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ use App\DataTables\Filters\Constraints\DateTimeConstraint;
|
||||
use App\DataTables\Filters\Constraints\EntityConstraint;
|
||||
use App\DataTables\Filters\Constraints\IntConstraint;
|
||||
use App\DataTables\Filters\Constraints\NumberConstraint;
|
||||
use App\DataTables\Filters\Constraints\Part\LessThanDesiredConstraint;
|
||||
use App\DataTables\Filters\Constraints\Part\ParameterConstraint;
|
||||
use App\DataTables\Filters\Constraints\Part\TagsConstraint;
|
||||
use App\DataTables\Filters\Constraints\TextConstraint;
|
||||
@@ -36,6 +37,8 @@ use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\MeasurementUnit;
|
||||
use App\Entity\Parts\Storelocation;
|
||||
use App\Entity\Parts\Supplier;
|
||||
use App\Entity\UserSystem\User;
|
||||
use App\Form\Filters\Constraints\UserEntityConstraintType;
|
||||
use App\Services\Trees\NodesListBuilder;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\QueryBuilder;
|
||||
@@ -68,10 +71,14 @@ class PartFilter implements FilterInterface
|
||||
protected EntityConstraint $storelocation;
|
||||
protected IntConstraint $lotCount;
|
||||
protected IntConstraint $amountSum;
|
||||
protected LessThanDesiredConstraint $lessThanDesired;
|
||||
|
||||
protected BooleanConstraint $lotNeedsRefill;
|
||||
protected TextConstraint $lotDescription;
|
||||
protected BooleanConstraint $lotUnknownAmount;
|
||||
protected DateTimeConstraint $lotExpirationDate;
|
||||
protected EntityConstraint $lotOwner;
|
||||
|
||||
protected EntityConstraint $measurementUnit;
|
||||
protected TextConstraint $manufacturer_product_url;
|
||||
protected TextConstraint $manufacturer_product_number;
|
||||
@@ -108,12 +115,14 @@ class PartFilter implements FilterInterface
|
||||
//We have to use Having here, as we use an alias column which is not supported on the where clause and would result in an error
|
||||
$this->amountSum = (new IntConstraint('amountSum'))->useHaving();
|
||||
$this->lotCount = new IntConstraint('COUNT(partLots)');
|
||||
$this->lessThanDesired = new LessThanDesiredConstraint();
|
||||
|
||||
$this->storelocation = new EntityConstraint($nodesListBuilder, Storelocation::class, 'partLots.storage_location');
|
||||
$this->lotNeedsRefill = new BooleanConstraint('partLots.needs_refill');
|
||||
$this->lotUnknownAmount = new BooleanConstraint('partLots.instock_unknown');
|
||||
$this->lotExpirationDate = new DateTimeConstraint('partLots.expiration_date');
|
||||
$this->lotDescription = new TextConstraint('partLots.description');
|
||||
$this->lotOwner = new EntityConstraint($nodesListBuilder, User::class, 'partLots.owner');
|
||||
|
||||
$this->manufacturer = new EntityConstraint($nodesListBuilder, Manufacturer::class, 'part.manufacturer');
|
||||
$this->manufacturer_product_number = new TextConstraint('part.manufacturer_product_number');
|
||||
@@ -280,6 +289,14 @@ class PartFilter implements FilterInterface
|
||||
return $this->lotCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EntityConstraint
|
||||
*/
|
||||
public function getLotOwner(): EntityConstraint
|
||||
{
|
||||
return $this->lotOwner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TagsConstraint
|
||||
*/
|
||||
@@ -383,7 +400,13 @@ class PartFilter implements FilterInterface
|
||||
return $this->obsolete;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return LessThanDesiredConstraint
|
||||
*/
|
||||
public function getLessThanDesired(): LessThanDesiredConstraint
|
||||
{
|
||||
return $this->lessThanDesired;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -226,6 +226,14 @@ class LogDataTable implements DataTableTypeInterface
|
||||
|
||||
//If user was deleted, show the info from the username field
|
||||
if ($user === null) {
|
||||
if ($context->isCLIEntry()) {
|
||||
return sprintf('%s [%s]',
|
||||
htmlentities($context->getCLIUsername()),
|
||||
$this->translator->trans('log.cli_user')
|
||||
);
|
||||
}
|
||||
|
||||
//Else we just deal with a deleted user
|
||||
return sprintf(
|
||||
'@%s [%s]',
|
||||
htmlentities($context->getUsername()),
|
||||
|
||||
@@ -202,6 +202,12 @@ final class PartsDataTable implements DataTableTypeInterface
|
||||
htmlspecialchars($this->amountFormatter->format($expiredAmount, $context->getPartUnit())));
|
||||
}
|
||||
|
||||
//When the amount is below the minimum amount, we highlight the number red
|
||||
if ($context->isNotEnoughInstock()) {
|
||||
$ret = sprintf('<b class="text-danger" title="%s">%s</b>',
|
||||
$this->translator->trans('part.info.amount.less_than_desired'),
|
||||
$ret);
|
||||
}
|
||||
|
||||
return $ret;
|
||||
},
|
||||
|
||||
@@ -179,7 +179,7 @@ class ResetAutoIncrementORMPurger implements PurgerInterface, ORMPurgerInterface
|
||||
}
|
||||
|
||||
// If the table is excluded, skip it as well
|
||||
if (array_search($tbl, $this->excluded) !== false) {
|
||||
if (in_array($tbl, $this->excluded, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
47
src/Doctrine/Types/TinyIntType.php
Normal file
47
src/Doctrine/Types/TinyIntType.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Doctrine\Types;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
|
||||
/**
|
||||
* A type to use for tinyint columns in MySQL
|
||||
*/
|
||||
class TinyIntType extends Type
|
||||
{
|
||||
|
||||
public function getSQLDeclaration(array $column, AbstractPlatform $platform)
|
||||
{
|
||||
return 'TINYINT';
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'tinyint';
|
||||
}
|
||||
|
||||
public function requiresSQLCommentHint(AbstractPlatform $platform)
|
||||
{
|
||||
//We use the comment, so that doctrine migrations can properly detect, that nothing has changed and no migration is needed.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -340,11 +340,11 @@ abstract class AbstractStructuralDBElement extends AttachmentContainingDBElement
|
||||
/**
|
||||
* Set the comment.
|
||||
*
|
||||
* @param string|null $new_comment the new comment
|
||||
* @param string $new_comment the new comment
|
||||
*
|
||||
* @return AbstractStructuralDBElement
|
||||
*/
|
||||
public function setComment(?string $new_comment): self
|
||||
public function setComment(string $new_comment): self
|
||||
{
|
||||
$this->comment = $new_comment;
|
||||
|
||||
|
||||
@@ -33,14 +33,14 @@ trait TimestampTrait
|
||||
{
|
||||
/**
|
||||
* @var DateTime|null the date when this element was modified the last time
|
||||
* @ORM\Column(type="datetime", name="last_modified", options={"default"="CURRENT_TIMESTAMP"})
|
||||
* @ORM\Column(type="datetime", name="last_modified", options={"default":"CURRENT_TIMESTAMP"})
|
||||
* @Groups({"extended", "full"})
|
||||
*/
|
||||
protected ?DateTime $lastModified = null;
|
||||
|
||||
/**
|
||||
* @var DateTime|null the date when this element was created
|
||||
* @ORM\Column(type="datetime", name="datetime_added", options={"default"="CURRENT_TIMESTAMP"})
|
||||
* @ORM\Column(type="datetime", name="datetime_added", options={"default":"CURRENT_TIMESTAMP"})
|
||||
* @Groups({"extended", "full"})
|
||||
*/
|
||||
protected ?DateTime $addedDate = null;
|
||||
|
||||
@@ -160,7 +160,7 @@ abstract class AbstractLogEntry extends AbstractDBElement
|
||||
protected ?DateTime $timestamp = null;
|
||||
|
||||
/** @var int The priority level of the associated level. 0 is highest, 7 lowest
|
||||
* @ORM\Column(type="integer", name="level", columnDefinition="TINYINT(4) NOT NULL")
|
||||
* @ORM\Column(type="tinyint", name="level", nullable=false)
|
||||
*/
|
||||
protected int $level;
|
||||
|
||||
@@ -216,6 +216,41 @@ abstract class AbstractLogEntry extends AbstractDBElement
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this log entry was created by a CLI command, false otherwise.
|
||||
* @return bool
|
||||
*/
|
||||
public function isCLIEntry(): bool
|
||||
{
|
||||
return strpos($this->username, '!!!CLI ') === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this log entry as a CLI entry, and set the username of the CLI user.
|
||||
* This removes the association to a user object in database, as CLI users are not really related to logged in
|
||||
* Part-DB users.
|
||||
* @param string $cli_username
|
||||
* @return $this
|
||||
*/
|
||||
public function setCLIUsername(string $cli_username): self
|
||||
{
|
||||
$this->user = null;
|
||||
$this->username = '!!!CLI ' . $cli_username;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the username of the CLI user that caused the event.
|
||||
* @return string|null The username of the CLI user, or null if this log entry was not created by a CLI command.
|
||||
*/
|
||||
public function getCLIUsername(): ?string
|
||||
{
|
||||
if ($this->isCLIEntry()) {
|
||||
return substr($this->username, 7);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retuns the username of the user that caused the event (useful if the user was deleted).
|
||||
*
|
||||
|
||||
@@ -54,7 +54,7 @@ class ElementCreatedLogEntry extends AbstractLogEntry implements LogWithCommentI
|
||||
*/
|
||||
public function getCreationInstockValue(): ?string
|
||||
{
|
||||
return $this->extra['i'] ?? null;
|
||||
return isset($this->extra['i']) ? (string)$this->extra['i'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -78,7 +78,7 @@ class Part extends AttachmentContainingDBElement
|
||||
protected $parameters;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="datetime", name="datetime_added", options={"default"="CURRENT_TIMESTAMP"})
|
||||
* @ORM\Column(type="datetime", name="datetime_added", options={"default":"CURRENT_TIMESTAMP"})
|
||||
*/
|
||||
protected ?DateTime $addedDate = null;
|
||||
|
||||
@@ -104,7 +104,7 @@ class Part extends AttachmentContainingDBElement
|
||||
|
||||
/**
|
||||
* @var DateTime the date when this element was modified the last time
|
||||
* @ORM\Column(type="datetime", name="last_modified", options={"default"="CURRENT_TIMESTAMP"})
|
||||
* @ORM\Column(type="datetime", name="last_modified", options={"default":"CURRENT_TIMESTAMP"})
|
||||
*/
|
||||
protected ?DateTime $lastModified = null;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ use App\Entity\Base\AbstractDBElement;
|
||||
use App\Entity\Base\TimestampTrait;
|
||||
use App\Entity\Contracts\NamedElementInterface;
|
||||
use App\Entity\Contracts\TimeStampableInterface;
|
||||
use App\Entity\UserSystem\User;
|
||||
use App\Validator\Constraints\Selectable;
|
||||
use App\Validator\Constraints\ValidPartLot;
|
||||
use DateTime;
|
||||
@@ -33,6 +34,7 @@ use Doctrine\ORM\Mapping as ORM;
|
||||
use Exception;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
use Symfony\Component\Validator\Constraints as Assert;
|
||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||
|
||||
/**
|
||||
* This entity describes a lot where parts can be stored.
|
||||
@@ -111,6 +113,13 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
|
||||
*/
|
||||
protected Part $part;
|
||||
|
||||
/**
|
||||
* @var User|null The owner of this part lot
|
||||
* @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User")
|
||||
* @ORM\JoinColumn(name="id_owner", referencedColumnName="id", nullable=true, onDelete="SET NULL")
|
||||
*/
|
||||
protected ?User $owner = null;
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->id) {
|
||||
@@ -304,8 +313,53 @@ class PartLot extends AbstractDBElement implements TimeStampableInterface, Named
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner of this part lot.
|
||||
* @return User|null
|
||||
*/
|
||||
public function getOwner(): ?User
|
||||
{
|
||||
return $this->owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the owner of this part lot.
|
||||
* @param User|null $owner
|
||||
* @return PartLot
|
||||
*/
|
||||
public function setOwner(?User $owner): PartLot
|
||||
{
|
||||
$this->owner = $owner;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Assert\Callback
|
||||
*/
|
||||
public function validate(ExecutionContextInterface $context, $payload)
|
||||
{
|
||||
//Ensure that the owner is not the anonymous user
|
||||
if ($this->getOwner() && $this->getOwner()->isAnonymousUser()) {
|
||||
$context->buildViolation('validator.part_lot.owner_must_not_be_anonymous')
|
||||
->atPath('owner')
|
||||
->addViolation();
|
||||
}
|
||||
|
||||
//When the storage location sets the owner must match, the part lot owner must match the storage location owner
|
||||
if ($this->getStorageLocation() && $this->getStorageLocation()->isPartOwnerMustMatch()
|
||||
&& $this->getStorageLocation()->getOwner() && $this->getOwner()) {
|
||||
if ($this->getOwner() !== $this->getStorageLocation()->getOwner()
|
||||
&& $this->owner->getID() !== $this->getStorageLocation()->getOwner()->getID()) {
|
||||
$context->buildViolation('validator.part_lot.owner_must_match_storage_location_owner')
|
||||
->setParameter('%owner_name%', $this->getStorageLocation()->getOwner()->getFullName(true))
|
||||
->atPath('owner')
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ trait InstockTrait
|
||||
* @ORM\OneToMany(targetEntity="PartLot", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
* @Assert\Valid()
|
||||
* @ORM\OrderBy({"amount" = "DESC"})
|
||||
* @Groups({"extended", "full"})
|
||||
* @Groups({"extended", "full", "import"})
|
||||
*/
|
||||
protected $partLots;
|
||||
|
||||
@@ -151,6 +151,15 @@ trait InstockTrait
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if the total instock amount of this part is less than the minimum amount.
|
||||
* @return bool
|
||||
*/
|
||||
public function isNotEnoughInstock(): bool
|
||||
{
|
||||
return $this->getAmountSum() < $this->getMinAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the summed amount of this part (over all part lots)
|
||||
* Part Lots that have unknown value or are expired, are not used for this value.
|
||||
|
||||
@@ -40,7 +40,7 @@ trait OrderTrait
|
||||
* @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
* @Assert\Valid()
|
||||
* @ORM\OrderBy({"supplierpartnr" = "ASC"})
|
||||
* @Groups({"extended", "full"})
|
||||
* @Groups({"extended", "full", "import"})
|
||||
*/
|
||||
protected $orderdetails;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace App\Entity\Parts;
|
||||
use App\Entity\Attachments\StorelocationAttachment;
|
||||
use App\Entity\Base\AbstractPartsContainingDBElement;
|
||||
use App\Entity\Parameters\StorelocationParameter;
|
||||
use App\Entity\UserSystem\User;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Component\Serializer\Annotation\Groups;
|
||||
@@ -89,6 +90,20 @@ class Storelocation extends AbstractPartsContainingDBElement
|
||||
*/
|
||||
protected bool $limit_to_existing_parts = false;
|
||||
|
||||
/**
|
||||
* @var User|null The owner of this storage location
|
||||
* @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User")
|
||||
* @ORM\JoinColumn(name="id_owner", referencedColumnName="id", nullable=true, onDelete="SET NULL")
|
||||
* @Assert\Expression("this.getOwner() == null or this.getOwner().isAnonymousUser() === false", message="validator.part_lot.owner_must_not_be_anonymous")
|
||||
*/
|
||||
protected ?User $owner = null;
|
||||
|
||||
/**
|
||||
* @var bool If this is set to true, only parts lots, which are owned by the same user as the store location are allowed to be stored here.
|
||||
* @ORM\Column(type="boolean", options={"default":false})
|
||||
*/
|
||||
protected bool $part_owner_must_match = false;
|
||||
|
||||
/**
|
||||
* @var Collection<int, StorelocationAttachment>
|
||||
* @ORM\OneToMany(targetEntity="App\Entity\Attachments\StorelocationAttachment", mappedBy="element", cascade={"persist", "remove"}, orphanRemoval=true)
|
||||
@@ -166,6 +181,49 @@ class Storelocation extends AbstractPartsContainingDBElement
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the owner of this storage location
|
||||
* @return User|null
|
||||
*/
|
||||
public function getOwner(): ?User
|
||||
{
|
||||
return $this->owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the owner of this storage location
|
||||
* @param User|null $owner
|
||||
* @return Storelocation
|
||||
*/
|
||||
public function setOwner(?User $owner): Storelocation
|
||||
{
|
||||
$this->owner = $owner;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is set to true, only parts lots, which are owned by the same user as the store location are allowed to be stored here.
|
||||
* @return bool
|
||||
*/
|
||||
public function isPartOwnerMustMatch(): bool
|
||||
{
|
||||
return $this->part_owner_must_match;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is set to true, only parts lots, which are owned by the same user as the store location are allowed to be stored here.
|
||||
* @param bool $part_owner_must_match
|
||||
* @return Storelocation
|
||||
*/
|
||||
public function setPartOwnerMustMatch(bool $part_owner_must_match): Storelocation
|
||||
{
|
||||
$this->part_owner_must_match = $part_owner_must_match;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/********************************************************************************
|
||||
*
|
||||
* Setters
|
||||
|
||||
@@ -344,7 +344,7 @@ class Orderdetail extends AbstractDBElement implements TimeStampableInterface, N
|
||||
public function setSupplierProductUrl(string $new_url): self
|
||||
{
|
||||
//Only change the internal URL if it is not the auto generated one
|
||||
if ($new_url === $this->supplier->getAutoProductUrl($this->getSupplierPartNr())) {
|
||||
if ($this->supplier && $new_url === $this->supplier->getAutoProductUrl($this->getSupplierPartNr())) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ class Project extends AbstractStructuralDBElement
|
||||
protected bool $order_only_missing_parts = false;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="text", nullable=false, columnDefinition="DEFAULT ''")
|
||||
* @ORM\Column(type="text", nullable=false)
|
||||
* @Groups({"simple", "extended", "full"})
|
||||
*/
|
||||
protected string $description = '';
|
||||
|
||||
@@ -58,7 +58,7 @@ class ProjectBOMEntry extends AbstractDBElement
|
||||
* @var string A comma separated list of the names, where this parts should be placed
|
||||
* @ORM\Column(type="text", name="mountnames")
|
||||
*/
|
||||
protected string $mountnames;
|
||||
protected string $mountnames = '';
|
||||
|
||||
/**
|
||||
* @var string An optional name describing this BOM entry (useful for non-part entries)
|
||||
|
||||
@@ -48,7 +48,7 @@ final class PermissionData implements \JsonSerializable
|
||||
* permission => [
|
||||
* operation => value,
|
||||
* ]
|
||||
* @ORM\Column(type="json", name="data", options={"default": "[]"})
|
||||
* @ORM\Column(type="json", name="data")
|
||||
*/
|
||||
protected ?array $data = [
|
||||
//$ prefixed entries are used for metadata
|
||||
|
||||
@@ -103,6 +103,13 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
||||
*/
|
||||
protected string $instock_comment_w = '';
|
||||
|
||||
/**
|
||||
* @var string A self-description of the user
|
||||
* @ORM\Column(type="text")
|
||||
* @Groups({"full", "import"})
|
||||
*/
|
||||
protected string $aboutMe = '';
|
||||
|
||||
/** @var int The version of the trusted device cookie. Used to invalidate all trusted device cookies at once.
|
||||
* @ORM\Column(type="integer")
|
||||
*/
|
||||
@@ -161,6 +168,12 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
||||
*/
|
||||
protected ?string $email = '';
|
||||
|
||||
/**
|
||||
* @var bool True if the user wants to show his email address on his (public) profile
|
||||
* @ORM\Column(type="boolean", options={"default": false})
|
||||
*/
|
||||
protected bool $show_email_on_profile = false;
|
||||
|
||||
/**
|
||||
* @var string|null The department the user is working
|
||||
* @ORM\Column(type="string", length=255, nullable=true)
|
||||
@@ -254,7 +267,7 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
||||
|
||||
/**
|
||||
* @var DateTime the time until the password reset token is valid
|
||||
* @ORM\Column(type="datetime", nullable=true, columnDefinition="DEFAULT NULL")
|
||||
* @ORM\Column(type="datetime", nullable=true, options={"default": null})
|
||||
*/
|
||||
protected $pw_reset_expires;
|
||||
|
||||
@@ -625,6 +638,50 @@ class User extends AttachmentContainingDBElement implements UserInterface, HasPe
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the email address of the user is shown on the public profile page.
|
||||
* @return bool
|
||||
*/
|
||||
public function isShowEmailOnProfile(): bool
|
||||
{
|
||||
return $this->show_email_on_profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the email address of the user is shown on the public profile page.
|
||||
* @param bool $show_email_on_profile
|
||||
* @return User
|
||||
*/
|
||||
public function setShowEmailOnProfile(bool $show_email_on_profile): User
|
||||
{
|
||||
$this->show_email_on_profile = $show_email_on_profile;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the about me text of the user.
|
||||
* @return string
|
||||
*/
|
||||
public function getAboutMe(): string
|
||||
{
|
||||
return $this->aboutMe;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the about me text of the user.
|
||||
* @param string $aboutMe
|
||||
* @return User
|
||||
*/
|
||||
public function setAboutMe(string $aboutMe): User
|
||||
{
|
||||
$this->aboutMe = $aboutMe;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Gets the language the user prefers (as 2 letter ISO code).
|
||||
*
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace App\Form\AdminPages;
|
||||
|
||||
use App\Entity\Base\AbstractNamedDBElement;
|
||||
use App\Services\Attachments\FileTypeFilterTools;
|
||||
use App\Services\LogSystem\EventCommentNeededHelper;
|
||||
use Symfony\Component\Form\CallbackTransformer;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -33,10 +34,10 @@ class AttachmentTypeAdminForm extends BaseEntityAdminForm
|
||||
{
|
||||
protected FileTypeFilterTools $filterTools;
|
||||
|
||||
public function __construct(Security $security, FileTypeFilterTools $filterTools)
|
||||
public function __construct(Security $security, FileTypeFilterTools $filterTools, EventCommentNeededHelper $eventCommentNeededHelper)
|
||||
{
|
||||
$this->filterTools = $filterTools;
|
||||
parent::__construct($security);
|
||||
parent::__construct($security, $eventCommentNeededHelper);
|
||||
}
|
||||
|
||||
protected function additionalFormElements(FormBuilderInterface $builder, array $options, AbstractNamedDBElement $entity): void
|
||||
|
||||
@@ -31,6 +31,7 @@ use App\Form\ParameterType;
|
||||
use App\Form\Type\MasterPictureAttachmentType;
|
||||
use App\Form\Type\RichTextEditorType;
|
||||
use App\Form\Type\StructuralEntityType;
|
||||
use App\Services\LogSystem\EventCommentNeededHelper;
|
||||
use FOS\CKEditorBundle\Form\Type\CKEditorType;
|
||||
use function get_class;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
@@ -46,10 +47,12 @@ use Symfony\Component\Security\Core\Security;
|
||||
class BaseEntityAdminForm extends AbstractType
|
||||
{
|
||||
protected Security $security;
|
||||
protected EventCommentNeededHelper $eventCommentNeededHelper;
|
||||
|
||||
public function __construct(Security $security)
|
||||
public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper)
|
||||
{
|
||||
$this->security = $security;
|
||||
$this->eventCommentNeededHelper = $eventCommentNeededHelper;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
@@ -141,7 +144,7 @@ class BaseEntityAdminForm extends AbstractType
|
||||
$builder->add('log_comment', TextType::class, [
|
||||
'label' => 'edit.log_comment',
|
||||
'mapped' => false,
|
||||
'required' => false,
|
||||
'required' => $this->eventCommentNeededHelper->isCommentNeeded($is_new ? 'datastructure_create': 'datastructure_edit'),
|
||||
'empty_data' => null,
|
||||
]);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace App\Form\AdminPages;
|
||||
|
||||
use App\Entity\Base\AbstractNamedDBElement;
|
||||
use App\Form\Type\BigDecimalMoneyType;
|
||||
use App\Services\LogSystem\EventCommentNeededHelper;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CurrencyType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -33,9 +34,9 @@ class CurrencyAdminForm extends BaseEntityAdminForm
|
||||
{
|
||||
private string $default_currency;
|
||||
|
||||
public function __construct(Security $security, string $default_currency)
|
||||
public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, string $default_currency)
|
||||
{
|
||||
parent::__construct($security);
|
||||
parent::__construct($security, $eventCommentNeededHelper);
|
||||
$this->default_currency = $default_currency;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace App\Form\AdminPages;
|
||||
|
||||
use App\Entity\Base\AbstractNamedDBElement;
|
||||
use App\Entity\Parts\MeasurementUnit;
|
||||
use App\Entity\UserSystem\User;
|
||||
use App\Form\Type\StructuralEntityType;
|
||||
use App\Form\Type\UserSelectType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
|
||||
@@ -63,5 +65,16 @@ class StorelocationAdminForm extends BaseEntityAdminForm
|
||||
'disable_not_selectable' => true,
|
||||
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),
|
||||
]);
|
||||
|
||||
$builder->add('owner', UserSelectType::class, [
|
||||
'required' => false,
|
||||
'label' => 'storelocation.owner.label',
|
||||
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),
|
||||
]);
|
||||
$builder->add('part_owner_must_match', CheckboxType::class, [
|
||||
'required' => false,
|
||||
'label' => 'storelocation.part_owner_must_match.label',
|
||||
'disabled' => !$this->security->isGranted($is_new ? 'create' : 'edit', $entity),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ use App\Entity\Base\AbstractNamedDBElement;
|
||||
use App\Entity\PriceInformations\Currency;
|
||||
use App\Form\Type\BigDecimalMoneyType;
|
||||
use App\Form\Type\StructuralEntityType;
|
||||
use App\Services\LogSystem\EventCommentNeededHelper;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
@@ -33,9 +34,9 @@ class SupplierForm extends CompanyForm
|
||||
{
|
||||
protected string $default_currency;
|
||||
|
||||
public function __construct(Security $security, string $default_currency)
|
||||
public function __construct(Security $security, EventCommentNeededHelper $eventCommentNeededHelper, string $default_currency)
|
||||
{
|
||||
parent::__construct($security);
|
||||
parent::__construct($security, $eventCommentNeededHelper);
|
||||
$this->default_currency = $default_currency;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ use App\DataTables\Filters\Constraints\BooleanConstraint;
|
||||
use App\Form\Type\TriStateCheckboxType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class BooleanConstraintType extends AbstractType
|
||||
@@ -43,4 +45,10 @@ class BooleanConstraintType extends AbstractType
|
||||
'required' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
public function finishView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
//Remove the label from the compound form, as the checkbox already has a label
|
||||
$view->vars['label'] = false;
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ namespace App\Form\Filters\Constraints;
|
||||
use App\DataTables\Filters\Constraints\EntityConstraint;
|
||||
use App\Entity\UserSystem\User;
|
||||
use App\Form\Type\StructuralEntityType;
|
||||
use App\Form\Type\UserSelectType;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
@@ -51,8 +52,7 @@ class UserEntityConstraintType extends AbstractType
|
||||
'filter.entity_constraint.operator.NEQ' => '!=',
|
||||
];
|
||||
|
||||
$builder->add('value', EntityType::class, [
|
||||
'class' => User::class,
|
||||
$builder->add('value', UserSelectType::class, [
|
||||
'required' => false,
|
||||
]);
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ use App\Form\Filters\Constraints\ParameterConstraintType;
|
||||
use App\Form\Filters\Constraints\StructuralEntityConstraintType;
|
||||
use App\Form\Filters\Constraints\TagsConstraintType;
|
||||
use App\Form\Filters\Constraints\TextConstraintType;
|
||||
use App\Form\Filters\Constraints\UserEntityConstraintType;
|
||||
use Svg\Tag\Text;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||
@@ -206,6 +207,10 @@ class PartFilterType extends AbstractType
|
||||
'min' => 0,
|
||||
]);
|
||||
|
||||
$builder->add('lessThanDesired', BooleanConstraintType::class, [
|
||||
'label' => 'part.filter.lessThanDesired'
|
||||
]);
|
||||
|
||||
$builder->add('lotNeedsRefill', BooleanConstraintType::class, [
|
||||
'label' => 'part.filter.lotNeedsRefill'
|
||||
]);
|
||||
@@ -223,6 +228,10 @@ class PartFilterType extends AbstractType
|
||||
'label' => 'part.filter.lotDescription',
|
||||
]);
|
||||
|
||||
$builder->add('lotOwner', UserEntityConstraintType::class, [
|
||||
'label' => 'part.filter.lotOwner',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Attachments count
|
||||
*/
|
||||
|
||||
@@ -37,6 +37,7 @@ use App\Form\Type\RichTextEditorType;
|
||||
use App\Form\Type\SIUnitType;
|
||||
use App\Form\Type\StructuralEntityType;
|
||||
use App\Form\WorkaroundCollectionType;
|
||||
use App\Services\LogSystem\EventCommentNeededHelper;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
@@ -54,17 +55,20 @@ class PartBaseType extends AbstractType
|
||||
{
|
||||
protected Security $security;
|
||||
protected UrlGeneratorInterface $urlGenerator;
|
||||
protected EventCommentNeededHelper $event_comment_needed_helper;
|
||||
|
||||
public function __construct(Security $security, UrlGeneratorInterface $urlGenerator)
|
||||
public function __construct(Security $security, UrlGeneratorInterface $urlGenerator, EventCommentNeededHelper $event_comment_needed_helper)
|
||||
{
|
||||
$this->security = $security;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->event_comment_needed_helper = $event_comment_needed_helper;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
/** @var Part $part */
|
||||
$part = $builder->getData();
|
||||
$new_part = null === $part->getID();
|
||||
|
||||
$status_choices = [
|
||||
'm_status.unknown' => '',
|
||||
@@ -250,7 +254,7 @@ class PartBaseType extends AbstractType
|
||||
$builder->add('log_comment', TextType::class, [
|
||||
'label' => 'edit.log_comment',
|
||||
'mapped' => false,
|
||||
'required' => false,
|
||||
'required' => $this->event_comment_needed_helper->isCommentNeeded($new_part ? 'part_create' : 'part_edit'),
|
||||
'empty_data' => null,
|
||||
]);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ use App\Entity\Parts\PartLot;
|
||||
use App\Entity\Parts\Storelocation;
|
||||
use App\Form\Type\SIUnitType;
|
||||
use App\Form\Type\StructuralEntityType;
|
||||
use App\Form\Type\UserSelectType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\DateType;
|
||||
@@ -98,6 +99,12 @@ class PartLotType extends AbstractType
|
||||
'required' => false,
|
||||
'empty_data' => '',
|
||||
]);
|
||||
|
||||
$builder->add('owner', UserSelectType::class, [
|
||||
'label' => 'part_lot.owner',
|
||||
'required' => false,
|
||||
'help' => 'part_lot.owner.help',
|
||||
]);
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
|
||||
@@ -21,8 +21,13 @@
|
||||
namespace App\Form\Type\Helper;
|
||||
|
||||
use App\Entity\Attachments\AttachmentType;
|
||||
use App\Entity\Base\AbstractDBElement;
|
||||
use App\Entity\Base\AbstractNamedDBElement;
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
use App\Entity\Contracts\HasMasterAttachmentInterface;
|
||||
use App\Entity\PriceInformations\Currency;
|
||||
use App\Entity\UserSystem\User;
|
||||
use App\Form\Type\MasterPictureAttachmentType;
|
||||
use App\Services\Attachments\AttachmentURLGenerator;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Intl\Currencies;
|
||||
@@ -43,37 +48,49 @@ class StructuralEntityChoiceHelper
|
||||
|
||||
/**
|
||||
* Generates the choice attributes for the given AbstractStructuralDBElement.
|
||||
* @param AbstractStructuralDBElement $choice
|
||||
* @param AbstractNamedDBElement $choice
|
||||
* @param Options|array $options
|
||||
* @return array|string[]
|
||||
*/
|
||||
public function generateChoiceAttr(AbstractStructuralDBElement $choice, $options): array
|
||||
public function generateChoiceAttr(AbstractNamedDBElement $choice, $options): array
|
||||
{
|
||||
$tmp = [];
|
||||
|
||||
//Disable attribute if the choice is marked as not selectable
|
||||
if (($options['disable_not_selectable'] ?? false) && $choice->isNotSelectable()) {
|
||||
$tmp += ['disabled' => 'disabled'];
|
||||
}
|
||||
|
||||
if ($choice instanceof AttachmentType) {
|
||||
$tmp += ['data-filetype_filter' => $choice->getFiletypeFilter()];
|
||||
}
|
||||
|
||||
$level = $choice->getLevel();
|
||||
/** @var AbstractStructuralDBElement|null $parent */
|
||||
$parent = $options['subentities_of'] ?? null;
|
||||
if (null !== $parent) {
|
||||
$level -= $parent->getLevel() - 1;
|
||||
}
|
||||
|
||||
$tmp += [
|
||||
'data-level' => $level,
|
||||
'data-parent' => $choice->getParent() ? $choice->getParent()->getFullPath() : null,
|
||||
'data-path' => $choice->getFullPath('->'),
|
||||
'data-image' => $choice->getMasterPictureAttachment() ? $this->attachmentURLGenerator->getThumbnailURL($choice->getMasterPictureAttachment(), 'thumbnail_xs') : null,
|
||||
$tmp = [
|
||||
'data-level' => 0,
|
||||
'data-path' => $choice->getName(),
|
||||
];
|
||||
|
||||
if ($choice instanceof AbstractStructuralDBElement) {
|
||||
//Disable attribute if the choice is marked as not selectable
|
||||
if (($options['disable_not_selectable'] ?? false) && $choice->isNotSelectable()) {
|
||||
$tmp += ['disabled' => 'disabled'];
|
||||
}
|
||||
|
||||
if ($choice instanceof AttachmentType) {
|
||||
$tmp += ['data-filetype_filter' => $choice->getFiletypeFilter()];
|
||||
}
|
||||
|
||||
$level = $choice->getLevel();
|
||||
/** @var AbstractStructuralDBElement|null $parent */
|
||||
$parent = $options['subentities_of'] ?? null;
|
||||
if (null !== $parent) {
|
||||
$level -= $parent->getLevel() - 1;
|
||||
}
|
||||
|
||||
$tmp += [
|
||||
'data-level' => $level,
|
||||
'data-parent' => $choice->getParent() ? $choice->getParent()->getFullPath() : null,
|
||||
'data-path' => $choice->getFullPath('->'),
|
||||
];
|
||||
}
|
||||
|
||||
if ($choice instanceof HasMasterAttachmentInterface) {
|
||||
$tmp['data-image'] = $choice->getMasterPictureAttachment() ?
|
||||
$this->attachmentURLGenerator->getThumbnailURL($choice->getMasterPictureAttachment(),
|
||||
'thumbnail_xs')
|
||||
: null
|
||||
;
|
||||
}
|
||||
|
||||
if ($choice instanceof AttachmentType && !empty($choice->getFiletypeFilter())) {
|
||||
$tmp += ['data-filetype_filter' => $choice->getFiletypeFilter()];
|
||||
}
|
||||
@@ -112,20 +129,20 @@ class StructuralEntityChoiceHelper
|
||||
|
||||
/**
|
||||
* Returns the choice label for the given AbstractStructuralDBElement.
|
||||
* @param AbstractStructuralDBElement $choice
|
||||
* @param AbstractNamedDBElement $choice
|
||||
* @return string
|
||||
*/
|
||||
public function generateChoiceLabel(AbstractStructuralDBElement $choice): string
|
||||
public function generateChoiceLabel(AbstractNamedDBElement $choice): string
|
||||
{
|
||||
return $choice->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the choice value for the given AbstractStructuralDBElement.
|
||||
* @param AbstractStructuralDBElement|null $element
|
||||
* @param AbstractNamedDBElement|null $element
|
||||
* @return string|int|null
|
||||
*/
|
||||
public function generateChoiceValue(?AbstractStructuralDBElement $element)
|
||||
public function generateChoiceValue(?AbstractNamedDBElement $element)
|
||||
{
|
||||
if ($element === null) {
|
||||
return null;
|
||||
@@ -138,18 +155,21 @@ class StructuralEntityChoiceHelper
|
||||
* So please do not change this!
|
||||
*/
|
||||
if ($element->getID() === null) {
|
||||
//Must be the same as the separator in the choice_loader, otherwise this will not work!
|
||||
return $element->getFullPath('->');
|
||||
if ($element instanceof AbstractStructuralDBElement) {
|
||||
//Must be the same as the separator in the choice_loader, otherwise this will not work!
|
||||
return $element->getFullPath('->');
|
||||
}
|
||||
return $element->getName();
|
||||
}
|
||||
|
||||
return $element->getID();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AbstractStructuralDBElement $element
|
||||
* @param AbstractDBElement $element
|
||||
* @return string|null
|
||||
*/
|
||||
public function generateGroupBy(AbstractStructuralDBElement $element): ?string
|
||||
public function generateGroupBy(AbstractDBElement $element): ?string
|
||||
{
|
||||
//Show entities that are not added to DB yet separately from other entities
|
||||
if ($element->getID() === null) {
|
||||
|
||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Entity\Attachments\AttachmentType;
|
||||
use App\Entity\Base\AbstractNamedDBElement;
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
use App\Form\Type\Helper\StructuralEntityChoiceHelper;
|
||||
use App\Form\Type\Helper\StructuralEntityChoiceLoader;
|
||||
@@ -105,7 +106,7 @@ class StructuralEntityType extends AbstractType
|
||||
'show_fullpath_in_subtext' => true, //When this is enabled, the full path will be shown in subtext
|
||||
'subentities_of' => null, //Only show entities with the given parent class
|
||||
'disable_not_selectable' => false, //Disable entries with not selectable property
|
||||
'choice_value' => function (?AbstractStructuralDBElement $element) {
|
||||
'choice_value' => function (?AbstractNamedDBElement $element) {
|
||||
return $this->choice_helper->generateChoiceValue($element);
|
||||
}, //Use the element id as option value and for comparing items
|
||||
'choice_loader' => function (Options $options) {
|
||||
@@ -121,7 +122,7 @@ class StructuralEntityType extends AbstractType
|
||||
return $this->choice_helper->generateChoiceAttr($choice, $options);
|
||||
};
|
||||
},
|
||||
'group_by' => function (AbstractStructuralDBElement $element) {
|
||||
'group_by' => function (AbstractNamedDBElement $element) {
|
||||
return $this->choice_helper->generateGroupBy($element);
|
||||
},
|
||||
'choice_translation_domain' => false, //Don't translate the entity names
|
||||
|
||||
47
src/Form/Type/UserSelectType.php
Normal file
47
src/Form/Type/UserSelectType.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Entity\UserSystem\User;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\OptionsResolver\Options;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class UserSelectType extends AbstractType
|
||||
{
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver)
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'class' => User::class,
|
||||
'choice_label' => function (Options $options) {
|
||||
return function (User $choice, $key, $value) {
|
||||
return $choice->getFullName(true);
|
||||
};
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return StructuralEntityType::class;
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ use App\Entity\UserSystem\User;
|
||||
use App\Form\Permissions\PermissionsType;
|
||||
use App\Form\Type\CurrencyEntityType;
|
||||
use App\Form\Type\MasterPictureAttachmentType;
|
||||
use App\Form\Type\RichTextEditorType;
|
||||
use App\Form\Type\StructuralEntityType;
|
||||
use App\Form\Type\ThemeChoiceType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
@@ -116,7 +117,11 @@ class UserAdminForm extends AbstractType
|
||||
'required' => false,
|
||||
'disabled' => !$this->security->isGranted('edit_infos', $entity),
|
||||
])
|
||||
|
||||
->add('showEmailOnProfile', CheckboxType::class, [
|
||||
'required' => false,
|
||||
'label' => 'user.show_email_on_profile.label',
|
||||
'disabled' => !$this->security->isGranted('edit_infos', $entity),
|
||||
])
|
||||
->add('department', TextType::class, [
|
||||
'empty_data' => '',
|
||||
'label' => 'user.department.label',
|
||||
@@ -126,6 +131,16 @@ class UserAdminForm extends AbstractType
|
||||
'required' => false,
|
||||
'disabled' => !$this->security->isGranted('edit_infos', $entity),
|
||||
])
|
||||
->add('aboutMe', RichTextEditorType::class, [
|
||||
'required' => false,
|
||||
'empty_data' => '',
|
||||
'label' => 'user.aboutMe.label',
|
||||
'attr' => [
|
||||
'rows' => 4,
|
||||
],
|
||||
'mode' => 'markdown-full',
|
||||
'disabled' => !$this->security->isGranted('edit_infos', $entity),
|
||||
])
|
||||
|
||||
//Config section
|
||||
->add('language', LanguageType::class, [
|
||||
|
||||
@@ -24,9 +24,11 @@ namespace App\Form;
|
||||
|
||||
use App\Entity\UserSystem\User;
|
||||
use App\Form\Type\CurrencyEntityType;
|
||||
use App\Form\Type\RichTextEditorType;
|
||||
use App\Form\Type\ThemeChoiceType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Event\PreSetDataEvent;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||
@@ -79,6 +81,11 @@ class UserSettingsType extends AbstractType
|
||||
'label' => 'user.email.label',
|
||||
'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode,
|
||||
])
|
||||
->add('showEmailOnProfile', CheckboxType::class, [
|
||||
'required' => false,
|
||||
'label' => 'user.show_email_on_profile.label',
|
||||
'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode,
|
||||
])
|
||||
->add('avatar_file', FileType::class, [
|
||||
'label' => 'user_settings.change_avatar.label',
|
||||
'mapped' => false,
|
||||
@@ -93,6 +100,16 @@ class UserSettingsType extends AbstractType
|
||||
]),
|
||||
],
|
||||
])
|
||||
->add('aboutMe', RichTextEditorType::class, [
|
||||
'required' => false,
|
||||
'empty_data' => '',
|
||||
'label' => 'user.aboutMe.label',
|
||||
'attr' => [
|
||||
'rows' => 4,
|
||||
],
|
||||
'mode' => 'markdown-full',
|
||||
'disabled' => !$this->security->isGranted('edit_infos', $options['data']) || $this->demo_mode,
|
||||
])
|
||||
->add('language', LanguageType::class, [
|
||||
'disabled' => $this->demo_mode,
|
||||
'required' => false,
|
||||
|
||||
@@ -36,8 +36,6 @@ use Psr\Log\LoggerInterface;
|
||||
abstract class AbstractMultiPlatformMigration extends AbstractMigration
|
||||
{
|
||||
public const ADMIN_PW_LENGTH = 10;
|
||||
|
||||
protected bool $permissions_updated = false;
|
||||
protected string $admin_pw = '';
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
@@ -122,17 +120,9 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration
|
||||
return password_hash($this->admin_pw, PASSWORD_DEFAULT);
|
||||
}
|
||||
|
||||
public function printPermissionUpdateMessage(): void
|
||||
{
|
||||
$this->permissions_updated = true;
|
||||
}
|
||||
|
||||
public function postUp(Schema $schema): void
|
||||
{
|
||||
parent::postUp($schema);
|
||||
if($this->permissions_updated) {
|
||||
$this->logger->warning('<question>[!!!] Permissions were updated! Please check if they fit your expectations!</question>');
|
||||
}
|
||||
|
||||
if (!empty($this->admin_pw)) {
|
||||
$this->logger->warning('');
|
||||
@@ -141,6 +131,27 @@ abstract class AbstractMultiPlatformMigration extends AbstractMigration
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a foreign key on a table exists in the database.
|
||||
* This method is only supported for MySQL/MariaDB databases yet!
|
||||
* @param string $table
|
||||
* @param string $fk_name
|
||||
* @return bool Returns true, if the foreign key exists
|
||||
* @throws Exception
|
||||
*/
|
||||
public function doesFKExists(string $table, string $fk_name): bool
|
||||
{
|
||||
$db_type = $this->getDatabaseType();
|
||||
if ($db_type !== 'mysql') {
|
||||
throw new \RuntimeException('This method is only supported for MySQL/MariaDB databases!');
|
||||
}
|
||||
|
||||
$sql = "SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS WHERE CONSTRAINT_SCHEMA = DATABASE() AND CONSTRAINT_NAME = '$fk_name' AND TABLE_NAME = '$table' AND CONSTRAINT_TYPE = 'FOREIGN KEY'";
|
||||
$result = (int) $this->connection->fetchOne($sql);
|
||||
|
||||
return $result > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the database type of the used database.
|
||||
* @return string|null Returns 'mysql' for MySQL/MariaDB and 'sqlite' for SQLite. Returns null if unknown type
|
||||
|
||||
@@ -59,4 +59,14 @@ class NamedDBElementRepository extends DBElementRepository
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all nodes to use in a select box.
|
||||
* @return AbstractNamedDBElement[]
|
||||
*/
|
||||
public function toNodesList(): array
|
||||
{
|
||||
//All nodes are sorted by name
|
||||
return $this->findBy([], ['name' => 'ASC']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,15 @@ class PartLotVoter extends ExtendedVoter
|
||||
|
||||
if (in_array($attribute, ['withdraw', 'add', 'move']))
|
||||
{
|
||||
return $this->resolver->inherit($user, 'parts_stock', $attribute) ?? false;
|
||||
$base_permission = $this->resolver->inherit($user, 'parts_stock', $attribute) ?? false;
|
||||
|
||||
$lot_permission = true;
|
||||
//If the lot has an owner, we need to check if the user is the owner of the lot to be allowed to withdraw it.
|
||||
if ($subject instanceof PartLot && $subject->getOwner()) {
|
||||
$lot_permission = $subject->getOwner() === $user || $subject->getOwner()->getID() === $user->getID();
|
||||
}
|
||||
|
||||
return $base_permission && $lot_permission;
|
||||
}
|
||||
|
||||
switch ($attribute) {
|
||||
|
||||
@@ -38,10 +38,13 @@ class UserVoter extends ExtendedVoter
|
||||
protected function supports(string $attribute, $subject): bool
|
||||
{
|
||||
if (is_a($subject, User::class, true)) {
|
||||
return in_array($attribute, array_merge(
|
||||
$this->resolver->listOperationsForPermission('users'),
|
||||
$this->resolver->listOperationsForPermission('self')),
|
||||
false
|
||||
return in_array($attribute,
|
||||
array_merge(
|
||||
$this->resolver->listOperationsForPermission('users'),
|
||||
$this->resolver->listOperationsForPermission('self'),
|
||||
['info']
|
||||
),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -56,6 +59,16 @@ class UserVoter extends ExtendedVoter
|
||||
*/
|
||||
protected function voteOnUser(string $attribute, $subject, User $user): bool
|
||||
{
|
||||
if ($attribute === 'info') {
|
||||
//Every logged-in user (non-anonymous) can see the info pages of other users
|
||||
if (!$user->isAnonymousUser()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//For the anonymous user, use the user read permission
|
||||
$attribute = 'read';
|
||||
}
|
||||
|
||||
//Check if the checked user is the user itself
|
||||
if (($subject instanceof User) && $subject->getID() === $user->getID() &&
|
||||
$this->resolver->isValidOperation('self', $attribute)) {
|
||||
|
||||
92
src/Serializer/StructuralElementDenormalizer.php
Normal file
92
src/Serializer/StructuralElementDenormalizer.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Serializer;
|
||||
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
use App\Repository\StructuralDBElementRepository;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\ContextAwareDenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
|
||||
|
||||
class StructuralElementDenormalizer implements ContextAwareDenormalizerInterface, CacheableSupportsMethodInterface
|
||||
{
|
||||
|
||||
private DenormalizerInterface $normalizer;
|
||||
private EntityManagerInterface $entityManager;
|
||||
|
||||
private array $object_cache = [];
|
||||
|
||||
public function __construct(ObjectNormalizer $normalizer, EntityManagerInterface $entityManager)
|
||||
{
|
||||
$this->normalizer = $normalizer;
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
public function supportsDenormalization($data, string $type, string $format = null, array $context = [])
|
||||
{
|
||||
return is_array($data)
|
||||
&& is_subclass_of($type, AbstractStructuralDBElement::class)
|
||||
//Only denormalize if we are doing an file import operation
|
||||
&& in_array('import', $context['groups'] ?? []);
|
||||
}
|
||||
|
||||
public function denormalize($data, string $type, string $format = null, array $context = [])
|
||||
{
|
||||
/** @var AbstractStructuralDBElement $deserialized_entity */
|
||||
$deserialized_entity = $this->normalizer->denormalize($data, $type, $format, $context);
|
||||
|
||||
//Check if we already have the entity in the database (via path)
|
||||
/** @var StructuralDBElementRepository $repo */
|
||||
$repo = $this->entityManager->getRepository($type);
|
||||
|
||||
$path = $deserialized_entity->getFullPath(AbstractStructuralDBElement::PATH_DELIMITER_ARROW);
|
||||
$db_elements = $repo->getEntityByPath($path, AbstractStructuralDBElement::PATH_DELIMITER_ARROW);
|
||||
if ($db_elements) {
|
||||
//We already have the entity in the database, so we can return it
|
||||
return end($db_elements);
|
||||
}
|
||||
|
||||
|
||||
//Check if we have created the entity in this request before (so we don't create multiple entities for the same path)
|
||||
//Entities get saved in the cache by type and path
|
||||
//We use a different cache for this then the objects created by a string value (saved in repo). However that should not be a problem
|
||||
//unless the user data has mixed structure between json data and a string path
|
||||
if (isset($this->object_cache[$type][$path])) {
|
||||
return $this->object_cache[$type][$path];
|
||||
}
|
||||
|
||||
//Save the entity in the cache
|
||||
$this->object_cache[$type][$path] = $deserialized_entity;
|
||||
|
||||
//We don't have the entity in the database, so we have to persist it
|
||||
$this->entityManager->persist($deserialized_entity);
|
||||
|
||||
return $deserialized_entity;
|
||||
}
|
||||
|
||||
public function hasCacheableSupportsMethod(): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -24,9 +24,13 @@ namespace App\Services\Attachments;
|
||||
|
||||
use App\Entity\Attachments\Attachment;
|
||||
use InvalidArgumentException;
|
||||
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
|
||||
use Liip\ImagineBundle\Service\FilterService;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\UrlHelper;
|
||||
use Symfony\Component\Routing\Generator\UrlGenerator;
|
||||
use function strlen;
|
||||
use Symfony\Component\Asset\Packages;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
@@ -38,19 +42,19 @@ class AttachmentURLGenerator
|
||||
protected AttachmentPathResolver $pathResolver;
|
||||
protected UrlGeneratorInterface $urlGenerator;
|
||||
protected AttachmentManager $attachmentHelper;
|
||||
protected FilterService $filterService;
|
||||
protected CacheManager $thumbnailManager;
|
||||
|
||||
protected LoggerInterface $logger;
|
||||
|
||||
public function __construct(Packages $assets, AttachmentPathResolver $pathResolver,
|
||||
UrlGeneratorInterface $urlGenerator, AttachmentManager $attachmentHelper,
|
||||
FilterService $filterService, LoggerInterface $logger)
|
||||
CacheManager $thumbnailManager, LoggerInterface $logger)
|
||||
{
|
||||
$this->assets = $assets;
|
||||
$this->pathResolver = $pathResolver;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->attachmentHelper = $attachmentHelper;
|
||||
$this->filterService = $filterService;
|
||||
$this->thumbnailManager = $thumbnailManager;
|
||||
$this->logger = $logger;
|
||||
|
||||
//Determine a normalized path to the public folder (assets are relative to this folder)
|
||||
@@ -145,17 +149,16 @@ class AttachmentURLGenerator
|
||||
return $this->urlGenerator->generate('attachment_view', ['id' => $attachment->getID()]);
|
||||
}
|
||||
|
||||
//For builtin ressources it is not useful to create a thumbnail
|
||||
//because the footprints images are small and highly optimized already.
|
||||
if (('thumbnail_md' === $filter_name && $attachment->isBuiltIn())
|
||||
//GD can not work with SVG, so serve it directly...
|
||||
|| 'svg' === $attachment->getExtension()) {
|
||||
//GD can not work with SVG, so serve it directly...
|
||||
if ('svg' === $attachment->getExtension()) {
|
||||
return $this->assets->getUrl($asset_path);
|
||||
}
|
||||
|
||||
try {
|
||||
//Otherwise we can serve the relative path via Asset component
|
||||
return $this->filterService->getUrlOfFilteredImage($asset_path, $filter_name);
|
||||
//We try to get network path here (so no schema), but this param might just get ignored by the cache manager
|
||||
$tmp = $this->thumbnailManager->getBrowserPath($asset_path, $filter_name, [], null, UrlGeneratorInterface::NETWORK_PATH);
|
||||
//So we remove the schema manually
|
||||
return preg_replace('/^https?:/', '', $tmp);
|
||||
} catch (\Imagine\Exception\RuntimeException $e) {
|
||||
//If the filter fails, we can not serve the thumbnail and fall back to the original image and log an warning
|
||||
$this->logger->warning('Could not open thumbnail for attachment with ID ' . $attachment->getID() . ': ' . $e->getMessage());
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\ImportExportSystem\PartKeeprImporter;
|
||||
|
||||
class MySQLDumpXMLConverter
|
||||
{
|
||||
|
||||
/**
|
||||
* Converts a MySQL dump XML file to an associative array structure in the following form
|
||||
* [
|
||||
* 'table_name' => [
|
||||
* [
|
||||
* 'column_name' => 'value',
|
||||
* 'column_name' => 'value',
|
||||
* ...
|
||||
* ],
|
||||
* [
|
||||
* 'column_name' => 'value',
|
||||
* 'column_name' => 'value',
|
||||
* ...
|
||||
* ],
|
||||
* ...
|
||||
* ],
|
||||
*
|
||||
* @param string $xml_string The XML string to convert
|
||||
* @return array The associative array structure
|
||||
*/
|
||||
public function convertMySQLDumpXMLDataToArrayStructure(string $xml_string): array
|
||||
{
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadXML($xml_string);
|
||||
|
||||
//Check that the root node is a <mysqldump> node
|
||||
$root = $dom->documentElement;
|
||||
if ($root->nodeName !== 'mysqldump') {
|
||||
throw new \InvalidArgumentException('The given XML string is not a valid MySQL dump XML file!');
|
||||
}
|
||||
|
||||
//Get all <database> nodes (there must be exactly one)
|
||||
$databases = $root->getElementsByTagName('database');
|
||||
if ($databases->length !== 1) {
|
||||
throw new \InvalidArgumentException('The given XML string is not a valid MySQL dump XML file!');
|
||||
}
|
||||
|
||||
//Get the <database> node
|
||||
$database = $databases->item(0);
|
||||
|
||||
//Get all <table_data> nodes
|
||||
$tables = $database->getElementsByTagName('table_data');
|
||||
$table_data = [];
|
||||
|
||||
//Iterate over all <table> nodes and convert them to arrays
|
||||
foreach ($tables as $table) {
|
||||
$table_data[$table->getAttribute('name')] = $this->convertTableToArray($table);
|
||||
}
|
||||
|
||||
return $table_data;
|
||||
}
|
||||
|
||||
private function convertTableToArray(\DOMElement $table): array
|
||||
{
|
||||
$table_data = [];
|
||||
|
||||
//Get all <row> nodes
|
||||
$rows = $table->getElementsByTagName('row');
|
||||
|
||||
//Iterate over all <row> nodes and convert them to arrays
|
||||
foreach ($rows as $row) {
|
||||
$table_data[] = $this->convertTableRowToArray($row);
|
||||
}
|
||||
|
||||
return $table_data;
|
||||
}
|
||||
|
||||
private function convertTableRowToArray(\DOMElement $table_row): array
|
||||
{
|
||||
$row_data = [];
|
||||
|
||||
//Get all <field> nodes
|
||||
$fields = $table_row->getElementsByTagName('field');
|
||||
|
||||
//Iterate over all <field> nodes
|
||||
foreach ($fields as $field) {
|
||||
$row_data[$field->getAttribute('name')] = $field->nodeValue;
|
||||
}
|
||||
|
||||
return $row_data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\ImportExportSystem\PartKeeprImporter;
|
||||
|
||||
use App\Doctrine\Purger\ResetAutoIncrementORMPurger;
|
||||
use App\Entity\Attachments\FootprintAttachment;
|
||||
use App\Entity\Attachments\ManufacturerAttachment;
|
||||
use App\Entity\Attachments\StorelocationAttachment;
|
||||
use App\Entity\Base\AbstractDBElement;
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
use App\Entity\Contracts\TimeStampableInterface;
|
||||
use App\Entity\Parameters\PartParameter;
|
||||
use App\Entity\Parts\Category;
|
||||
use App\Entity\Parts\Footprint;
|
||||
use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\MeasurementUnit;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Entity\Parts\Storelocation;
|
||||
use App\Entity\Parts\Supplier;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
use function \count;
|
||||
|
||||
/**
|
||||
* This service is used to import the datastructures (categories, manufacturers, etc.) from a PartKeepr export.
|
||||
*/
|
||||
class PKDatastructureImporter
|
||||
{
|
||||
|
||||
use PKImportHelperTrait;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Imports the distributors from the given data.
|
||||
* @param array $data The data to import (associated array, containing a 'distributor' key
|
||||
* @return int The number of imported distributors
|
||||
*/
|
||||
public function importDistributors(array $data): int
|
||||
{
|
||||
if (!isset($data['distributor'])) {
|
||||
throw new \RuntimeException('$data must contain a "distributor" key!');
|
||||
}
|
||||
|
||||
$distributor_data = $data['distributor'];
|
||||
|
||||
foreach ($distributor_data as $distributor) {
|
||||
$supplier = new Supplier();
|
||||
$supplier->setName($distributor['name']);
|
||||
$supplier->setWebsite($distributor['url'] ?? '');
|
||||
$supplier->setAddress($distributor['address'] ?? '');
|
||||
$supplier->setPhoneNumber($distributor['phone'] ?? '');
|
||||
$supplier->setFaxNumber($distributor['fax'] ?? '');
|
||||
$supplier->setEmailAddress($distributor['email'] ?? '');
|
||||
$supplier->setComment($distributor['comment']);
|
||||
$supplier->setAutoProductUrl($distributor['skuurl'] ?? '');
|
||||
|
||||
$this->setIDOfEntity($supplier, $distributor['id']);
|
||||
$this->em->persist($supplier);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
return count($distributor_data);
|
||||
}
|
||||
|
||||
public function importManufacturers(array $data): int
|
||||
{
|
||||
if (!isset($data['manufacturer'])) {
|
||||
throw new \RuntimeException('$data must contain a "manufacturer" key!');
|
||||
}
|
||||
|
||||
$manufacturer_data = $data['manufacturer'];
|
||||
|
||||
$max_id = 0;
|
||||
|
||||
//Assign a parent manufacturer to all manufacturers, as partkeepr has a lot of manufacturers by default
|
||||
$parent_manufacturer = new Manufacturer();
|
||||
$parent_manufacturer->setName('PartKeepr');
|
||||
$parent_manufacturer->setNotSelectable(true);
|
||||
|
||||
foreach ($manufacturer_data as $manufacturer) {
|
||||
$entity = new Manufacturer();
|
||||
$entity->setName($manufacturer['name']);
|
||||
$entity->setWebsite($manufacturer['url'] ?? '');
|
||||
$entity->setAddress($manufacturer['address'] ?? '');
|
||||
$entity->setPhoneNumber($manufacturer['phone'] ?? '');
|
||||
$entity->setFaxNumber($manufacturer['fax'] ?? '');
|
||||
$entity->setEmailAddress($manufacturer['email'] ?? '');
|
||||
$entity->setComment($manufacturer['comment']);
|
||||
$entity->setParent($parent_manufacturer);
|
||||
|
||||
$this->setIDOfEntity($entity, $manufacturer['id']);
|
||||
$this->em->persist($entity);
|
||||
|
||||
$max_id = max($max_id, $manufacturer['id']);
|
||||
}
|
||||
|
||||
//Set the ID of the parent manufacturer to the max ID + 1, to avoid trouble with the auto increment
|
||||
$this->setIDOfEntity($parent_manufacturer, $max_id + 1);
|
||||
$this->em->persist($parent_manufacturer);
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
$this->importAttachments($data, 'manufacturericlogo', Manufacturer::class, 'manufacturer_id', ManufacturerAttachment::class);
|
||||
|
||||
return count($manufacturer_data);
|
||||
}
|
||||
|
||||
public function importPartUnits(array $data): int
|
||||
{
|
||||
if (!isset($data['partunit'])) {
|
||||
throw new \RuntimeException('$data must contain a "partunit" key!');
|
||||
}
|
||||
|
||||
$partunit_data = $data['partunit'];
|
||||
foreach ($partunit_data as $partunit) {
|
||||
$unit = new MeasurementUnit();
|
||||
$unit->setName($partunit['name']);
|
||||
$unit->setUnit($partunit['shortName'] ?? null);
|
||||
|
||||
$this->setIDOfEntity($unit, $partunit['id']);
|
||||
$this->em->persist($unit);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
return count($partunit_data);
|
||||
}
|
||||
|
||||
public function importCategories(array $data): int
|
||||
{
|
||||
if (!isset($data['partcategory'])) {
|
||||
throw new \RuntimeException('$data must contain a "partcategory" key!');
|
||||
}
|
||||
|
||||
$partcategory_data = $data['partcategory'];
|
||||
|
||||
//In a first step, create all categories like they were a flat structure (so ignore the parent)
|
||||
foreach ($partcategory_data as $partcategory) {
|
||||
$category = new Category();
|
||||
$category->setName($partcategory['name']);
|
||||
$category->setComment($partcategory['description']);
|
||||
|
||||
$this->setIDOfEntity($category, $partcategory['id']);
|
||||
$this->em->persist($category);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
//In a second step, set the correct parent element
|
||||
foreach ($partcategory_data as $partcategory) {
|
||||
$this->setParent(Category::class, $partcategory['id'], $partcategory['parent_id']);
|
||||
}
|
||||
$this->em->flush();
|
||||
|
||||
return count($partcategory_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* The common import functions for footprints and storeloactions
|
||||
* @param array $data
|
||||
* @param string $target_class
|
||||
* @param string $data_prefix
|
||||
* @return int
|
||||
*/
|
||||
private function importElementsWithCategory(array $data, string $target_class, string $data_prefix): int
|
||||
{
|
||||
$key = $data_prefix;
|
||||
$category_key = $data_prefix.'category';
|
||||
|
||||
if (!isset($data[$key])) {
|
||||
throw new \RuntimeException('$data must contain a "'. $key .'" key!');
|
||||
}
|
||||
if (!isset($data[$category_key])) {
|
||||
throw new \RuntimeException('$data must contain a "'. $category_key .'" key!');
|
||||
}
|
||||
|
||||
//We import the footprints first, as we need the IDs of the footprints be our real DBs later (as we match the part import by ID)
|
||||
//As the footprints category is not existing yet, we just skip the parent field for now
|
||||
$footprint_data = $data[$key];
|
||||
$max_footprint_id = 0;
|
||||
foreach ($footprint_data as $footprint) {
|
||||
$entity = new $target_class();
|
||||
$entity->setName($footprint['name']);
|
||||
$entity->setComment($footprint['description'] ?? '');
|
||||
|
||||
$this->setIDOfEntity($entity, $footprint['id']);
|
||||
$this->em->persist($entity);
|
||||
$max_footprint_id = max($max_footprint_id, (int) $footprint['id']);
|
||||
}
|
||||
|
||||
//Import the footprint categories ignoring the parents for now
|
||||
//Their IDs are $max_footprint_id + $ID
|
||||
$footprintcategory_data = $data[$category_key];
|
||||
foreach ($footprintcategory_data as $footprintcategory) {
|
||||
$entity = new $target_class();
|
||||
$entity->setName($footprintcategory['name']);
|
||||
$entity->setComment($footprintcategory['description']);
|
||||
//Categories are not assignable to parts, so we set them to not selectable
|
||||
$entity->setNotSelectable(true);
|
||||
|
||||
$this->setIDOfEntity($entity, $max_footprint_id + (int) $footprintcategory['id']);
|
||||
$this->em->persist($entity);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
//Now we can correct the parents and category IDs of the parts
|
||||
foreach ($footprintcategory_data as $footprintcategory) {
|
||||
//We have to use the mapped IDs here, as the imported ID is not the effective ID
|
||||
if ($footprintcategory['parent_id']) {
|
||||
$this->setParent($target_class, $max_footprint_id + (int)$footprintcategory['id'],
|
||||
$max_footprint_id + (int)$footprintcategory['parent_id']);
|
||||
}
|
||||
}
|
||||
foreach ($footprint_data as $footprint) {
|
||||
if ($footprint['category_id']) {
|
||||
$this->setParent($target_class, $footprint['id'],
|
||||
$max_footprint_id + (int)$footprint['category_id']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
return count($footprint_data) + count($footprintcategory_data);
|
||||
}
|
||||
|
||||
public function importFootprints(array $data): int
|
||||
{
|
||||
$count = $this->importElementsWithCategory($data, Footprint::class, 'footprint');
|
||||
|
||||
//Footprints have both attachments and images
|
||||
$this->importAttachments($data, 'footprintattachment', Footprint::class, 'footprint_id', FootprintAttachment::class);
|
||||
$this->importAttachments($data, 'footprintimage', Footprint::class, 'footprint_id', FootprintAttachment::class);
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function importStorelocations(array $data): int
|
||||
{
|
||||
$count = $this->importElementsWithCategory($data, Storelocation::class, 'storagelocation');
|
||||
|
||||
//Footprints have both attachments and images
|
||||
$this->importAttachments($data, 'storagelocationimage', Storelocation::class, 'footprint_id', StorelocationAttachment::class);
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\ImportExportSystem\PartKeeprImporter;
|
||||
|
||||
use App\Doctrine\Purger\ResetAutoIncrementORMPurger;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
|
||||
/**
|
||||
* This service contains various helper functions for the PartKeeprImporter (like purging the database).
|
||||
*/
|
||||
class PKImportHelper
|
||||
{
|
||||
protected EntityManagerInterface $em;
|
||||
|
||||
public function __construct(EntityManagerInterface $em)
|
||||
{
|
||||
$this->em = $em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Purges the database tables for the import, so that all data can be created from scratch.
|
||||
* Existing users and groups are not purged.
|
||||
* This is needed to avoid ID collisions.
|
||||
* @return void
|
||||
*/
|
||||
public function purgeDatabaseForImport(): void
|
||||
{
|
||||
//Versions with "" are needed !!
|
||||
$purger = new ResetAutoIncrementORMPurger($this->em, ['users', '"users"', 'groups', '"groups"', 'u2f_keys', 'internal', 'migration_versions']);
|
||||
$purger->purge();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the current database schema version from the PartKeepr XML dump.
|
||||
* @param array $data
|
||||
* @return string
|
||||
*/
|
||||
public function getDatabaseSchemaVersion(array $data): string
|
||||
{
|
||||
if (!isset($data['schemaversions'])) {
|
||||
throw new \RuntimeException('Could not find schema version in XML dump!');
|
||||
}
|
||||
|
||||
return end($data['schemaversions'])['version'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the database schema of the PartKeepr XML dump is compatible with the importer
|
||||
* @param array $data
|
||||
* @return bool True if the schema is compatible, false otherwise
|
||||
*/
|
||||
public function checkVersion(array $data): bool
|
||||
{
|
||||
return $this->getDatabaseSchemaVersion($data) === '20170601175559';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\ImportExportSystem\PartKeeprImporter;
|
||||
|
||||
use App\Entity\Attachments\Attachment;
|
||||
use App\Entity\Attachments\AttachmentContainingDBElement;
|
||||
use App\Entity\Attachments\AttachmentType;
|
||||
use App\Entity\Base\AbstractDBElement;
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
use App\Entity\Contracts\TimeStampableInterface;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Doctrine\ORM\Mapping\ClassMetadataInfo;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* This trait contains helper functions for the PartKeeprImporter.
|
||||
*/
|
||||
trait PKImportHelperTrait
|
||||
{
|
||||
protected EntityManagerInterface $em;
|
||||
protected PropertyAccessorInterface $propertyAccessor;
|
||||
|
||||
private ?AttachmentType $import_attachment_type = null;
|
||||
|
||||
/**
|
||||
* Converts a PartKeepr attachment/image row to an Attachment entity.
|
||||
* @param array $attachment_row The attachment row from the PartKeepr database
|
||||
* @param string $target_class The target class for the attachment
|
||||
* @param string $type The type of the attachment (attachment or image)
|
||||
* @return Attachment
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function convertAttachmentDataToEntity(array $attachment_row, string $target_class, string $type): Attachment
|
||||
{
|
||||
//By default we use the cached version
|
||||
if (!$this->import_attachment_type) {
|
||||
//Get the import attachment type
|
||||
$this->import_attachment_type = $this->em->getRepository(AttachmentType::class)->findOneBy([
|
||||
'name' => 'PartKeepr Attachment'
|
||||
]);
|
||||
if (!$this->import_attachment_type) { //If not existing in DB create it
|
||||
$this->import_attachment_type = new AttachmentType();
|
||||
$this->import_attachment_type->setName('PartKeepr Attachment');
|
||||
$this->em->persist($this->import_attachment_type);
|
||||
}
|
||||
}
|
||||
|
||||
if (!in_array($type, ['attachment', 'image'], true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The type %s is not a valid attachment type', $type));
|
||||
}
|
||||
|
||||
if (!is_a($target_class, Attachment::class, true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The target class %s is not a subclass of %s', $target_class, Attachment::class));
|
||||
}
|
||||
|
||||
/** @var Attachment $attachment */
|
||||
$attachment = new $target_class();
|
||||
if (!empty($attachment_row['description'])) {
|
||||
$attachment->setName($attachment_row['description']);
|
||||
} else {
|
||||
$attachment->setName($attachment_row['originalname']);
|
||||
}
|
||||
$attachment->setFilename($attachment_row['originalname']);
|
||||
$attachment->setAttachmentType($this->import_attachment_type);
|
||||
$this->setCreationDate($attachment, $attachment_row['created']);
|
||||
|
||||
//Determine file extension (if the extension is empty, we use the original extension)
|
||||
if (empty($attachment_row['extension'])) {
|
||||
$attachment_row['extension'] = pathinfo($attachment_row['originalname'], PATHINFO_EXTENSION);
|
||||
}
|
||||
|
||||
//Determine file path
|
||||
//Images are stored in the (public) media folder, attachments in the (private) uploads/ folder
|
||||
$path = $type === 'attachment' ? '%SECURE%' : '%MEDIA%';
|
||||
//The folder is the type of the attachment from the PartKeepr database
|
||||
$path .= '/'.$attachment_row['type'];
|
||||
//Next comes the filename plus extension
|
||||
$path .= '/'.$attachment_row['filename'].'.'.$attachment_row['extension'];
|
||||
|
||||
$attachment->setPath($path);
|
||||
|
||||
return $attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports the attachments from the given data
|
||||
* @param array $data The PartKeepr database
|
||||
* @param string $table_name The table name for the attachments (if it contain "image", it will be treated as an image)
|
||||
* @param string $target_class The target class (e.g. Part)
|
||||
* @param string $target_id_field The field name where the target ID is stored
|
||||
* @param string $attachment_class The attachment class (e.g. PartAttachment)
|
||||
* @return void
|
||||
*/
|
||||
protected function importAttachments(array $data, string $table_name, string $target_class, string $target_id_field, string $attachment_class): void
|
||||
{
|
||||
//Determine if we have an image or an attachment
|
||||
$type = str_contains($table_name, 'image') || str_contains($table_name, 'iclogo') ? 'image' : 'attachment';
|
||||
|
||||
if (!isset($data[$table_name])) {
|
||||
throw new \RuntimeException(sprintf('The table %s does not exist in the PartKeepr database', $table_name));
|
||||
}
|
||||
|
||||
if (!is_a($target_class, AttachmentContainingDBElement::class, true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The target class %s is not a subclass of %s', $target_class, AttachmentContainingDBElement::class));
|
||||
}
|
||||
|
||||
if (!is_a($attachment_class, Attachment::class, true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The attachment class %s is not a subclass of %s', $attachment_class, Attachment::class));
|
||||
}
|
||||
|
||||
//Get the table data
|
||||
$table_data = $data[$table_name];
|
||||
foreach($table_data as $attachment_row) {
|
||||
$attachment = $this->convertAttachmentDataToEntity($attachment_row, $attachment_class, $type);
|
||||
|
||||
//Retrieve the target entity
|
||||
$target_id = (int) $attachment_row[$target_id_field];
|
||||
/** @var AttachmentContainingDBElement $target */
|
||||
$target = $this->em->find($target_class, $target_id);
|
||||
if (!$target) {
|
||||
throw new \RuntimeException(sprintf('Could not find target entity with ID %s', $target_id));
|
||||
}
|
||||
|
||||
$target->addAttachment($attachment);
|
||||
$this->em->persist($attachment);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the parent to the given entity, using the numerical IDs from the imported data.
|
||||
* @param string $class
|
||||
* @param int|string $element_id
|
||||
* @param int|string $parent_id
|
||||
* @return AbstractStructuralDBElement The structural element that was modified (with $element_id)
|
||||
*/
|
||||
protected function setParent(string $class, $element_id, $parent_id): AbstractStructuralDBElement
|
||||
{
|
||||
$element = $this->em->find($class, (int) $element_id);
|
||||
if (!$element) {
|
||||
throw new \RuntimeException(sprintf('Could not find element with ID %s', $element_id));
|
||||
}
|
||||
|
||||
//If the parent is null, we're done
|
||||
if (!$parent_id) {
|
||||
return $element;
|
||||
}
|
||||
|
||||
$parent = $this->em->find($class, (int) $parent_id);
|
||||
if (!$parent) {
|
||||
throw new \RuntimeException(sprintf('Could not find parent with ID %s', $parent_id));
|
||||
}
|
||||
|
||||
$element->setParent($parent);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the given field of the given entity to the entity with the given ID.
|
||||
* @return AbstractDBElement
|
||||
*/
|
||||
protected function setAssociationField(AbstractDBElement $element, string $field, string $other_class, $other_id): AbstractDBElement
|
||||
{
|
||||
//If the parent is null, set the field to null and we're done
|
||||
if (!$other_id) {
|
||||
$this->propertyAccessor->setValue($element, $field, null);
|
||||
return $element;
|
||||
}
|
||||
|
||||
$parent = $this->em->find($other_class, (int) $other_id);
|
||||
if (!$parent) {
|
||||
throw new \RuntimeException(sprintf('Could not find other_class with ID %s', $other_id));
|
||||
}
|
||||
|
||||
$this->propertyAccessor->setValue($element, $field, $parent);
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ID of an entity to a specific value. Must be called before persisting the entity, but before flushing.
|
||||
* @param AbstractDBElement $element
|
||||
* @param int|string $id
|
||||
* @return void
|
||||
*/
|
||||
protected function setIDOfEntity(AbstractDBElement $element, $id): void
|
||||
{
|
||||
if (!is_int($id) && !is_string($id)) {
|
||||
throw new \InvalidArgumentException('ID must be an integer or string');
|
||||
}
|
||||
|
||||
$id = (int) $id;
|
||||
|
||||
$metadata = $this->em->getClassMetadata(get_class($element));
|
||||
$metadata->setIdGeneratorType(ClassMetadataInfo::GENERATOR_TYPE_NONE);
|
||||
$metadata->setIdGenerator(new \Doctrine\ORM\Id\AssignedGenerator());
|
||||
$metadata->setIdentifierValues($element, ['id' => $id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the creation date of an entity to a specific value.
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function setCreationDate(TimeStampableInterface $entity, ?string $datetime_str)
|
||||
{
|
||||
if ($datetime_str) {
|
||||
$date = new \DateTime($datetime_str);
|
||||
} else {
|
||||
$date = null; //Null means "now" at persist time
|
||||
}
|
||||
|
||||
$reflectionClass = new \ReflectionClass($entity);
|
||||
$property = $reflectionClass->getProperty('addedDate');
|
||||
$property->setAccessible(true);
|
||||
$property->setValue($entity, $date);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\ImportExportSystem\PartKeeprImporter;
|
||||
|
||||
use App\Entity\Attachments\ProjectAttachment;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\ProjectSystem\Project;
|
||||
use App\Entity\ProjectSystem\ProjectBOMEntry;
|
||||
use App\Entity\UserSystem\Group;
|
||||
use App\Entity\UserSystem\User;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* This service is used to other non mandatory data from a PartKeepr export.
|
||||
* You have to import the datastructures and parts first to use project import!
|
||||
*/
|
||||
class PKOptionalImporter
|
||||
{
|
||||
use PKImportHelperTrait;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the projects from the given data.
|
||||
* @param array $data
|
||||
* @return int The number of imported projects
|
||||
*/
|
||||
public function importProjects(array $data): int
|
||||
{
|
||||
if (!isset($data['project'])) {
|
||||
throw new \RuntimeException('$data must contain a "project" key!');
|
||||
}
|
||||
if (!isset($data['projectpart'])) {
|
||||
throw new \RuntimeException('$data must contain a "projectpart" key!');
|
||||
}
|
||||
|
||||
$projects_data = $data['project'];
|
||||
$projectparts_data = $data['projectpart'];
|
||||
|
||||
//First import the projects
|
||||
foreach ($projects_data as $project_data) {
|
||||
$project = new Project();
|
||||
$project->setName($project_data['name']);
|
||||
$project->setDescription($project_data['description'] ?? '');
|
||||
|
||||
$this->setIDOfEntity($project, $project_data['id']);
|
||||
$this->em->persist($project);
|
||||
}
|
||||
$this->em->flush();
|
||||
|
||||
//Then the project BOM entries
|
||||
foreach ($projectparts_data as $projectpart_data) {
|
||||
/** @var Project $project */
|
||||
$project = $this->em->find(Project::class, (int) $projectpart_data['project_id']);
|
||||
if (!$project) {
|
||||
throw new \RuntimeException('Could not find project with ID '.$projectpart_data['project_id']);
|
||||
}
|
||||
|
||||
$bom_entry = new ProjectBOMEntry();
|
||||
$bom_entry->setQuantity((float) $projectpart_data['quantity']);
|
||||
$bom_entry->setName($projectpart_data['remarks']);
|
||||
$this->setAssociationField($bom_entry, 'part', Part::class, $projectpart_data['part_id']);
|
||||
|
||||
$comments = [];
|
||||
if (!empty($projectpart_data['lotNumber'])) {
|
||||
$comments[] = 'Lot number: '.$projectpart_data['lotNumber'];
|
||||
}
|
||||
if (!empty($projectpart_data['overage'])) {
|
||||
$comments[] = 'Overage: '.$projectpart_data['overage'].($projectpart_data['overageType'] ? ' %' : ' pcs');
|
||||
}
|
||||
$bom_entry->setComment(implode(',', $comments));
|
||||
|
||||
$project->addBomEntry($bom_entry);
|
||||
}
|
||||
$this->em->flush();
|
||||
|
||||
$this->importAttachments($data, 'projectattachment', Project::class, 'project_id', ProjectAttachment::class);
|
||||
|
||||
return count($projects_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Import the users from the given data.
|
||||
* @param array $data
|
||||
* @return int The number of imported users
|
||||
*/
|
||||
public function importUsers(array $data): int
|
||||
{
|
||||
if (!isset($data['fosuser'])) {
|
||||
throw new \RuntimeException('$data must contain a "fosuser" key!');
|
||||
}
|
||||
|
||||
//All imported users get assigned to the "PartKeepr Users" group
|
||||
$group_users = $this->em->find(Group::class, 3);
|
||||
$group = $this->em->getRepository(Group::class)->findOneBy(['name' => 'PartKeepr Users', 'parent' => $group_users]);
|
||||
if (!$group) {
|
||||
$group = new Group();
|
||||
$group->setName('PartKeepr Users');
|
||||
$group->setParent($group_users);
|
||||
$this->em->persist($group);
|
||||
}
|
||||
|
||||
|
||||
$users_data = $data['fosuser'];
|
||||
foreach ($users_data as $user_data) {
|
||||
if (in_array($user_data['username'], ['admin', 'anonymous'], true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$user = new User();
|
||||
$user->setName($user_data['username']);
|
||||
$user->setEmail($user_data['email']);
|
||||
$user->setGroup($group);
|
||||
|
||||
//User is disabled by default
|
||||
$user->setDisabled(true);
|
||||
|
||||
//We let doctrine generate a new ID for the user
|
||||
$this->em->persist($user);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
return count($users_data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\ImportExportSystem\PartKeeprImporter;
|
||||
|
||||
use App\Entity\Attachments\PartAttachment;
|
||||
use App\Entity\Parameters\PartParameter;
|
||||
use App\Entity\Parts\Category;
|
||||
use App\Entity\Parts\Footprint;
|
||||
use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\MeasurementUnit;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Entity\Parts\Storelocation;
|
||||
use App\Entity\Parts\Supplier;
|
||||
use App\Entity\PriceInformations\Orderdetail;
|
||||
use App\Entity\PriceInformations\Pricedetail;
|
||||
use Brick\Math\BigDecimal;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||
|
||||
/**
|
||||
* This service is used to import parts from a PartKeepr export. You have to import the datastructures first!
|
||||
*/
|
||||
class PKPartImporter
|
||||
{
|
||||
use PKImportHelperTrait;
|
||||
|
||||
public function __construct(EntityManagerInterface $em, PropertyAccessorInterface $propertyAccessor)
|
||||
{
|
||||
$this->em = $em;
|
||||
$this->propertyAccessor = $propertyAccessor;
|
||||
}
|
||||
|
||||
public function importParts(array $data): int
|
||||
{
|
||||
if (!isset($data['part'])) {
|
||||
throw new \RuntimeException('$data must contain a "part" key!');
|
||||
}
|
||||
|
||||
|
||||
$part_data = $data['part'];
|
||||
foreach ($part_data as $part) {
|
||||
$entity = new Part();
|
||||
$entity->setName($part['name']);
|
||||
$entity->setDescription($part['description'] ?? '');
|
||||
//All parts get a tag, that they were imported from PartKeepr
|
||||
$entity->setTags('partkeepr-imported');
|
||||
$this->setAssociationField($entity, 'category', Category::class, $part['category_id']);
|
||||
|
||||
//If the part is a metapart, write that in the description, and we can skip the rest
|
||||
if ($part['metaPart'] === '1') {
|
||||
$entity->setDescription('Metapart (Not supported in Part-DB)');
|
||||
$entity->setComment('This part represents a former metapart in PartKeepr. It is not supported in Part-DB yet. And you can most likely delete it.');
|
||||
$entity->setTags('partkeepr-imported,partkeepr-metapart');
|
||||
} else {
|
||||
$entity->setMinAmount($part['minStockLevel'] ?? 0);
|
||||
if (!empty($part['internalPartNumber'])) {
|
||||
$entity->setIpn($part['internalPartNumber']);
|
||||
}
|
||||
$entity->setComment($part['comment'] ?? '');
|
||||
$entity->setNeedsReview($part['needsReview'] === '1');
|
||||
$this->setCreationDate($entity, $part['createDate']);
|
||||
|
||||
$this->setAssociationField($entity, 'footprint', Footprint::class, $part['footprint_id']);
|
||||
|
||||
//Set partUnit (when it is not ID=1, which is Pieces in Partkeepr)
|
||||
if ($part['partUnit_id'] !== '1') {
|
||||
$this->setAssociationField($entity, 'partUnit', MeasurementUnit::class, $part['partUnit_id']);
|
||||
}
|
||||
|
||||
//Create a part lot to store the stock level and location
|
||||
$lot = new PartLot();
|
||||
$lot->setAmount($part['stockLevel'] ?? 0);
|
||||
$this->setAssociationField($lot, 'storage_location', Storelocation::class, $part['storageLocation_id']);
|
||||
$entity->addPartLot($lot);
|
||||
|
||||
//For partCondition, productionsRemarks and Status, create a custom parameter
|
||||
if ($part['partCondition']) {
|
||||
$partCondition = (new PartParameter())->setName('Part Condition')->setGroup('PartKeepr')
|
||||
->setValueText($part['partCondition']);
|
||||
$entity->addParameter($partCondition);
|
||||
}
|
||||
if ($part['productionRemarks']) {
|
||||
$partCondition = (new PartParameter())->setName('Production Remarks')->setGroup('PartKeepr')
|
||||
->setValueText($part['productionRemarks']);
|
||||
$entity->addParameter($partCondition);
|
||||
}
|
||||
if ($part['status']) {
|
||||
$partCondition = (new PartParameter())->setName('Status')->setGroup('PartKeepr')
|
||||
->setValueText($part['status']);
|
||||
$entity->addParameter($partCondition);
|
||||
}
|
||||
}
|
||||
|
||||
$this->setIDOfEntity($entity, $part['id']);
|
||||
$this->em->persist($entity);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
|
||||
$this->importPartManufacturers($data);
|
||||
$this->importPartParameters($data);
|
||||
$this->importOrderdetails($data);
|
||||
|
||||
//Import attachments
|
||||
$this->importAttachments($data, 'partattachment', Part::class, 'part_id', PartAttachment::class);
|
||||
|
||||
return count($part_data);
|
||||
}
|
||||
|
||||
protected function importPartManufacturers(array $data): void
|
||||
{
|
||||
if (!isset($data['partmanufacturer'])) {
|
||||
throw new \RuntimeException('$data must contain a "partmanufacturer" key!');
|
||||
}
|
||||
|
||||
//Part-DB only supports one manufacturer per part, only the last one is imported
|
||||
$partmanufacturer_data = $data['partmanufacturer'];
|
||||
foreach ($partmanufacturer_data as $partmanufacturer) {
|
||||
/** @var Part $part */
|
||||
$part = $this->em->find(Part::class, (int) $partmanufacturer['part_id']);
|
||||
if (!$part) {
|
||||
throw new \RuntimeException(sprintf('Could not find part with ID %s', $partmanufacturer['part_id']));
|
||||
}
|
||||
$manufacturer = $this->em->find(Manufacturer::class, (int) $partmanufacturer['manufacturer_id']);
|
||||
if (!$manufacturer) {
|
||||
throw new \RuntimeException(sprintf('Could not find manufacturer with ID %s', $partmanufacturer['manufacturer_id']));
|
||||
}
|
||||
$part->setManufacturer($manufacturer);
|
||||
$part->setManufacturerProductNumber($partmanufacturer['partNumber']);
|
||||
}
|
||||
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
protected function importPartParameters(array $data): void
|
||||
{
|
||||
if (!isset($data['partparameter'])) {
|
||||
throw new \RuntimeException('$data must contain a "partparameter" key!');
|
||||
}
|
||||
|
||||
foreach ($data['partparameter'] as $partparameter) {
|
||||
$entity = new PartParameter();
|
||||
|
||||
//Name format: Name (Description)
|
||||
$name = $partparameter['name'];
|
||||
if (!empty($partparameter['description'])) {
|
||||
$name .= ' ('.$partparameter['description'].')';
|
||||
}
|
||||
$entity->setName($name);
|
||||
|
||||
$entity->setValueText($partparameter['stringValue'] ?? '');
|
||||
$entity->setUnit($this->getUnitSymbol($data, (int) $partparameter['unit_id']));
|
||||
|
||||
$entity->setValueMin($partparameter['normalizedMinValue'] ?? null);
|
||||
$entity->setValueTypical($partparameter['normalizedValue'] ?? null);
|
||||
$entity->setValueMax($partparameter['normalizedMaxValue'] ?? null);
|
||||
|
||||
$part = $this->em->find(Part::class, (int) $partparameter['part_id']);
|
||||
if (!$part) {
|
||||
throw new \RuntimeException(sprintf('Could not find part with ID %s', $partparameter['part_id']));
|
||||
}
|
||||
|
||||
$part->addParameter($entity);
|
||||
$this->em->persist($entity);
|
||||
}
|
||||
$this->em->flush();
|
||||
}
|
||||
|
||||
protected function importOrderdetails(array $data): void
|
||||
{
|
||||
if (!isset($data['partdistributor'])) {
|
||||
throw new \RuntimeException('$data must contain a "partdistributor" key!');
|
||||
}
|
||||
|
||||
foreach ($data['partdistributor'] as $partdistributor) {
|
||||
//Retrieve the part
|
||||
$part = $this->em->find(Part::class, (int) $partdistributor['part_id']);
|
||||
if (!$part) {
|
||||
throw new \RuntimeException(sprintf('Could not find part with ID %s', $partdistributor['part_id']));
|
||||
}
|
||||
//Retrieve the distributor
|
||||
$supplier = $this->em->find(Supplier::class, (int) $partdistributor['distributor_id']);
|
||||
if (!$supplier) {
|
||||
throw new \RuntimeException(sprintf('Could not find supplier with ID %s', $partdistributor['distributor_id']));
|
||||
}
|
||||
|
||||
//Check if the part already has an orderdetail for this supplier and ordernumber
|
||||
if (empty($partdistributor['orderNumber']) && !empty($partdistributor['sku'])) {
|
||||
$spn = $partdistributor['sku'];
|
||||
} elseif (!empty($partdistributor['orderNumber']) && empty($partdistributor['sku'])) {
|
||||
$spn = $partdistributor['orderNumber'];
|
||||
} elseif (!empty($partdistributor['orderNumber']) && !empty($partdistributor['sku'])) {
|
||||
$spn = $partdistributor['orderNumber'] . ' (' . $partdistributor['sku'] . ')';
|
||||
} else {
|
||||
$spn = 'PartKeepr Import';
|
||||
}
|
||||
|
||||
$orderdetail = $this->em->getRepository(Orderdetail::class)->findOneBy([
|
||||
'part' => $part,
|
||||
'supplier' => $supplier,
|
||||
'supplierpartnr' => $spn,
|
||||
]);
|
||||
|
||||
//When no orderdetail exists, create one
|
||||
if (!$orderdetail) {
|
||||
$orderdetail = new Orderdetail();
|
||||
$orderdetail->setSupplier($supplier);
|
||||
$orderdetail->setSupplierpartnr($spn);
|
||||
$part->addOrderdetail($orderdetail);
|
||||
}
|
||||
|
||||
//Add the price information to the orderdetail
|
||||
if (!empty($partdistributor['price'])) {
|
||||
$pricedetail = new Pricedetail();
|
||||
$orderdetail->addPricedetail($pricedetail);
|
||||
//Partkeepr stores the price per item, we need to convert it to the price per packaging unit
|
||||
$price_per_item = BigDecimal::of($partdistributor['price']);
|
||||
$pricedetail->setPrice($price_per_item->multipliedBy($partdistributor['packagingUnit']));
|
||||
$pricedetail->setPriceRelatedQuantity($partdistributor['packagingUnit'] ?? 1);
|
||||
}
|
||||
|
||||
//We have to flush the changes in every loop, so the find function can find newly created entities
|
||||
$this->em->flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the (parameter) unit symbol for the given ID.
|
||||
* @param array $data
|
||||
* @param int $id
|
||||
* @return string
|
||||
*/
|
||||
protected function getUnitSymbol(array $data, int $id): string
|
||||
{
|
||||
foreach ($data['unit'] as $unit) {
|
||||
if ((int) $unit['id'] === $id) {
|
||||
return $unit['symbol'];
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException(sprintf('Could not find unit with ID %s', $id));
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ declare(strict_types=1);
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\LabelSystem\Barcodes;
|
||||
namespace App\Services\LabelSystem;
|
||||
|
||||
use App\Entity\Base\AbstractStructuralDBElement;
|
||||
use App\Entity\Parts\Category;
|
||||
@@ -48,11 +48,12 @@ use App\Entity\Parts\Manufacturer;
|
||||
use App\Entity\Parts\Part;
|
||||
use App\Entity\Parts\PartLot;
|
||||
use App\Entity\Parts\Storelocation;
|
||||
use App\Entity\UserSystem\User;
|
||||
use DateTime;
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
|
||||
final class BarcodeExampleElementsGenerator
|
||||
final class LabelExampleElementsGenerator
|
||||
{
|
||||
public function getElement(string $type): object
|
||||
{
|
||||
@@ -102,6 +103,7 @@ final class BarcodeExampleElementsGenerator
|
||||
$lot->setExpirationDate(new DateTime('+1 days'));
|
||||
$lot->setStorageLocation($this->getStructuralData(Storelocation::class));
|
||||
$lot->setAmount(123);
|
||||
$lot->setOwner($this->getUser());
|
||||
|
||||
return $lot;
|
||||
}
|
||||
@@ -112,6 +114,8 @@ final class BarcodeExampleElementsGenerator
|
||||
$storelocation->setName('Location 1');
|
||||
$storelocation->setComment('Example comment');
|
||||
$storelocation->updateTimestamps();
|
||||
$storelocation->setOwner($this->getUser());
|
||||
|
||||
|
||||
$parent = new Storelocation();
|
||||
$parent->setName('Parent');
|
||||
@@ -121,6 +125,16 @@ final class BarcodeExampleElementsGenerator
|
||||
return $storelocation;
|
||||
}
|
||||
|
||||
private function getUser(): User
|
||||
{
|
||||
$user = new User();
|
||||
$user->setName('user');
|
||||
$user->setFirstName('John');
|
||||
$user->setLastName('Doe');
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function getStructuralData(string $class): AbstractStructuralDBElement
|
||||
{
|
||||
if (!is_a($class, AbstractStructuralDBElement::class, true)) {
|
||||
@@ -102,6 +102,14 @@ final class PartLotProvider implements PlaceholderProviderInterface
|
||||
return $label_target->getStorageLocation() ? $label_target->getStorageLocation()->getFullPath() : '';
|
||||
}
|
||||
|
||||
if ('[[OWNER]]' === $placeholder) {
|
||||
return $label_target->getOwner() ? $label_target->getOwner()->getFullName() : '';
|
||||
}
|
||||
|
||||
if ('[[OWNER_USERNAME]]' === $placeholder) {
|
||||
return $label_target->getOwner() ? $label_target->getOwner()->getUsername() : '';
|
||||
}
|
||||
|
||||
return $this->labelTextReplacer->handlePlaceholder($placeholder, $label_target->getPart());
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\LabelSystem\PlaceholderProviders;
|
||||
|
||||
use App\Entity\Parts\Storelocation;
|
||||
|
||||
class StorelocationProvider implements PlaceholderProviderInterface
|
||||
{
|
||||
public function replace(string $placeholder, object $label_target, array $options = []): ?string
|
||||
{
|
||||
if ($label_target instanceof Storelocation) {
|
||||
if ('[[OWNER]]' === $placeholder) {
|
||||
return $label_target->getOwner() ? $label_target->getOwner()->getFullName() : '';
|
||||
}
|
||||
|
||||
if ('[[OWNER_USERNAME]]' === $placeholder) {
|
||||
return $label_target->getOwner() ? $label_target->getOwner()->getUsername() : '';
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
60
src/Services/LogSystem/EventCommentNeededHelper.php
Normal file
60
src/Services/LogSystem/EventCommentNeededHelper.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\LogSystem;
|
||||
|
||||
/**
|
||||
* This service is used to check if a log change comment is needed for a given operation type.
|
||||
* It is configured using the "enforce_change_comments_for" config parameter.
|
||||
*/
|
||||
class EventCommentNeededHelper
|
||||
{
|
||||
protected array $enforce_change_comments_for;
|
||||
|
||||
public const VALID_OPERATION_TYPES = [
|
||||
'part_edit',
|
||||
'part_create',
|
||||
'part_delete',
|
||||
'part_stock_operation',
|
||||
'datastructure_edit',
|
||||
'datastructure_create',
|
||||
'datastructure_delete',
|
||||
];
|
||||
|
||||
public function __construct(array $enforce_change_comments_for)
|
||||
{
|
||||
$this->enforce_change_comments_for = $enforce_change_comments_for;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a log change comment is needed for the given operation type
|
||||
* @param string $comment_type
|
||||
* @return bool
|
||||
*/
|
||||
public function isCommentNeeded(string $comment_type): bool
|
||||
{
|
||||
//Check if the comment type is valid
|
||||
if (! in_array($comment_type, self::VALID_OPERATION_TYPES, true)) {
|
||||
throw new \InvalidArgumentException('The comment type "'.$comment_type.'" is not valid!');
|
||||
}
|
||||
|
||||
return in_array($comment_type, $this->enforce_change_comments_for, true);
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ namespace App\Services\LogSystem;
|
||||
|
||||
use App\Entity\LogSystem\AbstractLogEntry;
|
||||
use App\Entity\UserSystem\User;
|
||||
use App\Services\Misc\ConsoleInfoHelper;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
@@ -34,14 +35,17 @@ class EventLogger
|
||||
protected array $whitelist;
|
||||
protected EntityManagerInterface $em;
|
||||
protected Security $security;
|
||||
protected ConsoleInfoHelper $console_info_helper;
|
||||
|
||||
public function __construct(int $minimum_log_level, array $blacklist, array $whitelist, EntityManagerInterface $em, Security $security)
|
||||
public function __construct(int $minimum_log_level, array $blacklist, array $whitelist, EntityManagerInterface $em,
|
||||
Security $security, ConsoleInfoHelper $console_info_helper)
|
||||
{
|
||||
$this->minimum_log_level = $minimum_log_level;
|
||||
$this->blacklist = $blacklist;
|
||||
$this->whitelist = $whitelist;
|
||||
$this->em = $em;
|
||||
$this->security = $security;
|
||||
$this->console_info_helper = $console_info_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,6 +71,11 @@ class EventLogger
|
||||
$logEntry->setUser($user);
|
||||
}
|
||||
|
||||
//Set the console user info, if the log entry was created in a console command
|
||||
if ($this->console_info_helper->isCLI()) {
|
||||
$logEntry->setCLIUsername($this->console_info_helper->getCLIUser() ?? 'Unknown');
|
||||
}
|
||||
|
||||
if ($this->shouldBeAdded($logEntry)) {
|
||||
$this->em->persist($logEntry);
|
||||
|
||||
|
||||
63
src/Services/Misc/ConsoleInfoHelper.php
Normal file
63
src/Services/Misc/ConsoleInfoHelper.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Services\Misc;
|
||||
|
||||
class ConsoleInfoHelper
|
||||
{
|
||||
/**
|
||||
* Returns true if the current script is executed in a CLI environment.
|
||||
* @return bool true if the current script is executed in a CLI environment, false otherwise
|
||||
*/
|
||||
public function isCLI(): bool
|
||||
{
|
||||
return \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the username of the user who started the current script if possible.
|
||||
* @return string|null the username of the user who started the current script if possible, null otherwise
|
||||
*/
|
||||
public function getCLIUser(): ?string
|
||||
{
|
||||
if (!$this->isCLI()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//Try to use the posix extension if available (Linux)
|
||||
if (function_exists('posix_getpwuid') && function_exists('posix_geteuid')) {
|
||||
$user = posix_getpwuid(posix_geteuid());
|
||||
return $user['name'];
|
||||
}
|
||||
|
||||
//Try to retrieve the name via the environment variable Username (Windows)
|
||||
if (isset($_SERVER['USERNAME'])) {
|
||||
return $_SERVER['USERNAME'];
|
||||
}
|
||||
|
||||
//Try to retrieve the name via the environment variable USER (Linux)
|
||||
if (isset($_SERVER['USER'])) {
|
||||
return $_SERVER['USER'];
|
||||
}
|
||||
|
||||
//Otherwise we can't determine the username
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -170,7 +170,7 @@ class PricedetailHelper
|
||||
return null;
|
||||
}
|
||||
|
||||
return $avg->dividedBy($count)->toScale(Pricedetail::PRICE_PRECISION, RoundingMode::HALF_UP);
|
||||
return $avg->dividedBy($count, Pricedetail::PRICE_PRECISION, RoundingMode::HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,7 +46,7 @@ class NodesListBuilder
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a flattened hierachical tree. Useful for generating option lists.
|
||||
* Gets a flattened hierarchical tree. Useful for generating option lists.
|
||||
* In difference to the Repository Function, the results here are cached.
|
||||
*
|
||||
* @param string $class_name the class name of the entity you want to retrieve
|
||||
@@ -66,6 +66,7 @@ class NodesListBuilder
|
||||
$item->tag(['groups', 'tree_list', $this->keyGenerator->generateKey(), $secure_class_name]);
|
||||
/** @var StructuralDBElementRepository $repo */
|
||||
$repo = $this->em->getRepository($class_name);
|
||||
|
||||
return $repo->toNodesList($parent);
|
||||
});
|
||||
}
|
||||
|
||||
43
src/Twig/MiscExtension.php
Normal file
43
src/Twig/MiscExtension.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
/*
|
||||
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||
*
|
||||
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Services\LogSystem\EventCommentNeededHelper;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
|
||||
final class MiscExtension extends AbstractExtension
|
||||
{
|
||||
private EventCommentNeededHelper $eventCommentNeededHelper;
|
||||
|
||||
public function __construct(EventCommentNeededHelper $eventCommentNeededHelper)
|
||||
{
|
||||
$this->eventCommentNeededHelper = $eventCommentNeededHelper;
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new \Twig\TwigFunction('event_comment_needed',
|
||||
fn(string $operation_type) => $this->eventCommentNeededHelper->isCommentNeeded($operation_type)
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,8 @@
|
||||
</button>
|
||||
<div class="dropdown-menu p-2">
|
||||
<div class="form-group"><label for="delete_log_comment">{% trans %}edit.log_comment{% endtrans %}</label>
|
||||
<input type="text" id="delete_log_comment" name="log_comment" class="form-control">
|
||||
<input type="text" id="delete_log_comment" name="log_comment" class="form-control"
|
||||
{% if event_comment_needed('datastructure_delete') %}required{% endif %}>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
<i class="fas fa-balance-scale fa-fw"></i> {% trans %}measurement_unit.caption{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block edit_title %}
|
||||
{% trans %}measurement_unit.edit{% endtrans %}: {{ entity.name }}
|
||||
{% endblock %}
|
||||
|
||||
{% block new_title %}
|
||||
{% trans %}measurement_unit.new{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_controls %}
|
||||
{{ form_row(form.unit) }}
|
||||
{{ form_row(form.is_integer) }}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
{{ form_row(form.is_full) }}
|
||||
{{ form_row(form.limit_to_existing_parts) }}
|
||||
{{ form_row(form.only_single_part) }}
|
||||
{{ form_row(form.owner) }}
|
||||
{{ form_row(form.part_owner_must_match) }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
{{ form_row(form.first_name) }}
|
||||
{{ form_row(form.last_name) }}
|
||||
{{ form_row(form.email) }}
|
||||
{{ form_row(form.showEmailOnProfile) }}
|
||||
{{ form_row(form.department) }}
|
||||
{{ form_row(form.aboutMe) }}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_panes %}
|
||||
@@ -33,9 +35,11 @@
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="password">
|
||||
<div class="offset-3 mb-3 col-9">
|
||||
<span class="badge badge-warning bg-warning"><i class="fa-solid fa-house-user"></i> {% trans %}user.saml_user{% endtrans %}</span>
|
||||
</div>
|
||||
{% if entity.samlUser %}
|
||||
<div class="offset-3 mb-3 col-9">
|
||||
<span class="badge badge-warning bg-warning"><i class="fa-solid fa-house-user"></i> {% trans %}user.saml_user{% endtrans %}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{{ form_row(form.new_password) }}
|
||||
{{ form_row(form.need_pw_change) }}
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
{% macro string_to_tags(string, class="badge bg-info") %}
|
||||
{% for tag in string|split(',') %}
|
||||
<a href="{{ path('part_list_tags', {'tag': tag | trim | url_encode}) }}" class="{{ class }}" >{{ tag | trim }}</a>
|
||||
<a href="{{ path('part_list_tags', {'tag': tag | trim}) }}" class="{{ class }}" >{{ tag | trim }}</a>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
|
||||
@@ -159,11 +159,7 @@
|
||||
{% endif %}
|
||||
|
||||
{% if user is not null %}
|
||||
{% if user.fullName is not empty %}
|
||||
({{ _self.user_icon(user) }} <a href="{{ path('user_info', {"id": user.id}) }}" title="@{{ user.name }}">{{ user.fullName }}</a>)
|
||||
{% else %}
|
||||
({{ _self.user_icon(user) }} <a href="{{ path('user_info', {"id": user.id}) }}" title="@{{ user.name }}">@{{ user.name }}</a>)
|
||||
{% endif %}
|
||||
({{ _self.user_icon_link(user) }})
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
@@ -180,6 +176,14 @@
|
||||
<img src="{{ avatar_helper.avatarSmURL(user) }}" class="avatar-xs" alt="User avatar" {{ stimulus_controller('elements/hoverpic') }} data-thumbnail="{{ avatar_helper.avatarMdURL(user) }}">
|
||||
{% endmacro %}
|
||||
|
||||
{% macro user_icon_link(user) %}
|
||||
{% if user.fullName is not empty %}
|
||||
{{ _self.user_icon(user) }} <a href="{{ path('user_info', {"id": user.id}) }}" title="@{{ user.name }}">{{ user.fullName }}</a>
|
||||
{% else %}
|
||||
{{ _self.user_icon(user) }} <a href="{{ path('user_info', {"id": user.id}) }}" title="@{{ user.name }}">@{{ user.name }}</a>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% macro entity_preview_sm(entity) %}
|
||||
{# @var entity \App\Entity\Contracts\HasMasterAttachmentInterface #}
|
||||
{% if entity.masterPictureAttachment and entity.masterPictureAttachment.picture and attachment_manager.fileExisting(entity.masterPictureAttachment) %}
|
||||
|
||||
@@ -106,9 +106,9 @@
|
||||
{{ form_widget(form.save_and_clone, {'attr': {'class': 'dropdown-item'}}) }}
|
||||
{{ form_widget(form.save_and_new, {'attr': {'class': 'dropdown-item'}}) }}
|
||||
<div class="dropdown-divider"></div>
|
||||
<div class="p-2">
|
||||
|
||||
{{ form_row(form.log_comment)}}
|
||||
<div class="px-2">
|
||||
<label class="form-label">{% trans %}edit.log_comment{% endtrans %}</label>
|
||||
{{ form_widget(form.log_comment)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,13 @@
|
||||
{% endif %}
|
||||
{% if part.manufacturerProductUrl %}
|
||||
<small>
|
||||
<a class="link-external" href="{{ part.manufacturerProductUrl }}" rel="noopener" target="_blank">{{ part.manufacturerProductNumber }}</a>
|
||||
<a class="link-external" href="{{ part.manufacturerProductUrl }}" rel="noopener" target="_blank">
|
||||
{% if part.manufacturerProductNumber is not empty %}
|
||||
{{ part.manufacturerProductNumber }}
|
||||
{% else %}
|
||||
<i>{{ part.name }}</i>
|
||||
{% endif %}
|
||||
</a>
|
||||
</small>
|
||||
{% else %}
|
||||
<small>{{ part.manufacturerProductNumber }}</small>
|
||||
@@ -35,11 +41,17 @@
|
||||
<span class="text-muted">{{ helper.structural_entity_link(part.category) }}</span>
|
||||
</h6>
|
||||
<h6><i class="fas fa-shapes fa-fw"></i>
|
||||
<span class="text-muted">
|
||||
<span title="{% trans %}instock.label{% endtrans %}">{{ part.amountSum | format_amount(part.partUnit) }}</span>
|
||||
/
|
||||
<span title="{% trans %}mininstock.label{% endtrans %}">{{ part.minAmount | format_amount(part.partUnit) }}</span>
|
||||
</span>
|
||||
<span class="{% if part.notEnoughInstock %}text-danger font-weight-bold{% else %}text-muted{% endif %}">
|
||||
<span title="{% trans %}instock.label{% endtrans %}">{{ part.amountSum | format_amount(part.partUnit) }}</span>
|
||||
{% if part.expiredAmountSum > 0 %}
|
||||
<span title="{% trans %}part_lots.is_expired{% endtrans %}" class="text-muted">(+{{ part.expiredAmountSum }})</span>
|
||||
{% endif %}
|
||||
/
|
||||
<span title="{% trans %}mininstock.label{% endtrans %}">{{ part.minAmount | format_amount(part.partUnit) }}</span>
|
||||
</span>
|
||||
{% if part.notEnoughInstock %}
|
||||
<span class="badge badge-warning bg-warning rounded-pill"><i class="fa-solid fa-less-than-equal"></i> {% trans %}part.info.amount.less_than_desired{% endtrans %}</span>
|
||||
{% endif %}
|
||||
</h6>
|
||||
<h6 class="">
|
||||
<i class="fas fa-microchip fa-fw" title="{% trans %}footprint.label{% endtrans %}"></i>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user