diff --git a/_define.php b/_define.php index 82914db7..d5deb590 100644 --- a/_define.php +++ b/_define.php @@ -20,6 +20,7 @@ acls: [ 'objectslend_preferences' => 'admin', 'store_objectlend_preferences' => 'admin', + 'objectslend_sample_data' => 'admin', 'objectslend_category_add' => 'staff', 'objectslend_category_edit' => 'staff', 'objectslend_category_action_add' => 'staff', @@ -56,5 +57,5 @@ 'objectslend_object_dotake' => 'member', 'objectslend_object_doreturn' => 'member' ], - dbver: 1.00 + dbver: 1.1 ); diff --git a/_routes.php b/_routes.php index 21a47ffa..49c2af42 100644 --- a/_routes.php +++ b/_routes.php @@ -32,6 +32,11 @@ [MainController::class, 'storePreferences'] )->setName('store_objectlend_preferences')->add(Authenticate::class); +$app->post( + '/sample-data', + [MainController::class, 'loadSampleData'] +)->setName('objectslend_sample_data')->add(Authenticate::class); + $app->get( '/category/add', [CategoriesController::class, 'add'] diff --git a/lib/GaletteObjectsLend/Controllers/Crud/CategoriesController.php b/lib/GaletteObjectsLend/Controllers/Crud/CategoriesController.php index fdb274d5..f12f0520 100644 --- a/lib/GaletteObjectsLend/Controllers/Crud/CategoriesController.php +++ b/lib/GaletteObjectsLend/Controllers/Crud/CategoriesController.php @@ -15,7 +15,7 @@ use GaletteObjectsLend\Entity\CategoryPicture; use GaletteObjectsLend\Entity\LendCategory; use GaletteObjectsLend\Entity\LendStatus; -use GaletteObjectsLend\Entity\Preferences; +use GaletteObjectsLend\LendPreferences; use GaletteObjectsLend\Filters\CategoriesList; use GaletteObjectsLend\Repository\Categories; use Slim\Psr7\Request; @@ -79,7 +79,7 @@ protected function getListView(Pagination $filters): array 'page_title' => _T("Categories list", "objectslend"), 'categories' => $list, 'nb_categories' => count($list), - 'olendsprefs' => new Preferences($this->zdb), + 'olendsprefs' => new LendPreferences($this->preferences), 'time' => time() ] ]; @@ -126,7 +126,7 @@ protected function getEditView(LendCategory|LendStatus $entity, string $action): : _T("New category", "objectslend"), 'category' => $entity, 'time' => time(), - 'olendsprefs' => new Preferences($this->zdb), + 'olendsprefs' => new LendPreferences($this->preferences), 'picture' => new CategoryPicture($entity->getId()) ] ]; @@ -144,7 +144,8 @@ protected function getEditView(LendCategory|LendStatus $entity, string $action): protected function afterStore(LendCategory|LendStatus $entity, Request $request, array $post): array { $errors = []; - $picture = new CategoryPicture($entity->getId()); + $picture = (new CategoryPicture($entity->getId())) + ->setMaxLength((new LendPreferences($this->preferences))->getUploadSize()); if (!$picture->upload($request->getUploadedFiles(), 'picture')) { $errors = $picture->uploadErrors(); } diff --git a/lib/GaletteObjectsLend/Controllers/Crud/ObjectsController.php b/lib/GaletteObjectsLend/Controllers/Crud/ObjectsController.php index 54be7a61..11777058 100644 --- a/lib/GaletteObjectsLend/Controllers/Crud/ObjectsController.php +++ b/lib/GaletteObjectsLend/Controllers/Crud/ObjectsController.php @@ -21,7 +21,7 @@ use GaletteObjectsLend\Repository\Status; use GaletteObjectsLend\Entity\LendObject; use GaletteObjectsLend\Entity\LendRent; -use GaletteObjectsLend\Entity\Preferences; +use GaletteObjectsLend\LendPreferences; use GaletteObjectsLend\LendException; use GaletteObjectsLend\LendService; use Galette\Controllers\Crud\AbstractPluginController; @@ -126,7 +126,7 @@ public function list(Request $request, Response $response, ?string $option = nul } } - $lendsprefs = new Preferences($this->zdb); + $lendsprefs = new LendPreferences($this->preferences); $objects = new Objects($this->zdb, $this->preferences, $this->login, $lendsprefs, $filters); $list = $objects->getObjectsList(true); @@ -156,7 +156,7 @@ public function list(Request $request, Response $response, ?string $option = nul 'objects' => $list, 'nb_objects' => count($list), 'filters' => $filters, - 'lendsprefs' => $lendsprefs->getPreferences(), + 'lendsprefs' => $lendsprefs->toArray(), 'olendsprefs' => $lendsprefs, 'time' => time(), 'module_id' => $this->getModuleId(), @@ -314,14 +314,14 @@ public function edit(Request $request, Response $response, ?int $id = null, stri $statuses = new Status($this->zdb, $this->preferences, $this->login, $sfilter); $slist = $statuses->getStatusList(true); - $lendsprefs = new Preferences($this->zdb); + $lendsprefs = new LendPreferences($this->preferences); $params = [ 'page_title' => $title, 'object' => $object, 'rents' => $object->getId() !== null ? (new Rents($this->zdb))->getForObject($object->getId()) : [], 'time' => time(), 'action' => $action, - 'lendsprefs' => $lendsprefs->getPreferences(), + 'lendsprefs' => $lendsprefs->toArray(), 'olendsprefs' => $lendsprefs, 'categories' => $categories_list, 'statuses' => $slist, @@ -392,6 +392,7 @@ public function doEdit(Request $request, Response $response, ?int $id = null, st } // picture upload + $object->getPicture()->setMaxLength((new LendPreferences($this->preferences))->getUploadSize()); if (!$object->getPicture()->upload($request->getUploadedFiles(), 'picture')) { $error_detected = $object->getPicture()->uploadErrors(); } @@ -592,7 +593,7 @@ public function doClone(Request $request, Response $response, int $id): Response */ public function lend(Request $request, Response $response, string $action, int $id): Response { - $lendsprefs = new Preferences($this->zdb); + $lendsprefs = new LendPreferences($this->preferences); $params = [ 'page_title' => ( @@ -604,7 +605,7 @@ public function lend(Request $request, Response $response, string $action, int $ 'statuses' => ($action == 'take' ? (new Status($this->zdb, $this->preferences, $this->login))->getActiveTakeAwayStatuses() : (new Status($this->zdb, $this->preferences, $this->login))->getActiveStockStatuses()), - 'lendsprefs' => $lendsprefs->getPreferences(), + 'lendsprefs' => $lendsprefs->toArray(), 'olendsprefs' => $lendsprefs, 'ajax' => $this->isAjax($request), 'takeorgive' => $action, @@ -826,11 +827,11 @@ private function lendResponse(Request $request, Response $response): Response /** * Get lend service * - * @param ?Preferences $lendsprefs Plugin preferences, loaded if not provided + * @param ?LendPreferences $lendsprefs Plugin preferences, loaded if not provided */ - private function getLendService(?Preferences $lendsprefs = null): LendService + private function getLendService(?LendPreferences $lendsprefs = null): LendService { - return new LendService($this->zdb, $this->preferences, $this->login, $lendsprefs ?? new Preferences($this->zdb)); + return new LendService($this->zdb, $this->preferences, $this->login, $lendsprefs ?? new LendPreferences($this->preferences)); } // /CRUD - Update @@ -915,7 +916,7 @@ public function confirmRemoveTitle(array $args): string protected function doDelete(array $args, array $post): bool { $filters = $this->getFilters(); - $lendsprefs = new Preferences($this->zdb); + $lendsprefs = new LendPreferences($this->preferences); $objects = new Objects($this->zdb, $this->preferences, $this->login, $lendsprefs, $filters); if (!is_array($post['id'])) { diff --git a/lib/GaletteObjectsLend/Controllers/ImagesController.php b/lib/GaletteObjectsLend/Controllers/ImagesController.php index e7349b0e..e0ede265 100644 --- a/lib/GaletteObjectsLend/Controllers/ImagesController.php +++ b/lib/GaletteObjectsLend/Controllers/ImagesController.php @@ -11,7 +11,7 @@ namespace GaletteObjectsLend\Controllers; use Galette\Controllers\ImagesController as GImagesController; -use GaletteObjectsLend\Entity\Preferences; +use GaletteObjectsLend\LendPreferences; use Slim\Psr7\Request; use Slim\Psr7\Response; @@ -23,7 +23,7 @@ class ImagesController extends GImagesController { - private Preferences $lendsprefs; + private LendPreferences $lendsprefs; /** * Objects lends category or object route @@ -40,14 +40,8 @@ public function lendPicture(Request $request, Response $response, string $type, . ($type == 'category' ? 'CategoryPicture' : 'ObjectPicture'); $picture = new $class($id); - $this->lendsprefs = new Preferences($this->zdb); - $thumb = false; - if (!$this->lendsprefs->showFullsize() || $mode == 'thumbnail') { - //force thumbnail display from preferences - $thumb = true; - } - - if ($thumb) { + $this->lendsprefs = new LendPreferences($this->preferences); + if ($mode == 'thumbnail') { return $picture->displayThumb($response, $this->lendsprefs); } else { return $picture->display($response); diff --git a/lib/GaletteObjectsLend/Controllers/MainController.php b/lib/GaletteObjectsLend/Controllers/MainController.php index 2c903033..658ba2d6 100644 --- a/lib/GaletteObjectsLend/Controllers/MainController.php +++ b/lib/GaletteObjectsLend/Controllers/MainController.php @@ -10,10 +10,12 @@ namespace GaletteObjectsLend\Controllers; +use Analog\Analog; use DI\Attribute\Inject; use Galette\Controllers\AbstractPluginController; use Galette\Entity\ContributionsTypes; -use GaletteObjectsLend\Entity\Preferences; +use GaletteObjectsLend\LendPreferences; +use GaletteObjectsLend\SampleData; use Slim\Psr7\Request; use Slim\Psr7\Response; @@ -39,25 +41,13 @@ class MainController extends AbstractPluginController */ public function preferences(Request $request, Response $response): Response { - if ($this->session->objectslend_preferences !== null) { - $lendsprefs = $this->session->objectslend_preferences; - $this->session->objectslend_preferences = null; - } else { - $lendsprefs = new Preferences($this->zdb); - } - $ctypes = new ContributionsTypes($this->zdb); - $types_list = $ctypes->getList(); - $ctypes_params = [0 => _T("Choose a contribution type", "objectslend")]; - foreach ($types_list as $id => $type) { - $ctypes_params[$id] = $type['label']; - } $params = [ 'page_title' => _T('ObjectsLend preferences', 'objectslend'), 'type_cotis_options' => $ctypes->getList(), - 'ctypes' => $ctypes_params, - 'lendsprefs' => $lendsprefs->getPreferences() + 'lendsprefs' => (new LendPreferences($this->preferences))->toArray(), + 'sample_data' => !(new SampleData($this->zdb))->hasObjects() ]; // display page @@ -70,7 +60,10 @@ public function preferences(Request $request, Response $response): Response } /** - * Objects lends preferences + * Store objects lends preferences + * + * Only declared preferences are read from the request. A yes/no one + * missing from it is an unchecked box, and is set off. * * @param Request $request PSR Request * @param Response $response PSR Response @@ -78,29 +71,82 @@ public function preferences(Request $request, Response $response): Response public function storePreferences(Request $request, Response $response): Response { $post = $request->getParsedBody(); - $lendsprefs = new Preferences($this->zdb); + $booleans = LendPreferences::getBooleans(); + + $stored = true; + $errors = []; + foreach (array_keys(LendPreferences::getSchema()) as $name) { + if (isset($booleans[$name])) { + $value = (int)isset($post[$name]); + } elseif (isset($post[$name])) { + $value = trim((string)$post[$name]); + } else { + continue; + } + + if (!$this->preferences->setValue($name, $value, $this->login)) { + $stored = false; + $errors = array_merge($errors, $this->preferences->getErrors()); + } + } - $error_detected = []; - if ($lendsprefs->store($post)) { + if ($stored) { $this->flash->addMessage( 'success_detected', _T("Preferences have been successfully stored!", "objectslend") ); } else { - $this->session->objectslend_preferences = $lendsprefs; - foreach ($error_detected as $error) { - $this->flash->addMessage( - 'error_detected', - $error - ); + foreach (array_unique($errors) as $error) { + $this->flash->addMessage('error_detected', $error); } } return $response - ->withStatus(301) + ->withStatus(302) ->withHeader( 'Location', $this->routeparser->urlFor('objectslend_preferences') ); } + + /** + * Load sample data into an empty catalog + * + * @param Request $request PSR Request + * @param Response $response PSR Response + */ + public function loadSampleData(Request $request, Response $response): Response + { + $sample = new SampleData($this->zdb); + + if ($sample->hasObjects()) { + $this->flash->addMessage( + 'error_detected', + _T("Sample data can only be loaded into an empty catalog.", "objectslend") + ); + } else { + try { + $created = $sample->load($sample->getMembers()); + $this->flash->addMessage( + 'success_detected', + sprintf( + //TRANS: %1$d is the number of objects, %2$d the number of rents + _T('Sample data loaded: %1$d objects, %2$d rents.', 'objectslend'), + $created['objects'], + $created['rents'] + ) + ); + } catch (\Throwable $e) { + Analog::log('Unable to load sample data | ' . $e->getMessage(), Analog::ERROR); + $this->flash->addMessage( + 'error_detected', + _T("Sample data could not be loaded.", "objectslend") + ); + } + } + + return $response + ->withStatus(302) + ->withHeader('Location', $this->routeparser->urlFor('objectslend_objects')); + } } diff --git a/lib/GaletteObjectsLend/Controllers/PdfController.php b/lib/GaletteObjectsLend/Controllers/PdfController.php index b5b8b7ac..d835a8a2 100644 --- a/lib/GaletteObjectsLend/Controllers/PdfController.php +++ b/lib/GaletteObjectsLend/Controllers/PdfController.php @@ -12,7 +12,7 @@ use Galette\Controllers\PdfController as GPdfController; use GaletteObjectsLend\Controllers\Crud\ObjectsController; -use GaletteObjectsLend\Entity\Preferences; +use GaletteObjectsLend\LendPreferences; use GaletteObjectsLend\Filters\ObjectsList; use GaletteObjectsLend\Repository\Objects; use GaletteObjectsLend\IO\PdfObject; @@ -37,7 +37,7 @@ class PdfController extends GPdfController */ public function printObject(Request $request, Response $response, int $id): Response { - $lendsprefs = new Preferences($this->zdb); + $lendsprefs = new LendPreferences($this->preferences); $object = (new Objects($this->zdb, $this->preferences, $this->login, $lendsprefs))->getWithCurrentRent($id); $pdf = new PdfObject( @@ -57,7 +57,7 @@ public function printObject(Request $request, Response $response, int $id): Resp */ public function printObjects(Request $request, Response $response): Response { - $lendsprefs = new Preferences($this->zdb); + $lendsprefs = new LendPreferences($this->preferences); $filters = clone ($this->session->{$this->getFilterName(ObjectsController::getDefaultFilterName())} ?? new ObjectsList()); diff --git a/lib/GaletteObjectsLend/Entity/Picture.php b/lib/GaletteObjectsLend/Entity/Picture.php index 35c1d4d1..51fff859 100644 --- a/lib/GaletteObjectsLend/Entity/Picture.php +++ b/lib/GaletteObjectsLend/Entity/Picture.php @@ -11,6 +11,7 @@ namespace GaletteObjectsLend\Entity; use Analog\Analog; +use GaletteObjectsLend\LendPreferences; use Psr\Http\Message\UploadedFileInterface; use Slim\Psr7\Response; use Slim\Psr7\Stream; @@ -64,6 +65,17 @@ public function __construct(mixed $objectid = null) parent::__construct($objectid); } + /** + * Set maximum size of an uploaded picture + * + * @param int $maxlength Maximum size, in Ko + */ + public function setMaxLength(int $maxlength): self + { + $this->maxlength = $maxlength; + return $this; + } + /** * Gets the default picture to show, anyway * @@ -82,10 +94,10 @@ protected function getDefaultPicture(): void /** * Display a thumbnail image, create it if necessary * - * @param Response $response Response - * @param Preferences $prefs Preferences instance + * @param Response $response Response + * @param LendPreferences $prefs Plugin preferences */ - public function displayThumb(Response $response, Preferences $prefs): Response + public function displayThumb(Response $response, LendPreferences $prefs): Response { $this->setThumbSizes($prefs); $response = $response->withHeader('Content-Type', $this->mime) @@ -269,9 +281,9 @@ public function getThumbPath(): string * * Should override Picture::setSize(), but this one is private :/ * - * @param Preferences $prefs Preferences instance + * @param LendPreferences $prefs Plugin preferences */ - private function setThumbSizes(Preferences $prefs): void + private function setThumbSizes(LendPreferences $prefs): void { $thumb = $this->getThumbPath(); $this->thumb_max_width = $prefs->getThumbWidth(); @@ -313,11 +325,11 @@ private function setThumbSizes(Preferences $prefs): void /** * Returns current thumbnail optimal height * - * @param Preferences $prefs Preferences instance + * @param LendPreferences $prefs Plugin preferences * * @return int optimal height */ - public function getOptimalThumbHeight(Preferences $prefs): int + public function getOptimalThumbHeight(LendPreferences $prefs): int { if (!isset($this->thumb_optimal_height)) { $this->setThumbSizes($prefs); @@ -328,11 +340,11 @@ public function getOptimalThumbHeight(Preferences $prefs): int /** * Returns current thumbnail optimal width * - * @param Preferences $prefs Preferences instance + * @param LendPreferences $prefs Plugin preferences * * @return int optimal width */ - public function getOptimalThumbWidth(Preferences $prefs): int + public function getOptimalThumbWidth(LendPreferences $prefs): int { if (!isset($this->thumb_optimal_width)) { $this->setThumbSizes($prefs); diff --git a/lib/GaletteObjectsLend/Entity/Preferences.php b/lib/GaletteObjectsLend/Entity/Preferences.php deleted file mode 100644 index 22080fd8..00000000 --- a/lib/GaletteObjectsLend/Entity/Preferences.php +++ /dev/null @@ -1,312 +0,0 @@ - - * @author Johan Cwiklinski - * - * @property bool $ENABLE_MEMBER_RENT_OBJECT - * @property bool $AUTO_GENERATE_CONTRIBUTION - * @property int $GENERATED_CONTRIBUTION_TYPE_ID - * @property string $GENERATED_CONTRIB_INFO_TEXT - * @property bool $VIEW_CATEGORY - * @property bool $VIEW_SERIAL - * @property bool $VIEW_THUMBNAIL - * @property bool $VIEW_DESCRIPTION - * @property bool $VIEW_PRICE - * @property bool $VIEW_DIMENSION - * @property bool $VIEW_WEIGHT - * @property bool $VIEW_LEND_PRICE - * @property bool $VIEW_DATE_FORECAST - * @property bool $VIEW_LIST_PRICE_SUM - * @property int $THUMB_MAX_WIDTH - * @property int $THUMB_MAX_HEIGHT - */ -class Preferences -{ - public const string TABLE = 'parameters'; - public const string PK = 'code'; - - private Db $zdb; - /** @var array */ - private array $prefs = []; - - /** - * Show categories at the top of the objects list - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_CATEGORY = 'VIEW_CATEGORY'; - - /** - * Show serial number column - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_SERIAL = 'VIEW_SERIAL'; - - /** - * Show thumbnail column - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_THUMBNAIL = 'VIEW_THUMBNAIL'; - - /** - * Show description column - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_DESCRIPTION = 'VIEW_DESCRIPTION'; - - /** - * Show price column - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_PRICE = 'VIEW_PRICE'; - - /** - * Show dimensions column - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_DIMENSION = 'VIEW_DIMENSION'; - - /** - * Show weight column - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_WEIGHT = 'VIEW_WEIGHT'; - - /** - * Show rent price column - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_LEND_PRICE = 'VIEW_LEND_PRICE'; - - /** - * Show previsional return date column - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_DATE_FORECAST = 'VIEW_DATE_FORECAST'; - - /** - * Show the sum of prices on the list of objects - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_VIEW_LIST_PRICE_SUM = 'VIEW_LIST_PRICE_SUM'; - - /** - * Maximum width of a thumbnail (applied to objects/categories) - * Valeur : largeur en pixels - */ - public const string PARAM_THUMB_MAX_WIDTH = 'THUMB_MAX_WIDTH'; - - /** - * Maximum height of a thumbnail (applied to objects/categories) - * Valeur : largeur en pixels - */ - public const string PARAM_THUMB_MAX_HEIGHT = 'THUMB_MAX_HEIGHT'; - - /** - * Generate automatically a contribution when an object is rented - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_AUTO_GENERATE_CONTRIBUTION = 'AUTO_GENERATE_CONTRIBUTION'; - - /** - * Contribution ID to generate when an object is rented - * Valeur : ID du type de contribution - */ - public const string PARAM_GENERATED_CONTRIBUTION_TYPE_ID = 'GENERATED_CONTRIBUTION_TYPE_ID'; - - /** - * Text for the contribution - * Valeur : texte d'info à mettre avec des placeholders à remplacer - */ - public const string PARAM_GENERATED_CONTRIB_INFO_TEXT = 'GENERATED_CONTRIB_INFO_TEXT'; - - /** - * Allow non staff members to rent objects - * Valeur : 0 = false / 1 = true - */ - public const string PARAM_ENABLE_MEMBER_RENT_OBJECT = 'ENABLE_MEMBER_RENT_OBJECT'; - - /** - * Default constructor - * - * @param Db $zdb Db instance - * @param bool $load Automatically load preferences on load - * - * @return void - */ - public function __construct(Db $zdb, bool $load = true) - { - $this->zdb = $zdb; - if ($load) { - $this->load(); - } - } - - /** - * Get preferences - * - * @return array - */ - public function getPreferences(): array - { - if (count($this->prefs) == 0) { - $this->load(); - } - return $this->prefs; - } - - /** - * Global getter method - * - * @param string $name name of the property we want to retrieve - * - * @return mixed the called property - */ - public function __get(string $name): mixed - { - if (isset($this->prefs[$name])) { - return $this->prefs[$name]; - } - - $msg = __CLASS__ . '::' . $name . ' is not set'; - Analog::log( - $msg, - Analog::INFO - ); - throw new \RuntimeException($msg); - } - - /** - * Store preferences - * - * @param array $data Posted data - */ - public function store(array $data): bool - { - foreach ($data as $key => $value) { - $this->prefs[$key] = $value; - } - - $need_transaction = !$this->zdb->inTransaction(); - try { - if ($need_transaction) { - $this->zdb->beginTransaction(); - } - $update = $this->zdb->update(LEND_PREFIX . self::TABLE); - $update->set( - [ - 'value_text' => $data['GENERATED_CONTRIB_INFO_TEXT'], - 'date_modification' => date('Y-m-d H:i:s') - ] - )->where->equalTo(self::PK, 'GENERATED_CONTRIB_INFO_TEXT'); - $this->zdb->execute($update); - - $update = $this->zdb->update(LEND_PREFIX . self::TABLE); - $update->set( - [ - 'value_numeric' => ':value_numeric', - 'date_modification' => ':date_modification' - ] - )->where->equalTo(self::PK, ':' . self::PK); - $stmt = $this->zdb->sql->prepareStatementForSqlObject($update); - - unset($data['GENERATED_CONTRIB_INFO_TEXT']); - foreach ($data as $key => $value) { - $stmt->execute( - [ - 'value_numeric' => $value, - 'date_modification' => date('Y-m-d H:i:s'), - self::PK => $key - ] - ); - } - - if ($need_transaction) { - $this->zdb->commit(); - } - return true; - } catch (\Exception $e) { - if ($need_transaction) { - $this->zdb->rollback(); - } - throw $e; - } - } - - /** - * Load current preferences from database. - */ - public function load(): bool - { - $this->prefs = []; - - try { - $result = $this->zdb->selectAll(LEND_PREFIX . self::TABLE); - foreach ($result as $pref) { - $pk_field = self::PK; - $value_field = 'value_numeric'; - if ($pref->is_text == '1') { - $value_field = 'value_text'; - } - $this->prefs[$pref->$pk_field] = $pref->$value_field; - } - return true; - } catch (\Exception $e) { - Analog::log( - 'ObjectsLend Preferences cannot be loaded. Plugin should not work without ' - . 'it. Exiting. ' . $e->getMessage(), - Analog::URGENT - ); - return false; - } - } - - /** - * Get thumbnail maximum with - */ - public function getThumbWidth(): int - { - return (int)$this->prefs['THUMB_MAX_WIDTH']; - } - - /** - * Get thumbnail maximum height - */ - public function getThumbHeight(): int - { - return (int)$this->prefs['THUMB_MAX_HEIGHT']; - } - - /** - * Whether to display images (as thumbnails) in lists - */ - public function imagesInLists(): bool - { - return (bool)$this->prefs['VIEW_THUMBNAIL']; - } - - /** - * Shall we show fullsize images? - * - * Would appear editing object, and on thumbnails click - */ - public function showFullsize(): bool - { - return (bool)$this->prefs['VIEW_FULLSIZE']; - } -} diff --git a/lib/GaletteObjectsLend/Filters/ObjectsList.php b/lib/GaletteObjectsLend/Filters/ObjectsList.php index bbc0967f..ef935ac8 100644 --- a/lib/GaletteObjectsLend/Filters/ObjectsList.php +++ b/lib/GaletteObjectsLend/Filters/ObjectsList.php @@ -12,7 +12,7 @@ use Analog\Analog; use Galette\Core\Pagination; -use GaletteObjectsLend\Entity\Preferences; +use GaletteObjectsLend\LendPreferences; use GaletteObjectsLend\Repository\Objects; use Slim\Views\Twig; @@ -163,21 +163,19 @@ private function warnType(string $name, string $expected, mixed $value): void /** * Set commons filters for templates * - * @param \GaletteObjectsLend\Entity\Preferences $prefs Preferences instance - * @param Twig $view Template reference + * @param LendPreferences $prefs Plugin preferences + * @param Twig $view Template reference */ - public function setViewCommonsFilters(Preferences $prefs, Twig $view): void + public function setViewCommonsFilters(LendPreferences $prefs, Twig $view): void { - $prefs = $prefs->getPreferences(); - $options = [ - Objects::FILTER_NAME => ($prefs['VIEW_DESCRIPTION'] + Objects::FILTER_NAME => ($prefs->isEnabled(LendPreferences::VIEW_DESCRIPTION) ? _T("Name/description", "objectslend") : _T("Name", "objectslend")), Objects::FILTER_SERIAL => _T("Serial number", "objectslend"), Objects::FILTER_ID => _T("Id", "objectslend") ]; - if ($prefs['VIEW_DIMENSION']) { + if ($prefs->isEnabled(LendPreferences::VIEW_DIMENSION)) { $options[Objects::FILTER_DIM] = _T("Dimensions", "objectslend"); } diff --git a/lib/GaletteObjectsLend/IO/PdfObject.php b/lib/GaletteObjectsLend/IO/PdfObject.php index f988ce9d..11ed93cc 100644 --- a/lib/GaletteObjectsLend/IO/PdfObject.php +++ b/lib/GaletteObjectsLend/IO/PdfObject.php @@ -13,7 +13,7 @@ use Galette\IO\Pdf; use Galette\Core\Db; use Galette\Core\Preferences; -use GaletteObjectsLend\Entity\Preferences as LPreferences; +use GaletteObjectsLend\LendPreferences; use GaletteObjectsLend\Entity\LendObject; use GaletteObjectsLend\Repository\Rents; @@ -25,16 +25,16 @@ class PdfObject extends Pdf { private Db $zdb; - private LPreferences $lprefs; + private LendPreferences $lprefs; /** * Main constructor * - * @param Db $zdb Database instance - * @param Preferences $prefs Preferences instance - * @param LPreferences $lprefs Plugin Preferences instance + * @param Db $zdb Database instance + * @param Preferences $prefs Preferences instance + * @param LendPreferences $lprefs Plugin Preferences instance */ - public function __construct(Db $zdb, Preferences $prefs, LPreferences $lprefs) + public function __construct(Db $zdb, Preferences $prefs, LendPreferences $lprefs) { parent::__construct($prefs); // Disable Auto Page breaks @@ -120,19 +120,19 @@ public function drawCard(LendObject $object): void } $this->addCell(_T("Name", "objectslend"), $object->getName(), $wpic); - if ($this->lprefs->{LPreferences::PARAM_VIEW_DESCRIPTION}) { + if ($this->lprefs->isEnabled(LendPreferences::VIEW_DESCRIPTION)) { $this->addCell(_T("Description", "objectslend"), $object->getDescription(), $wpic); } - if ($this->lprefs->{LPreferences::PARAM_VIEW_CATEGORY}) { + if ($this->lprefs->isEnabled(LendPreferences::VIEW_CATEGORY)) { $this->addCell(_T("Category", "objectslend"), $object->getCategoryName() ?? '', $wpic); } - if ($this->lprefs->{LPreferences::PARAM_VIEW_SERIAL}) { + if ($this->lprefs->isEnabled(LendPreferences::VIEW_SERIAL)) { $this->addCell(_T("Serial number", "objectslend"), $object->getSerialNumber(), $wpic); } - if ($this->lprefs->{LPreferences::PARAM_VIEW_PRICE}) { + if ($this->lprefs->isEnabled(LendPreferences::VIEW_PRICE)) { $this->addCell(_T("Price", "objectslend"), number_format($object->getPrice(), 2, ',', ' '), $wpic); } - if ($this->lprefs->{LPreferences::PARAM_VIEW_LEND_PRICE}) { + if ($this->lprefs->isEnabled(LendPreferences::VIEW_LEND_PRICE)) { $this->addCell( _T("Borrow price", "objectslend"), number_format($object->getRentPrice(), 2, ',', ' ') . ' €' @@ -140,17 +140,17 @@ public function drawCard(LendObject $object): void $wpic ); } - if ($this->lprefs->{LPreferences::PARAM_VIEW_DIMENSION}) { + if ($this->lprefs->isEnabled(LendPreferences::VIEW_DIMENSION)) { $this->addCell(_T("Dimensions", "objectslend"), $object->getDimension() . ' ' . _T('Cm', 'objectslend'), $wpic); } - if ($this->lprefs->{LPreferences::PARAM_VIEW_WEIGHT}) { + if ($this->lprefs->isEnabled(LendPreferences::VIEW_WEIGHT)) { $this->addCell(_T("Weight", "objectslend"), number_format($object->getWeight(), 3, ',', ' ') . ' ' . _T('Kg', 'objectslend'), $wpic); } $this->addCell(_T("Active", "objectslend"), $object->isObjectActive() ? 'X' : '', $wpic); $this->addCell(_T("Location", "objectslend"), $object->getStatusText(), $wpic); $this->addCell(_T("Since", "objectslend"), $object->getDateBegin(), $wpic); $this->addCell(_T("Member", "objectslend"), $object->getMemberName(), $wpic); - if ($this->lprefs->{LPreferences::PARAM_VIEW_DATE_FORECAST}) { + if ($this->lprefs->isEnabled(LendPreferences::VIEW_DATE_FORECAST)) { $this->addCell(_T("Return", "objectslend"), $object->getDateForecast(), $wpic); } diff --git a/lib/GaletteObjectsLend/IO/PdfObjects.php b/lib/GaletteObjectsLend/IO/PdfObjects.php index 3029ae53..27ddc7ca 100644 --- a/lib/GaletteObjectsLend/IO/PdfObjects.php +++ b/lib/GaletteObjectsLend/IO/PdfObjects.php @@ -17,7 +17,7 @@ use GaletteObjectsLend\Entity\LendObject; use GaletteObjectsLend\Filters\ObjectsList; use GaletteObjectsLend\Entity\LendCategory; -use GaletteObjectsLend\Entity\Preferences as LendPreferences; +use GaletteObjectsLend\LendPreferences; /** * Object labels PDF @@ -159,7 +159,7 @@ public function drawList(array $objects): void foreach ($objects as $object) { if ( - $this->lendsprefs->{LendPreferences::PARAM_VIEW_CATEGORY} + $this->lendsprefs->isEnabled(LendPreferences::VIEW_CATEGORY) && $current_category !== $object->getCategoryId() ) { $this->SetFont('', 'B'); diff --git a/lib/GaletteObjectsLend/LendPreferences.php b/lib/GaletteObjectsLend/LendPreferences.php new file mode 100644 index 00000000..43472594 --- /dev/null +++ b/lib/GaletteObjectsLend/LendPreferences.php @@ -0,0 +1,230 @@ + + */ +final class LendPreferences +{ + public const string PREFIX = 'pref_objectslend_'; + + /** Allow non staff members to rent objects */ + public const string ENABLE_MEMBER_RENT_OBJECT = self::PREFIX . 'enable_member_rent_object'; + /** Generate automatically a contribution when an object is rented */ + public const string AUTO_GENERATE_CONTRIBUTION = self::PREFIX . 'auto_generate_contribution'; + /** Type of the generated contribution */ + public const string GENERATED_CONTRIBUTION_TYPE_ID = self::PREFIX . 'generated_contribution_type_id'; + /** Information text of the generated contribution, with placeholders */ + public const string GENERATED_CONTRIB_INFO_TEXT = self::PREFIX . 'generated_contrib_info_text'; + /** Maximum width of a thumbnail, in pixels */ + public const string THUMB_MAX_WIDTH = self::PREFIX . 'thumb_max_width'; + /** Maximum height of a thumbnail, in pixels */ + public const string THUMB_MAX_HEIGHT = self::PREFIX . 'thumb_max_height'; + /** Maximum size of an uploaded picture, in Ko; 0 follows core images one */ + public const string UPLOAD_SIZE_IMAGES = self::PREFIX . 'upload_size_images'; + /** Show images in objects and categories lists */ + public const string VIEW_THUMBNAIL = self::PREFIX . 'view_thumbnail'; + /** Show categories on the objects list */ + public const string VIEW_CATEGORY = self::PREFIX . 'view_category'; + /** Show the forecast return date */ + public const string VIEW_DATE_FORECAST = self::PREFIX . 'view_date_forecast'; + /** Show the description */ + public const string VIEW_DESCRIPTION = self::PREFIX . 'view_description'; + /** Show dimensions */ + public const string VIEW_DIMENSION = self::PREFIX . 'view_dimension'; + /** Show the rent price */ + public const string VIEW_LEND_PRICE = self::PREFIX . 'view_lend_price'; + /** Show the sum of prices on the objects list */ + public const string VIEW_LIST_PRICE_SUM = self::PREFIX . 'view_list_price_sum'; + /** Show the buy price */ + public const string VIEW_PRICE = self::PREFIX . 'view_price'; + /** Show the serial number */ + public const string VIEW_SERIAL = self::PREFIX . 'view_serial'; + /** Show the weight */ + public const string VIEW_WEIGHT = self::PREFIX . 'view_weight'; + + /** + * Constructor + * + * @param Preferences $preferences Core preferences + */ + public function __construct(private readonly Preferences $preferences) + { + } + + /** + * Get the preferences the plugin declares + * + * Defaults are the ones the former install scripts inserted; contribution + * type 5 is the "donation in money" core installs. + * + * @return array> + */ + public static function getSchema(): array + { + $schema = [ + self::GENERATED_CONTRIBUTION_TYPE_ID => [ + 'type' => PreferencesSchema::TYPE_INT, + 'default' => 5, + 'min' => 0, + 'error' => PreferencesSchema::ERR_POSITIVE_NUMBER, + ], + self::GENERATED_CONTRIB_INFO_TEXT => [ + 'type' => PreferencesSchema::TYPE_STRING, + 'default' => 'Location de {NAME} {DESCRIPTION} {SERIAL_NUMBER}', + ], + self::THUMB_MAX_WIDTH => [ + 'type' => PreferencesSchema::TYPE_INT, + 'default' => 128, + 'min' => 1, + 'error' => PreferencesSchema::ERR_POSITIVE_NUMBER, + ], + self::THUMB_MAX_HEIGHT => [ + 'type' => PreferencesSchema::TYPE_INT, + 'default' => 128, + 'min' => 1, + 'error' => PreferencesSchema::ERR_POSITIVE_NUMBER, + ], + self::UPLOAD_SIZE_IMAGES => [ + 'type' => PreferencesSchema::TYPE_INT, + 'default' => 0, + 'min' => 0, + 'error' => PreferencesSchema::ERR_POSITIVE_NUMBER, + ], + ]; + + foreach (self::getBooleans() as $name => $default) { + $schema[$name] = [ + 'type' => PreferencesSchema::TYPE_BOOL, + 'default' => $default, + ]; + } + + return $schema; + } + + /** + * Get yes/no preferences, with their default value + * + * A form does not post an unchecked box: this is the list to tell it + * from a value that was not part of the form at all. + * + * @return array + */ + public static function getBooleans(): array + { + return [ + self::ENABLE_MEMBER_RENT_OBJECT => true, + self::AUTO_GENERATE_CONTRIBUTION => true, + self::VIEW_THUMBNAIL => true, + self::VIEW_CATEGORY => false, + self::VIEW_DATE_FORECAST => true, + self::VIEW_DESCRIPTION => true, + self::VIEW_DIMENSION => false, + self::VIEW_LEND_PRICE => false, + self::VIEW_LIST_PRICE_SUM => false, + self::VIEW_PRICE => false, + self::VIEW_SERIAL => false, + self::VIEW_WEIGHT => false, + ]; + } + + /** + * Is a yes/no preference on? + * + * @param string $name Preference name, one of the class constants + */ + public function isEnabled(string $name): bool + { + return (bool)$this->preferences->getPluginValue($name); + } + + /** + * Get type of the generated contribution + */ + public function getContributionTypeId(): int + { + return (int)$this->preferences->getPluginValue(self::GENERATED_CONTRIBUTION_TYPE_ID); + } + + /** + * Get information text of the generated contribution + */ + public function getContributionText(): string + { + return (string)$this->preferences->getPluginValue(self::GENERATED_CONTRIB_INFO_TEXT); + } + + /** + * Get thumbnail maximum width + */ + public function getThumbWidth(): int + { + return (int)$this->preferences->getPluginValue(self::THUMB_MAX_WIDTH); + } + + /** + * Get thumbnail maximum height + */ + public function getThumbHeight(): int + { + return (int)$this->preferences->getPluginValue(self::THUMB_MAX_HEIGHT); + } + + /** + * Get maximum size of an uploaded picture, in Ko + * + * Unless set, it is the one core applies to its own pictures. + */ + public function getUploadSize(): int + { + $size = (int)$this->preferences->getPluginValue(self::UPLOAD_SIZE_IMAGES); + return $size > 0 ? $size : UploadSize::Images->get($this->preferences); + } + + /** + * Whether to display images (as thumbnails) in lists + */ + public function imagesInLists(): bool + { + return $this->isEnabled(self::VIEW_THUMBNAIL); + } + + /** + * Get every preference, named without its prefix, for templates + * + * @return array + */ + public function toArray(): array + { + $values = []; + foreach (self::getSchema() as $name => $entry) { + //core hands a false boolean back as an empty string + $values[substr($name, strlen(self::PREFIX))] = match ($entry['type']) { + PreferencesSchema::TYPE_BOOL => $this->isEnabled($name), + PreferencesSchema::TYPE_INT => (int)$this->preferences->getPluginValue($name), + default => (string)$this->preferences->getPluginValue($name), + }; + } + return $values; + } +} diff --git a/lib/GaletteObjectsLend/LendService.php b/lib/GaletteObjectsLend/LendService.php index 8730d29b..ac211010 100644 --- a/lib/GaletteObjectsLend/LendService.php +++ b/lib/GaletteObjectsLend/LendService.php @@ -20,7 +20,6 @@ use GaletteObjectsLend\Entity\LendObject; use GaletteObjectsLend\Entity\LendRent; use GaletteObjectsLend\Entity\LendStatus; -use GaletteObjectsLend\Entity\Preferences; use GaletteObjectsLend\Repository\Objects; use GaletteObjectsLend\Repository\Rents; use GaletteObjectsLend\Repository\Status; @@ -42,13 +41,13 @@ class LendService * @param Db $zdb Database instance * @param CorePreferences $preferences Preferences * @param Login $login Logged in instance - * @param Preferences $lendsprefs Plugin preferences + * @param LendPreferences $lendsprefs Plugin preferences */ public function __construct( private Db $zdb, private CorePreferences $preferences, private Login $login, - private Preferences $lendsprefs + private LendPreferences $lendsprefs ) { } @@ -68,7 +67,7 @@ public function getObject(int $id): LendObject public function canTake(): bool { return $this->isManager() - || $this->lendsprefs->{Preferences::PARAM_ENABLE_MEMBER_RENT_OBJECT}; + || $this->lendsprefs->isEnabled(LendPreferences::ENABLE_MEMBER_RENT_OBJECT); } /** @@ -85,7 +84,7 @@ public function canGiveBack(LendObject $object): bool return true; } - return $this->lendsprefs->{Preferences::PARAM_ENABLE_MEMBER_RENT_OBJECT} + return $this->lendsprefs->isEnabled(LendPreferences::ENABLE_MEMBER_RENT_OBJECT) && $object->getIdAdh() !== null && $this->login->id == $object->getIdAdh(); } @@ -178,7 +177,7 @@ public function take( return $this->inTransaction(function () use ($object, $status_id, $member_id, $date_forecast, $rent_price, $payment_type) { $rent = $this->openRent($object, $status_id, $member_id, '', $date_forecast); - if ($this->lendsprefs->{Preferences::PARAM_AUTO_GENERATE_CONTRIBUTION} && $rent_price > 0) { + if ($this->lendsprefs->isEnabled(LendPreferences::AUTO_GENERATE_CONTRIBUTION) && $rent_price > 0) { return $this->storeContribution($object, $rent, $rent_price, $payment_type); } return null; @@ -330,12 +329,12 @@ private function storeContribution( number_format($object->getWeight(), 3, ',', ' '), $object->getDimension() ], - $this->lendsprefs->{Preferences::PARAM_GENERATED_CONTRIB_INFO_TEXT} + $this->lendsprefs->getContributionText() ); $values = [ 'montant_cotis' => $amount, - ContributionsTypes::PK => $this->lendsprefs->{Preferences::PARAM_GENERATED_CONTRIBUTION_TYPE_ID}, + ContributionsTypes::PK => $this->lendsprefs->getContributionTypeId(), 'date_enreg' => date("Y-m-d"), 'date_debut_cotis' => date("Y-m-d"), 'type_paiement_cotis' => $payment_type, diff --git a/lib/GaletteObjectsLend/PluginGaletteObjectslend.php b/lib/GaletteObjectsLend/PluginGaletteObjectslend.php index 96027428..dff2cbb8 100644 --- a/lib/GaletteObjectsLend/PluginGaletteObjectslend.php +++ b/lib/GaletteObjectsLend/PluginGaletteObjectslend.php @@ -15,6 +15,7 @@ use Galette\Core\Login; use Galette\Core\Plugins\InstallableInterface; use Galette\Core\Plugins\MenuProviderInterface; +use Galette\Core\Plugins\PreferencesProviderInterface; use Galette\Core\GalettePlugin; use GaletteObjectsLend\Entity\CategoryPicture; use GaletteObjectsLend\Entity\LendObject; @@ -22,7 +23,6 @@ use GaletteObjectsLend\Entity\LendRent; use GaletteObjectsLend\Entity\ObjectPicture; use GaletteObjectsLend\Entity\LendStatus; -use GaletteObjectsLend\Entity\Preferences; /** * Plugin Galette Objects Lend @@ -30,7 +30,7 @@ * @author Johan Cwiklinski */ -class PluginGaletteObjectslend extends GalettePlugin implements InstallableInterface, MenuProviderInterface +class PluginGaletteObjectslend extends GalettePlugin implements InstallableInterface, MenuProviderInterface, PreferencesProviderInterface { #[Inject] private readonly Db $zdb; //@phpstan-ignore property.uninitializedReadonly, property.onlyRead (injected from DI) @@ -96,6 +96,16 @@ public function getMenus(): array return $menus; } + /** + * Get the preferences the plugin declares + * + * @return array> + */ + public function getPreferences(): array + { + return LendPreferences::getSchema(); + } + /** * Extra public menus entries * @@ -118,7 +128,6 @@ public function isInstalled(): bool && $this->zdb->tableExists(LEND_PREFIX . LendRent::TABLE) && $this->zdb->tableExists(LEND_PREFIX . LendStatus::TABLE) && $this->zdb->tableExists(LEND_PREFIX . ObjectPicture::TABLE) - && $this->zdb->tableExists(LEND_PREFIX . Preferences::TABLE) ; } } diff --git a/lib/GaletteObjectsLend/Repository/Categories.php b/lib/GaletteObjectsLend/Repository/Categories.php index acf9f6d2..9410e5aa 100644 --- a/lib/GaletteObjectsLend/Repository/Categories.php +++ b/lib/GaletteObjectsLend/Repository/Categories.php @@ -16,7 +16,7 @@ use Galette\Core\Preferences; use GaletteObjectsLend\Entity\LendCategory; use GaletteObjectsLend\Entity\LendObject; -use GaletteObjectsLend\Entity\Preferences as LendPreferences; +use GaletteObjectsLend\LendPreferences; use GaletteObjectsLend\Filters\CategoriesList; use GaletteObjectsLend\Filters\ObjectsList; use Laminas\Db\ResultSet\ResultSet; @@ -113,7 +113,7 @@ protected function buildSelect(): Select $this->zdb, $this->preferences, $this->login, - new LendPreferences($this->zdb), + new LendPreferences($this->preferences), $this->filters->objects_filters ); $objects->applyFilters($select); diff --git a/lib/GaletteObjectsLend/Repository/Objects.php b/lib/GaletteObjectsLend/Repository/Objects.php index 45b8bd75..7324e014 100644 --- a/lib/GaletteObjectsLend/Repository/Objects.php +++ b/lib/GaletteObjectsLend/Repository/Objects.php @@ -19,7 +19,7 @@ use GaletteObjectsLend\Entity\LendObject; use GaletteObjectsLend\Entity\LendRent; use GaletteObjectsLend\Entity\LendStatus; -use GaletteObjectsLend\Entity\Preferences as LendPreferences; +use GaletteObjectsLend\LendPreferences; use GaletteObjectsLend\Filters\ObjectsList; use Laminas\Db\ResultSet\ResultSet; use Laminas\Db\Sql\Select; @@ -286,7 +286,7 @@ public function applyFilters(Select $select): void switch ($this->filters->field_filter) { case self::FILTER_NAME: - if ($this->lendsprefs->{LendPreferences::PARAM_VIEW_DESCRIPTION}) { + if ($this->lendsprefs->isEnabled(LendPreferences::VIEW_DESCRIPTION)) { $select->where ->nest() ->addPredicate($this->contains('o.name', $search)) diff --git a/lib/GaletteObjectsLend/SampleData.php b/lib/GaletteObjectsLend/SampleData.php new file mode 100644 index 00000000..6273392f --- /dev/null +++ b/lib/GaletteObjectsLend/SampleData.php @@ -0,0 +1,295 @@ + + * @phpstan-type Rent array{status: string, member?: int, begin: int, end?: int, comment?: string} + * @phpstan-type Item array{ + * name: string, + * description: string, + * serial: string, + * price: float|int, + * rent_price: float|int, + * per_day: bool, + * dimension: string, + * weight: float|int, + * category: ?string, + * active: bool, + * rents: list + * } + */ +final class SampleData +{ + public const string FILE = __DIR__ . '/../../scripts/sample-data.json'; + + /** @var array Category key => id */ + private array $categories = []; + /** @var array Status key => id */ + private array $statuses = []; + + /** + * Constructor + * + * @param Db $zdb Database instance + * @param string $file Data file + */ + public function __construct(private readonly Db $zdb, private readonly string $file = self::FILE) + { + } + + /** + * Does the catalog already hold objects? + * + * Sample data are meant for an empty catalog; loading them twice would + * duplicate every object. + */ + public function hasObjects(): bool + { + $select = $this->zdb->select(LEND_PREFIX . LendObject::TABLE); + $select->limit(1); + return $this->zdb->execute($select)->count() > 0; + } + + /** + * Get active members ids, as sample data would pick them + * + * @param int $limit Maximum number of members + * + * @return list + */ + public function getMembers(int $limit = 10): array + { + $select = $this->zdb->select(Adherent::TABLE); + $select->columns([Adherent::PK]) + ->where(['activite_adh' => true]) + ->order(Adherent::PK) + ->limit($limit); + + $ids = []; + foreach ($this->zdb->execute($select) as $row) { + $ids[] = (int)$row->{Adherent::PK}; + } + return $ids; + } + + /** + * Load sample data, in a single transaction + * + * @param list $members Members to give rents to + * + * @return array{categories: int, statuses: int, objects: int, rents: int} What was created + */ + public function load(array $members): array + { + $data = $this->read(); + $created = ['categories' => 0, 'statuses' => 0, 'objects' => 0, 'rents' => 0]; + + $need_transaction = !$this->zdb->inTransaction(); + try { + if ($need_transaction) { + $this->zdb->beginTransaction(); + } + + foreach ($data['categories'] as $category) { + $created['categories'] += (int)$this->addCategory($category); + } + foreach ($data['statuses'] as $status) { + $created['statuses'] += (int)$this->addStatus($status); + } + foreach ($data['objects'] as $item) { + $created['rents'] += $this->addObject($item, $members); + ++$created['objects']; + } + + if ($need_transaction) { + $this->zdb->commit(); + } + } catch (Throwable $e) { + if ($need_transaction) { + $this->zdb->rollback(); + } + throw $e; + } + + return $created; + } + + /** + * Read and check data file + * + * @return array{ + * categories: list, + * statuses: list, + * objects: list + * } + */ + private function read(): array + { + $contents = file_get_contents($this->file); + if ($contents === false) { + throw new \RuntimeException(sprintf('Unable to read sample data file %s', $this->file)); + } + $data = json_decode($contents, true, flags: JSON_THROW_ON_ERROR); + if (!is_array($data) || !isset($data['categories'], $data['statuses'], $data['objects'])) { + throw new \RuntimeException(sprintf('Invalid sample data file %s', $this->file)); + } + return $data; + } + + /** + * Get category, create it if missing + * + * @param array{key: string, name: string, active: bool} $data Category data + * + * @return bool Whether it has been created + */ + private function addCategory(array $data): bool + { + $select = $this->zdb->select(LEND_PREFIX . LendCategory::TABLE) + ->where(['name' => $data['name']]) + ->limit(1); + $row = $this->zdb->execute($select)->current(); + if ($row) { + $this->categories[$data['key']] = (int)$row->{LendCategory::PK}; + return false; + } + + $category = new LendCategory($this->zdb); + $category->setName($data['name'])->setActive($data['active']); + $category->store(); + $this->categories[$data['key']] = (int)$category->getId(); + return true; + } + + /** + * Get status, create it if missing + * + * @param array{key: string, text: string, in_stock: bool, active: bool, days?: int} $data Status data + * + * @return bool Whether it has been created + */ + private function addStatus(array $data): bool + { + $select = $this->zdb->select(LEND_PREFIX . LendStatus::TABLE) + ->where(['status_text' => $data['text']]) + ->limit(1); + $row = $this->zdb->execute($select)->current(); + if ($row) { + $this->statuses[$data['key']] = (int)$row->{LendStatus::PK}; + return false; + } + + $status = new LendStatus($this->zdb); + $status + ->setText($data['text']) + ->setInStock($data['in_stock']) + ->setActive($data['active']) + ->setRentDayNumber($data['days'] ?? null); + $status->store(); + $this->statuses[$data['key']] = (int)$status->getId(); + return true; + } + + /** + * Create object, its rents, and set the last one as current + * + * @param Item $data Object data + * @param list $members Members to give rents to + * + * @return int Number of rents created + */ + private function addObject(array $data, array $members): int + { + $object = new LendObject($this->zdb); + $object + ->setName($data['name']) + ->setDescription($data['description']) + ->setSerialNumber($data['serial']) + ->setPrice((float)$data['price']) + ->setRentPrice((float)$data['rent_price']) + ->setPricePerDay($data['per_day']) + ->setDimension($data['dimension']) + ->setWeight((float)$data['weight']) + ->setActive($data['active']) + ->setCategoryId($data['category'] !== null ? $this->categories[$data['category']] : null); + $object->store(); + $object_id = (int)$object->getId(); + + $rent = null; + $last = count($data['rents']) - 1; + foreach ($data['rents'] as $i => $entry) { + $rent = new LendRent($this->zdb); + $rent + ->setObjectId($object_id) + ->setStatusId($this->statuses[$entry['status']]) + ->setAdherentId($this->getMember($entry['member'] ?? null, $members)) + ->setComments($entry['comment'] ?? '') + ->setDateBegin($this->getDate($entry['begin'])); + //the last rent is the current one + if ($i < $last && isset($entry['end'])) { + $rent->setDateEnd($this->getDate($entry['end'])); + } + $rent->store(); + } + + if ($rent !== null) { + $update = $this->zdb->update(LEND_PREFIX . LendObject::TABLE) + ->set([LendRent::PK => $rent->getId()]) + ->where([LendObject::PK => $object_id]); + $this->zdb->execute($update); + } + + return count($data['rents']); + } + + /** + * Get member for a rent + * + * @param ?int $position Member position in sample data + * @param list $members Available members + */ + private function getMember(?int $position, array $members): ?int + { + if ($position === null || count($members) === 0) { + return null; + } + return $members[$position % count($members)]; + } + + /** + * Get date from a number of days relative to today + * + * @param int $days Days, negative in the past + */ + private function getDate(int $days): string + { + return (new \DateTimeImmutable())->modify(sprintf('%+d days', $days))->format('Y-m-d H:i:s'); + } +} diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 10f2856b..c2e8d3d2 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -12,36 +12,6 @@ parameters: count: 1 path: lib/GaletteObjectsLend/Controllers/Crud/ObjectsController.php - - - message: '#^Empty array passed to foreach\.$#' - identifier: foreach.emptyArray - count: 1 - path: lib/GaletteObjectsLend/Controllers/MainController.php - - - - message: '#^Cannot access property \$code on array\|ArrayObject\.$#' - identifier: property.nonObject - count: 1 - path: lib/GaletteObjectsLend/Entity/Preferences.php - - - - message: '#^Cannot access property \$is_text on array\|ArrayObject\.$#' - identifier: property.nonObject - count: 1 - path: lib/GaletteObjectsLend/Entity/Preferences.php - - - - message: '#^Cannot access property \$value_numeric on array\|ArrayObject\.$#' - identifier: property.nonObject - count: 1 - path: lib/GaletteObjectsLend/Entity/Preferences.php - - - - message: '#^Cannot access property \$value_text on array\|ArrayObject\.$#' - identifier: property.nonObject - count: 1 - path: lib/GaletteObjectsLend/Entity/Preferences.php - - message: '#^Parameter \#5 \$ln of method TCPDF\:\:Cell\(\) expects int, false given\.$#' identifier: argument.type diff --git a/scripts/mysql.sql b/scripts/mysql.sql index 12f3a634..c267644b 100644 --- a/scripts/mysql.sql +++ b/scripts/mysql.sql @@ -78,107 +78,6 @@ INSERT INTO galette_lend_status (status_text, in_stock, is_active, rent_day_numb INSERT INTO galette_lend_status (status_text, in_stock, is_active) VALUES('Vendu (exemple)', 0, 1); INSERT INTO galette_lend_status (status_text, in_stock, is_active) VALUES('Detruit (exemple)', 0, 1); -DROP TABLE IF EXISTS galette_lend_parameters; -CREATE TABLE galette_lend_parameters ( - parameter_id int(10) unsigned NOT NULL AUTO_INCREMENT, - code varchar(30) COLLATE utf8_general_ci NOT NULL, - is_date tinyint(1) NOT NULL, - value_date date DEFAULT NULL, - is_text tinyint(1) NOT NULL, - value_text varchar(300) COLLATE utf8_general_ci DEFAULT NULL, - is_numeric tinyint(1) NOT NULL, - nb_digits int(11) DEFAULT NULL, - value_numeric double DEFAULT NULL, - date_creation datetime NOT NULL, - date_modification datetime NOT NULL, - PRIMARY KEY (parameter_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8; - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_CATEGORY', 0, 0, 1, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_SERIAL', 0, 0, 1, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_THUMBNAIL', 0, 0, 1, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_FULLSIZE', 0, 0, 1, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_DESCRIPTION', 0, 0, 1, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_PRICE', 0, 0, 1, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_DIMENSION', 0, 0, 1, 0, 0, NOW(), NOW()); - -insert into galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_WEIGHT', 0, 0, 1, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_LEND_PRICE', 0, 0, 1, 0, 0, NOW(), NOW()); - -insert into galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('THUMB_MAX_WIDTH', 0, 0, 1, 0, 128, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('THUMB_MAX_HEIGHT', 0, 0, 1, 0, 128, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('AUTO_GENERATE_CONTRIBUTION', 0, 0, 1, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('GENERATED_CONTRIBUTION_TYPE_ID', 0, 0, 1, 0, 5, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, value_text, date_creation, date_modification) -VALUES -('GENERATED_CONTRIB_INFO_TEXT', 0, 1, 0, 'Location de {NAME} {DESCRIPTION} {SERIAL_NUMBER}', NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('ENABLE_MEMBER_RENT_OBJECT', 0, 0, 1, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_DATE_FORECAST', 0, 0, 1, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_LIST_PRICE_SUM', 0, 0, 1, 0, 0, NOW(), NOW()); - DROP TABLE IF EXISTS galette_lend_categories_pictures; CREATE TABLE IF NOT EXISTS galette_lend_categories_pictures ( category_id int(11) NOT NULL, diff --git a/scripts/pgsql.sql b/scripts/pgsql.sql index 0ba06d03..c6bd68ed 100644 --- a/scripts/pgsql.sql +++ b/scripts/pgsql.sql @@ -30,15 +30,6 @@ CREATE SEQUENCE galette_lend_status_id_seq MINVALUE 1 CACHE 1; --- sequence for lend parameters -DROP SEQUENCE IF EXISTS galette_lend_parameters_id_seq; -CREATE SEQUENCE galette_lend_parameters_id_seq - START 1 - INCREMENT 1 - MAXVALUE 2147483647 - MINVALUE 1 - CACHE 1; - -- sequence for lend cetegories DROP SEQUENCE IF EXISTS galette_lend_category_id_seq; CREATE SEQUENCE galette_lend_category_id_seq @@ -115,22 +106,6 @@ CREATE TABLE galette_lend_pictures ( ); -DROP TABLE IF EXISTS galette_lend_parameters CASCADE; -CREATE TABLE IF NOT EXISTS galette_lend_parameters ( - parameter_id integer DEFAULT nextval('galette_lend_parameters_id_seq'::text) NOT NULL, - code character varying(30) NOT NULL, - is_date boolean NOT NULL, - value_date timestamp DEFAULT NULL, - is_text boolean NOT NULL, - value_text character varying(300) DEFAULT NULL, - is_numeric boolean NOT NULL, - nb_digits integer DEFAULT NULL, - value_numeric real DEFAULT NULL, - date_creation timestamp NOT NULL, - date_modification timestamp NOT NULL, - PRIMARY KEY (parameter_id) -); - DROP TABLE IF EXISTS galette_lend_categories_pictures; CREATE TABLE galette_lend_categories_pictures ( @@ -150,89 +125,3 @@ INSERT INTO galette_lend_status (status_text, in_stock, is_active, rent_day_numb INSERT INTO galette_lend_status (status_text, in_stock, is_active, rent_day_number) VALUES('Reparation (exemple)', FALSE, TRUE, 14); INSERT INTO galette_lend_status (status_text, in_stock, is_active) VALUES('Vendu (exemple)', FALSE, TRUE); INSERT INTO galette_lend_status (status_text, in_stock, is_active) VALUES('Detruit (exemple)', FALSE, TRUE); - --- Default parameters -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_CATEGORY', FALSE, FALSE, TRUE, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_SERIAL', FALSE, FALSE, TRUE, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_THUMBNAIL', FALSE, FALSE, TRUE, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_FULLSIZE', FALSE, FALSE, TRUE, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_DESCRIPTION', FALSE, FALSE, TRUE, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_PRICE', FALSE, FALSE, TRUE, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_DIMENSION', FALSE, FALSE, TRUE, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_WEIGHT', FALSE, FALSE, TRUE, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_LEND_PRICE', FALSE, FALSE, TRUE, 0, 0, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('THUMB_MAX_WIDTH', FALSE, FALSE, TRUE, 0, 128, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('THUMB_MAX_HEIGHT', FALSE, FALSE, TRUE, 0, 128, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -values -('AUTO_GENERATE_CONTRIBUTION', FALSE, FALSE, TRUE, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -values -('GENERATED_CONTRIBUTION_TYPE_ID', FALSE, FALSE, TRUE, 0, 5, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, value_text, date_creation, date_modification) -VALUES -('GENERATED_CONTRIB_INFO_TEXT', FALSE, TRUE, FALSE, 'Location de {NAME} {DESCRIPTION} {SERIAL_NUMBER}', NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('ENABLE_MEMBER_RENT_OBJECT', FALSE, FALSE, TRUE, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_DATE_FORECAST', FALSE, FALSE, TRUE, 0, 1, NOW(), NOW()); - -INSERT INTO galette_lend_parameters -(code, is_date, is_text, is_numeric, nb_digits, value_numeric, date_creation, date_modification) -VALUES -('VIEW_LIST_PRICE_SUM', FALSE, FALSE, TRUE, 0, 0, NOW(), NOW()); diff --git a/scripts/sample-data.json b/scripts/sample-data.json new file mode 100644 index 00000000..9eb2b363 --- /dev/null +++ b/scripts/sample-data.json @@ -0,0 +1,217 @@ +{ + "_comment": "Sample data for Galette Objects Lend. Dates are days relative to the import; the last rent of each object is its current one. Members are picked among existing ones, by position.", + "categories": [ + {"key": "tools", "name": "Outillage", "active": true}, + {"key": "garden", "name": "Jardin", "active": true}, + {"key": "party", "name": "Fêtes et événements", "active": true}, + {"key": "books", "name": "Ludothèque", "active": true}, + {"key": "old", "name": "Ancien matériel", "active": false} + ], + "statuses": [ + {"key": "shelf", "text": "Local associatif", "in_stock": true, "active": true}, + {"key": "cellar", "text": "Cave de la mairie", "in_stock": true, "active": true}, + {"key": "loan", "text": "Prêt courte durée", "in_stock": false, "active": true, "days": 7}, + {"key": "long", "text": "Prêt longue durée", "in_stock": false, "active": true, "days": 30}, + {"key": "repair", "text": "En réparation", "in_stock": false, "active": true, "days": 21}, + {"key": "lost", "text": "Perdu", "in_stock": false, "active": false} + ], + "objects": [ + { + "name": "Perceuse-visseuse sans fil", + "description": "18 V, deux batteries et chargeur", + "serial": "OUT-001", + "price": 129.90, "rent_price": 2.00, "per_day": true, + "dimension": "30x25x10", "weight": 1.8, + "category": "tools", "active": true, + "rents": [ + {"status": "loan", "member": 0, "begin": -95, "end": -90, "comment": "Rendue avec les deux batteries"}, + {"status": "shelf", "begin": -90, "end": -40}, + {"status": "loan", "member": 1, "begin": -40, "end": -33}, + {"status": "shelf", "begin": -33, "end": -3}, + {"status": "loan", "member": 2, "begin": -3} + ] + }, + { + "name": "Scie sauteuse", + "description": "Avec lot de lames bois et métal", + "serial": "OUT-002", + "price": 89.00, "rent_price": 1.50, "per_day": true, + "dimension": "25x20x8", "weight": 2.1, + "category": "tools", "active": true, + "rents": [ + {"status": "loan", "member": 3, "begin": -60, "end": -52, "comment": "Une lame cassée"}, + {"status": "repair", "begin": -52, "end": -30, "comment": "Changement du guide"}, + {"status": "shelf", "begin": -30} + ] + }, + { + "name": "Escabeau 5 marches", + "description": "Aluminium", + "serial": "OUT-003", + "price": 65.00, "rent_price": 0, "per_day": false, + "dimension": "150x45x10", "weight": 5.5, + "category": "tools", "active": true, + "rents": [ + {"status": "cellar", "begin": -200} + ] + }, + { + "name": "Ponceuse orbitale", + "description": "", + "serial": "OUT-004", + "price": 75.00, "rent_price": 1.00, "per_day": true, + "dimension": "20x15x15", "weight": 1.6, + "category": "tools", "active": true, + "rents": [ + {"status": "long", "member": 4, "begin": -45, "comment": "Rénovation d'un parquet"} + ] + }, + { + "name": "Tondeuse thermique", + "description": "Coupe 46 cm, bac de ramassage", + "serial": "JAR-001", + "price": 349.00, "rent_price": 5.00, "per_day": false, + "dimension": "150x50x100", "weight": 28, + "category": "garden", "active": true, + "rents": [ + {"status": "loan", "member": 1, "begin": -150, "end": -145}, + {"status": "shelf", "begin": -145, "end": -20}, + {"status": "repair", "begin": -20, "comment": "Démarreur à changer"} + ] + }, + { + "name": "Taille-haie électrique", + "description": "", + "serial": "JAR-002", + "price": 99.00, "rent_price": 2.00, "per_day": false, + "dimension": "100x20x20", "weight": 3.9, + "category": "garden", "active": true, + "rents": [ + {"status": "loan", "member": 0, "begin": -12, "end": -10}, + {"status": "shelf", "begin": -10} + ] + }, + { + "name": "Brouette", + "description": "100 litres", + "serial": "JAR-003", + "price": 55.00, "rent_price": 0, "per_day": false, + "dimension": "140x60x60", "weight": 12, + "category": "garden", "active": true, + "rents": [ + {"status": "shelf", "begin": -300} + ] + }, + { + "name": "Barnum 3x6 m", + "description": "Avec parois latérales et lestes", + "serial": "FET-001", + "price": 420.00, "rent_price": 15.00, "per_day": false, + "dimension": "120x40x40", "weight": 35, + "category": "party", "active": true, + "rents": [ + {"status": "loan", "member": 2, "begin": -80, "end": -78, "comment": "Fête de quartier"}, + {"status": "cellar", "begin": -78, "end": -8}, + {"status": "loan", "member": 5, "begin": -8, "end": -6, "comment": "Mariage"}, + {"status": "cellar", "begin": -6} + ] + }, + { + "name": "Sonorisation portable", + "description": "Enceinte 300 W, deux micros sans fil", + "serial": "FET-002", + "price": 499.00, "rent_price": 10.00, "per_day": false, + "dimension": "60x40x35", "weight": 14, + "category": "party", "active": true, + "rents": [ + {"status": "loan", "member": 3, "begin": -30, "end": -29}, + {"status": "shelf", "begin": -29, "end": -2}, + {"status": "loan", "member": 0, "begin": -2} + ] + }, + { + "name": "Lot de 10 tables pliantes", + "description": "180x75 cm", + "serial": "FET-003", + "price": 600.00, "rent_price": 8.00, "per_day": false, + "dimension": "180x75x5", "weight": 120, + "category": "party", "active": true, + "rents": [ + {"status": "cellar", "begin": -365} + ] + }, + { + "name": "Machine à barbe à papa", + "description": "", + "serial": "FET-004", + "price": 110.00, "rent_price": 4.00, "per_day": false, + "dimension": "50x50x40", "weight": 8, + "category": "party", "active": true, + "rents": [ + {"status": "loan", "member": 6, "begin": -15, "end": -14}, + {"status": "shelf", "begin": -14} + ] + }, + { + "name": "Jeu de société « Les Aventuriers du Rail »", + "description": "Boîte complète", + "serial": "LUD-001", + "price": 45.00, "rent_price": 0, "per_day": false, + "dimension": "30x30x8", "weight": 1.5, + "category": "books", "active": true, + "rents": [ + {"status": "loan", "member": 4, "begin": -50, "end": -43}, + {"status": "shelf", "begin": -43, "end": -21}, + {"status": "loan", "member": 1, "begin": -21, "end": -14}, + {"status": "shelf", "begin": -14, "end": -5}, + {"status": "loan", "member": 7, "begin": -5} + ] + }, + { + "name": "Mölkky", + "description": "Quilles finlandaises en bois", + "serial": "LUD-002", + "price": 35.00, "rent_price": 0, "per_day": false, + "dimension": "40x20x20", "weight": 3, + "category": "books", "active": true, + "rents": [ + {"status": "loan", "member": 2, "begin": -70, "end": -60, "comment": "Une quille manquante au retour"}, + {"status": "lost", "begin": -60, "comment": "Quille numéro 7 jamais retrouvée"} + ] + }, + { + "name": "Rétroprojecteur", + "description": "Remplacé par le vidéoprojecteur", + "serial": "ANC-001", + "price": 150.00, "rent_price": 0, "per_day": false, + "dimension": "40x40x50", "weight": 9, + "category": "old", "active": false, + "rents": [ + {"status": "cellar", "begin": -1000} + ] + }, + { + "name": "Vidéoprojecteur", + "description": "Full HD, câble HDMI de 10 m", + "serial": "FET-005", + "price": 550.00, "rent_price": 6.00, "per_day": true, + "dimension": "30x25x10", "weight": 2.8, + "category": "party", "active": true, + "rents": [ + {"status": "long", "member": 5, "begin": -35, "end": -5, "comment": "Cycle de projections"}, + {"status": "shelf", "begin": -5} + ] + }, + { + "name": "Nettoyeur haute pression", + "description": "", + "serial": "", + "price": 0, "rent_price": 3.00, "per_day": false, + "dimension": "", "weight": 0, + "category": null, "active": true, + "rents": [ + {"status": "shelf", "begin": -7} + ] + } + ] +} diff --git a/scripts/upgrade-to-1.1-mysql.sql b/scripts/upgrade-to-1.1-mysql.sql new file mode 100644 index 00000000..a368848d --- /dev/null +++ b/scripts/upgrade-to-1.1-mysql.sql @@ -0,0 +1,24 @@ +-- +-- This file is part of Galette Objects Lend plugin (https://galette.eu). +-- SPDX-FileCopyrightText: Copyright © 2013-2026 The Galette Team +-- SPDX-License-Identifier: GPL-3.0-or-later +-- + +-- Plugin preferences move to core preferences. Declaring them already +-- inserted their defaults: those give way to the values set so far. +DELETE FROM galette_preferences WHERE nom_pref LIKE 'pref_objectslend_%'; + +INSERT INTO galette_preferences (nom_pref, val_pref) +SELECT + CONCAT('pref_objectslend_', LOWER(code)), + CASE WHEN is_text = 1 THEN COALESCE(value_text, '') + ELSE CAST(CAST(COALESCE(value_numeric, 0) AS SIGNED) AS CHAR) END +FROM galette_lend_parameters +WHERE code IN ( + 'ENABLE_MEMBER_RENT_OBJECT', 'AUTO_GENERATE_CONTRIBUTION', 'GENERATED_CONTRIBUTION_TYPE_ID', + 'GENERATED_CONTRIB_INFO_TEXT', 'THUMB_MAX_WIDTH', 'THUMB_MAX_HEIGHT', 'VIEW_THUMBNAIL', + 'VIEW_CATEGORY', 'VIEW_DATE_FORECAST', 'VIEW_DESCRIPTION', 'VIEW_DIMENSION', 'VIEW_LEND_PRICE', + 'VIEW_LIST_PRICE_SUM', 'VIEW_PRICE', 'VIEW_SERIAL', 'VIEW_WEIGHT' +); + +DROP TABLE galette_lend_parameters; diff --git a/scripts/upgrade-to-1.1-pgsql.sql b/scripts/upgrade-to-1.1-pgsql.sql new file mode 100644 index 00000000..62f2ba2e --- /dev/null +++ b/scripts/upgrade-to-1.1-pgsql.sql @@ -0,0 +1,25 @@ +-- +-- This file is part of Galette Objects Lend plugin (https://galette.eu). +-- SPDX-FileCopyrightText: Copyright © 2013-2026 The Galette Team +-- SPDX-License-Identifier: GPL-3.0-or-later +-- + +-- Plugin preferences move to core preferences. Declaring them already +-- inserted their defaults: those give way to the values set so far. +DELETE FROM galette_preferences WHERE nom_pref LIKE 'pref_objectslend_%'; + +INSERT INTO galette_preferences (nom_pref, val_pref) +SELECT + 'pref_objectslend_' || LOWER(code), + CASE WHEN is_text THEN COALESCE(value_text, '') + ELSE CAST(CAST(ROUND(COALESCE(value_numeric, 0)) AS integer) AS text) END +FROM galette_lend_parameters +WHERE code IN ( + 'ENABLE_MEMBER_RENT_OBJECT', 'AUTO_GENERATE_CONTRIBUTION', 'GENERATED_CONTRIBUTION_TYPE_ID', + 'GENERATED_CONTRIB_INFO_TEXT', 'THUMB_MAX_WIDTH', 'THUMB_MAX_HEIGHT', 'VIEW_THUMBNAIL', + 'VIEW_CATEGORY', 'VIEW_DATE_FORECAST', 'VIEW_DESCRIPTION', 'VIEW_DIMENSION', 'VIEW_LEND_PRICE', + 'VIEW_LIST_PRICE_SUM', 'VIEW_PRICE', 'VIEW_SERIAL', 'VIEW_WEIGHT' +); + +DROP TABLE galette_lend_parameters; +DROP SEQUENCE IF EXISTS galette_lend_parameters_id_seq; diff --git a/templates/default/categories_list.html.twig b/templates/default/categories_list.html.twig index 8112c029..fdef3935 100644 --- a/templates/default/categories_list.html.twig +++ b/templates/default/categories_list.html.twig @@ -110,7 +110,7 @@ {{ categ.getName(false) }} - + {% if categ.isActive() %}{{ _T("Active", "objectslend") }}{% else %}{{ _T("Inactive", "objectslend") }}{% endif %} {% if login.isAdmin() or login.isStaff() %} diff --git a/templates/default/category_edit.html.twig b/templates/default/category_edit.html.twig index fc06753a..e56c37bf 100644 --- a/templates/default/category_edit.html.twig +++ b/templates/default/category_edit.html.twig @@ -29,7 +29,12 @@ } %}
- + {% include "components/forms/max_size.html.twig" with { + label: _T("Picture:", "objectslend"), + for: "picture_file", + max_size: olendsprefs.getUploadSize(), + input_id: "picture_file" + } %} +
diff --git a/templates/default/headers.html.twig b/templates/default/headers.html.twig index ffc45d24..51ee64a2 100644 --- a/templates/default/headers.html.twig +++ b/templates/default/headers.html.twig @@ -4,7 +4,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later #} -{% if olendsprefs is defined and olendsprefs.showFullsize() %} +{% if olendsprefs is defined %} {% endblock %} diff --git a/templates/default/objects_list.html.twig b/templates/default/objects_list.html.twig index be584811..7c7fd650 100644 --- a/templates/default/objects_list.html.twig +++ b/templates/default/objects_list.html.twig @@ -65,31 +65,31 @@ {'label': _T("Name", "objectslend"), 'order': constant("GaletteObjectsLend\\Repository\\Objects::ORDERBY_NAME")}, ]) %} - {% if lendsprefs.VIEW_SERIAL %} + {% if lendsprefs.view_serial %} {% set columns = columns|merge([ {'label': _T("Serial", "objectslend"), 'order': constant("GaletteObjectsLend\\Repository\\Objects::ORDERBY_SERIAL")}, ]) %} {% endif %} - {% if lendsprefs.VIEW_PRICE %} + {% if lendsprefs.view_price %} {% set columns = columns|merge([ {'label': _T("Price", "objectslend"), 'order': constant("GaletteObjectsLend\\Repository\\Objects::ORDERBY_PRICE")}, ]) %} {% endif %} - {% if lendsprefs.VIEW_LEND_PRICE %} + {% if lendsprefs.view_lend_price %} {% set columns = columns|merge([ {'label': _T("Borrow price", "objectslend"), 'order': constant("GaletteObjectsLend\\Repository\\Objects::ORDERBY_RENTPRICE")}, ]) %} {% endif %} - {% if lendsprefs.VIEW_DIMENSION %} + {% if lendsprefs.view_dimension %} {% set columns = columns|merge([ {'label': _T("Dimensions", "objectslend")}, ]) %} {% endif %} - {% if lendsprefs.VIEW_WEIGHT %} + {% if lendsprefs.view_weight %} {% set columns = columns|merge([ {'label': _T("Weight", "objectslend"), 'order': constant("GaletteObjectsLend\\Repository\\Objects::ORDERBY_WEIGHT")}, ]) %} @@ -104,7 +104,7 @@ {'label': _T("By", "objectslend"), 'order': constant("GaletteObjectsLend\\Repository\\Objects::ORDERBY_MEMBER")}, ]) %} - {% if lendsprefs.VIEW_DATE_FORECAST %} + {% if lendsprefs.view_date_forecast %} {% set columns = columns|merge([ { 'label': _T("Return", "objectslend"), 'order': constant("GaletteObjectsLend\\Repository\\Objects::ORDERBY_FDATE"), @@ -128,7 +128,7 @@ {% block search %}
-
+
@@ -141,7 +141,7 @@ {% endfor %}
- {% if lendsprefs.VIEW_CATEGORY and categories|length > 0 %} + {% if lendsprefs.view_category and categories|length > 0 %}
{% for categ in categories %}
- {% if lendsprefs.VIEW_LIST_PRICE_SUM and lendsprefs.VIEW_PRICE and (login.isAdmin() or login.isStaff()) %} + {% if lendsprefs.view_list_price_sum and lendsprefs.view_price and (login.isAdmin() or login.isStaff()) %} {{ categ.getSum()|format_currency('EUR', {}, i18n.getWebID()) }} {% endif %} @@ -227,21 +227,21 @@ {% endif %} {{ filters.highlight(object.getName()|e, 'name')|raw }} - {% if lendsprefs.VIEW_DESCRIPTION %} + {% if lendsprefs.view_description %}
{{ filters.highlight(object.getDescriptionHtml(), 'description')|raw }} {% endif %} - {% if lendsprefs.VIEW_SERIAL %} + {% if lendsprefs.view_serial %} {{ filters.highlight(object.getSerialNumber()|e, 'serial_number')|raw }} {% endif %} - {% if lendsprefs.VIEW_PRICE %} + {% if lendsprefs.view_price %} {{ object.getPrice()|format_currency('EUR', {}, i18n.getWebID()) }} {% endif %} - {% if lendsprefs.VIEW_LEND_PRICE %} + {% if lendsprefs.view_lend_price %} {{ object.getRentPrice()|format_currency('EUR', {}, i18n.getWebID()) }}
{% if object.isPricePerDay() %} @@ -251,12 +251,12 @@ {% endif %} {% endif %} - {% if lendsprefs.VIEW_DIMENSION %} + {% if lendsprefs.view_dimension %} {{ filters.highlight(object.getDimension()|e, 'dimension')|raw }} {% endif %} - {% if lendsprefs.VIEW_WEIGHT %} + {% if lendsprefs.view_weight %} {{ object.getWeight()|format_number({fraction_digit: 3}, 'decimal', 'default', i18n.getWebID()) }} @@ -274,7 +274,7 @@ {{ memberName({'id': object.getIdAdh()}) }} {% else %}-{% endif %} - {% if lendsprefs.VIEW_DATE_FORECAST %} + {% if lendsprefs.view_date_forecast %} {{ object.getDateForecast() }} @@ -282,7 +282,7 @@ {% if login.isAdmin() or login.isStaff() %} {% if object.isActive() %}{{ _T("Active", "objectslend") }}{% else %}{{ _T("Inactive", "objectslend") }}{% endif %} @@ -338,7 +338,7 @@ {% endif %} {% if not object.getRentId() or object.inStock() %} - {% if lendsprefs.ENABLE_MEMBER_RENT_OBJECT or login.isAdmin() or login.isStaff() %} + {% if lendsprefs.enable_member_rent_object or login.isAdmin() or login.isStaff() %} {% set actions = [ { 'label': _T("Take away", "objectslend"), @@ -413,35 +413,7 @@ {{ parent() }} {% endblock %} diff --git a/templates/default/preferences.html.twig b/templates/default/preferences.html.twig index 972cedb1..c0c968a0 100644 --- a/templates/default/preferences.html.twig +++ b/templates/default/preferences.html.twig @@ -6,28 +6,34 @@ {% extends 'page.html.twig' %} -{% macro yesno_checkbox(label, name, tip, yes, no) %} -
+{# labels in one column, toggles in the next: they line up whatever the label length #} +{% macro toggles(boxes, values) %} + {% for box in boxes %}
- -
-
- - -
+
+ {% include "components/forms/tip.html.twig" with { + label: box.label, + for: box.name, + tip: box.tip ?? '', + id: box.name, + html: true, + } only %}
-
-
- - +
+
+ +
-
+ {% endfor %} {% endmacro %} {% block content %} @@ -41,76 +47,47 @@
- {% set yesno_boxes = [ + {{ _self.toggles([ { label: _T("Members can borrow:", "objectslend"), - name: 'ENABLE_MEMBER_RENT_OBJECT', + name: 'enable_member_rent_object', tip: _T("Allow a member (not staff neither admin) to borrow an object. If set to 'No', only admin and staff members can access the 'Take object' page", "objectslend"), - yes: { - name: 'yes_memberborrow', - checked: lendsprefs.ENABLE_MEMBER_RENT_OBJECT == '1' - }, - no: { - name: 'no_memberborrow', - checked: lendsprefs.ENABLE_MEMBER_RENT_OBJECT == '0' - } }, { label: _T("Generate contribution:", "objectslend"), - name: 'AUTO_GENERATE_CONTRIBUTION', + name: 'auto_generate_contribution', tip: _T("Automatically generate a contribution for the member of the amount of the rental price of the object", "objectslend"), - yes: { - name: 'yes_contrib', - checked: lendsprefs.AUTO_GENERATE_CONTRIBUTION == '1' - }, - no: { - name: 'no_contrib', - checked: lendsprefs.AUTO_GENERATE_CONTRIBUTION == '0' - } } - ] %} - - {% for yesno_box in yesno_boxes %} - {{ _self.yesno_checkbox( - yesno_box.label, - yesno_box.name, - yesno_box.tip, - yesno_box.yes, - yesno_box.no - ) }} - {% endfor %} - - {% set ctype_list_values = {(0): _T("Choose a contribution type", "objectslend")} %} - {% for id, ctype in ctypes %} - {% set ctype_list_values = ctype_list_values + {(id): ctype} %} - {% endfor %} - - {# TODO: hide this one if AUTO_GENERATE_CONTRIBUTION is off #} -
- -
@@ -121,58 +98,33 @@
{% include "components/forms/number.html.twig" with { id: 'max_thumb_height', - name: 'THUMB_MAX_HEIGHT', - value: lendsprefs.THUMB_MAX_HEIGHT, + name: 'pref_objectslend_thumb_max_height', + value: lendsprefs.thumb_max_height, label: _T("Max thumb height (in px)", "objectslend"), } %} {% include "components/forms/number.html.twig" with { id: 'max_thumb_width', - name: 'THUMB_MAX_WIDTH', - value: lendsprefs.THUMB_MAX_WIDTH, + name: 'pref_objectslend_thumb_max_width', + value: lendsprefs.thumb_max_width, label: _T("Max thumb width (in px)", "objectslend"), } %} - {% set yesno_boxes = [ + {% include "components/forms/number.html.twig" with { + id: 'upload_size_images', + name: 'pref_objectslend_upload_size_images', + value: lendsprefs.upload_size_images, + label: _T("Max picture size (in Ko)", "objectslend"), + description: _T("0 to use the Galette setting for pictures, currently %1$s", "objectslend")|format(callstatic('\\Galette\\Util\\Filesize', 'fromKilobytes', preferences.pref_upload_size_images))|e, + } %} + + {{ _self.toggles([ { label: _T("Images in lists:", "objectslend"), - name: 'VIEW_THUMBNAIL', - title: _T("Display images in objects and categories lists", "objectslend"), + name: 'view_thumbnail', tip: _T("Display images in objects and categories lists", "objectslend"), - yes: { - name: 'yes_view_thumb', - checked: lendsprefs.VIEW_THUMBNAIL == '1' - }, - no: { - name: 'no_view_thumb', - checked: lendsprefs.VIEW_THUMBNAIL == '0' - } - }, - { - label: _T("Enable fullsize:", "objectslend"), - name: 'VIEW_FULLSIZE', - title: _T("Show fullsize image or just thumbs", "objectslend"), - tip: _T("Will permit to see fullsize image clicking on thumbnails.
If disabled, only thumbnails will be displayed, but full images are still kept.", "objectslend"), - yes: { - name: 'yes_view_fullsize', - checked: lendsprefs.VIEW_FULLSIZE == '1' - }, - no: { - name: 'no_view_fullsize', - checked: lendsprefs.VIEW_FULLSIZE == '0' - } } - ] %} - - {% for yesno_box in yesno_boxes %} - {{ _self.yesno_checkbox( - yesno_box.label, - yesno_box.name, - yesno_box.tip, - yesno_box.yes, - yesno_box.no - ) }} - {% endfor %} + ], lendsprefs) }}
@@ -180,128 +132,21 @@
- {% set yesno_boxes = [ - { - label: _T("View category:", "objectslend"), - name: 'VIEW_CATEGORY', - yes: { - name: 'yes_view_category', - checked: lendsprefs.VIEW_CATEGORY == '1' - }, - no: { - name: 'no_view_category', - checked: lendsprefs.VIEW_CATEGORY == '0' - } - }, - { - label: _T("View forecast return date:", "objectslend"), - name: 'VIEW_DATE_FORECAST', - yes: { - name: 'yes_view_dateforecast', - checked: lendsprefs.VIEW_DATE_FORECAST == '1' - }, - no: { - name: 'no_view_dateforecats', - checked: lendsprefs.VIEW_DATE_FORECAST == '0' - } - }, - { - label: _T("View description", "objectslend"), - name: 'VIEW_DESCRIPTION', - yes: { - name: 'yes_view_description', - checked: lendsprefs.VIEW_DESCRIPTION == '1' - }, - no: { - name: 'no_view_description', - checked: lendsprefs.VIEW_DESCRIPTION == '0' - } - }, - { - label: _T("View dimensions:", "objectslend"), - name: 'VIEW_DIMENSION', - yes: { - name: 'yes_view_dimension', - checked: lendsprefs.VIEW_DIMENSION == '1' - }, - no: { - name: 'no_view_dimension', - checked: lendsprefs.VIEW_DIMENSION == '0' - } - }, - { - label: _T("View borrow price:", "objectslend"), - name: 'VIEW_LEND_PRICE', - yes: { - name: 'yes_view_lendprice', - checked: lendsprefs.VIEW_LEND_PRICE == '1' - }, - no: { - name: 'no_view_lendprice', - checked: lendsprefs.VIEW_LEND_PRICE == '0' - } - }, + {{ _self.toggles([ + {label: _T("View category:", "objectslend"), name: 'view_category'}, + {label: _T("View forecast return date:", "objectslend"), name: 'view_date_forecast'}, + {label: _T("View description", "objectslend"), name: 'view_description'}, + {label: _T("View dimensions:", "objectslend"), name: 'view_dimension'}, + {label: _T("View borrow price:", "objectslend"), name: 'view_lend_price'}, { label: _T("View price sum:", "objectslend"), - name: 'VIEW_LIST_PRICE_SUM', + name: 'view_list_price_sum', tip: _T("View the objects buy price sum on the list under the category", "objectslend"), - yes: { - name: 'yes_view_pricesum', - checked: lendsprefs.VIEW_LIST_PRICE_SUM == '1' - }, - no: { - name: 'no_view_pricesum', - checked: lendsprefs.VIEW_LIST_PRICE_SUM == '0' - } }, - { - label: _T("View buy price:", "objectslend"), - name: 'VIEW_PRICE', - yes: { - name: 'yes_view_price', - checked: lendsprefs.VIEW_PRICE == '1' - }, - no: { - name: 'no_view_price', - checked: lendsprefs.VIEW_PRICE == '0' - } - }, - { - label: _T("View serial number:", "objectslend"), - name: 'VIEW_SERIAL', - yes: { - name: 'yes_view_serial', - checked: lendsprefs.VIEW_SERIAL == '1' - }, - no: { - name: 'no_view_serial', - checked: lendsprefs.VIEW_SERIAL == '0' - } - }, - { - label: _T("View weight", "objectslend"), - name: 'VIEW_WEIGHT', - yes: { - name: 'yes_view_weight', - checked: lendsprefs.VIEW_WEIGHT == '1' - }, - no: { - name: 'no_view_weight', - checked: lendsprefs.VIEW_WEIGHT == '0' - } - }, - - ] %} - - {% for yesno_box in yesno_boxes %} - {{ _self.yesno_checkbox( - yesno_box.label, - yesno_box.name, - yesno_box.tip ?? null, - yesno_box.yes, - yesno_box.no - ) }} - {% endfor %} + {label: _T("View buy price:", "objectslend"), name: 'view_price'}, + {label: _T("View serial number:", "objectslend"), name: 'view_serial'}, + {label: _T("View weight", "objectslend"), name: 'view_weight'}, + ], lendsprefs) }}
@@ -312,4 +157,33 @@
+ +{% if sample_data %} +
+
+ +
+
{{ _T("Sample data", "objectslend") }}
+

{{ _T("The catalog is empty. You can fill it with sample categories, statuses and objects, along with a rents history given to existing members.", "objectslend") }}

+ +
+
+
+{% endif %} +{% endblock %} + +{% block javascripts %} + {% endblock %} diff --git a/templates/default/status_list.html.twig b/templates/default/status_list.html.twig index 6d43ba6d..f5570344 100644 --- a/templates/default/status_list.html.twig +++ b/templates/default/status_list.html.twig @@ -142,19 +142,19 @@ {% if status.isActive() %} - + {{ _T("Active", "objectslend") }} {% else %} - + {{ _T("Inactive", "objectslend") }} {% endif %} {% if status.isInStock() %} - + {{ _T("In stock", "objectslend") }} {% else %} - + {{ _T("Not in stock", "objectslend") }} {% endif %} diff --git a/templates/default/take_object.html.twig b/templates/default/take_object.html.twig index c6f7164e..ee12c44d 100644 --- a/templates/default/take_object.html.twig +++ b/templates/default/take_object.html.twig @@ -24,28 +24,28 @@ alt="{{ _T("Object photo", "objectslend") }}"/>
- {% if lendsprefs.VIEW_DESCRIPTION %} + {% if lendsprefs.view_description %}
{{ object.getDescriptionHtml()|raw }}
{% endif %} - {% if lendsprefs.VIEW_SERIAL %} + {% if lendsprefs.view_serial %}
{{ object.getSerialNumber() }}
{% endif %} - {% if lendsprefs.VIEW_PRICE %} + {% if lendsprefs.view_price %}
{{ object.getPrice()|format_currency('EUR', {}, i18n.getWebID()) }}
{% endif %} - {% if lendsprefs.VIEW_DIMENSION %} + {% if lendsprefs.view_dimension %}
{% if object.getDimension() != '' %} @@ -56,7 +56,7 @@
{% endif %} - {% if lendsprefs.VIEW_WEIGHT %} + {% if lendsprefs.view_weight %}
{% if object.getWeight() %} @@ -128,7 +128,7 @@ } %}
- {% if lendsprefs.AUTO_GENERATE_CONTRIBUTION %} + {% if lendsprefs.auto_generate_contribution %}
{% include "components/forms/payment_types.html.twig" with { show_inline: true, @@ -211,9 +211,7 @@ var _init_takeobject_js = function() { $('button[name="valid"]').addClass('disabled'); - {% if olendsprefs.showFullsize() %} _init_fullimage(); - {% endif %} $('#id_adh, #status, #payment_type').on('change',function() { validStatus() diff --git a/tests/GaletteObjectsLend/Controllers/tests/units/MainController.php b/tests/GaletteObjectsLend/Controllers/tests/units/MainController.php new file mode 100644 index 00000000..a9d26c15 --- /dev/null +++ b/tests/GaletteObjectsLend/Controllers/tests/units/MainController.php @@ -0,0 +1,172 @@ + + */ +class MainController extends GaletteRoutingTestCase +{ + protected int $seed = 20260924101512; + protected bool $load_plugins = true; + + /** @var array */ + private array $orig_prefs; + + /** + * Set up tests + */ + public function setUp(): void + { + parent::setUp(); + $this->orig_prefs = []; + foreach (array_keys(LendPreferences::getSchema()) as $name) { + $this->orig_prefs[$name] = $this->preferences->getPluginValue($name); + } + } + + /** + * Cleanup after each test method + */ + public function tearDown(): void + { + foreach ($this->orig_prefs as $name => $value) { + $this->preferences->setValue($name, $value, $this->login); + } + $this->login->logout(); + parent::tearDown(); + } + + /** + * Preferences page shows stored values + */ + public function testPreferences(): void + { + $this->logSuperAdmin(); + $this->assertTrue($this->preferences->setValue(LendPreferences::THUMB_MAX_WIDTH, 321, $this->login)); + + $test_response = $this->app->handle($this->createRequest(route_name: 'objectslend_preferences')); + $this->assertSame(200, $test_response->getStatusCode()); + $body = (string)$test_response->getBody(); + $this->assertStringContainsString('name="pref_objectslend_thumb_max_width"', $body); + $this->assertStringContainsString('value="321"', $body); + $this->assertStringContainsString('name="pref_objectslend_view_serial"', $body); + } + + /** + * Store preferences: unchecked boxes are off, unknown names are ignored + */ + public function testStorePreferences(): void + { + $this->logSuperAdmin(); + $request = $this->createRequest(route_name: 'store_objectlend_preferences', method: 'POST') + ->withParsedBody([ + LendPreferences::VIEW_SERIAL => '1', + LendPreferences::GENERATED_CONTRIBUTION_TYPE_ID => '3', + LendPreferences::GENERATED_CONTRIB_INFO_TEXT => ' Rent of {NAME} ', + LendPreferences::THUMB_MAX_WIDTH => '200', + 'pref_nom' => 'Hijacked', + ]); + $test_response = $this->app->handle($request); + $this->assertSame(302, $test_response->getStatusCode()); + $this->assertSame( + [$this->routeparser->urlFor('objectslend_preferences')], + $test_response->getHeader('Location') + ); + $this->expectFlashData(['success_detected' => ['Preferences have been successfully stored!']]); + + $lendsprefs = new LendPreferences($this->preferences); + $this->assertTrue($lendsprefs->isEnabled(LendPreferences::VIEW_SERIAL)); + //defaults on, but not posted + $this->assertFalse($lendsprefs->isEnabled(LendPreferences::ENABLE_MEMBER_RENT_OBJECT)); + $this->assertFalse($lendsprefs->imagesInLists()); + $this->assertSame(3, $lendsprefs->getContributionTypeId()); + $this->assertSame('Rent of {NAME}', $lendsprefs->getContributionText()); + $this->assertSame(200, $lendsprefs->getThumbWidth()); + //not posted: kept + $this->assertSame($this->orig_prefs[LendPreferences::THUMB_MAX_HEIGHT], $lendsprefs->getThumbHeight()); + $this->assertNotSame('Hijacked', $this->preferences->pref_nom); + } + + /** + * Invalid value is reported + */ + public function testStoreInvalidPreference(): void + { + $this->logSuperAdmin(); + $request = $this->createRequest(route_name: 'store_objectlend_preferences', method: 'POST') + ->withParsedBody([LendPreferences::THUMB_MAX_WIDTH => '0']); + $test_response = $this->app->handle($request); + $this->assertSame(302, $test_response->getStatusCode()); + //an invalid value is not stored + $this->preferences->load(); + $this->assertNotSame(0, (new LendPreferences($this->preferences))->getThumbWidth()); + + $flash = $this->flash_data['slimFlash'] ?? []; + $this->assertArrayNotHasKey('success_detected', $flash); + $this->assertArrayHasKey('error_detected', $flash); + $this->flash_data = []; + } + + /** + * Preferences are for admins only + */ + public function testStorePreferencesAsMember(): void + { + $mdata = $this->dataAdherentOne(); + $this->getMemberOne(); + $this->assertTrue($this->login->login($mdata['login_adh'], $mdata['mdp_adh'])); + $request = $this->createRequest(route_name: 'store_objectlend_preferences', method: 'POST') + ->withParsedBody([LendPreferences::THUMB_MAX_WIDTH => '222']); + $this->app->handle($request); + $this->assertNotSame(222, (new LendPreferences($this->preferences))->getThumbWidth()); + } + + /** + * Sample data are offered and loaded on an empty catalog only + */ + public function testLoadSampleData(): void + { + $this->zdb->execute($this->zdb->update(LEND_PREFIX . LendObject::TABLE)->set([LendRent::PK => null])); + $this->zdb->execute($this->zdb->delete(LEND_PREFIX . LendRent::TABLE)); + $this->zdb->execute($this->zdb->delete(LEND_PREFIX . LendObject::TABLE)); + + $this->logSuperAdmin(); + $body = (string)$this->app->handle($this->createRequest(route_name: 'objectslend_preferences'))->getBody(); + $this->assertStringContainsString($this->routeparser->urlFor('objectslend_sample_data'), $body); + + $request = $this->createRequest(route_name: 'objectslend_sample_data', method: 'POST'); + $test_response = $this->app->handle($request); + $this->assertSame(302, $test_response->getStatusCode()); + $this->assertSame( + [$this->routeparser->urlFor('objectslend_objects')], + $test_response->getHeader('Location') + ); + $flash = $this->flash_data['slimFlash'] ?? []; + $this->flash_data = []; + $this->assertArrayHasKey('success_detected', $flash); + $this->assertStringStartsWith('Sample data loaded: 16 objects', $flash['success_detected'][0]); + + //no longer offered, and refused + $body = (string)$this->app->handle($this->createRequest(route_name: 'objectslend_preferences'))->getBody(); + $this->assertStringNotContainsString($this->routeparser->urlFor('objectslend_sample_data'), $body); + + $this->app->handle($this->createRequest(route_name: 'objectslend_sample_data', method: 'POST')); + $this->expectFlashData(['error_detected' => ['Sample data can only be loaded into an empty catalog.']]); + } +} diff --git a/tests/GaletteObjectsLend/Controllers/tests/units/ObjectsController.php b/tests/GaletteObjectsLend/Controllers/tests/units/ObjectsController.php index a4bf6220..b9695ded 100644 --- a/tests/GaletteObjectsLend/Controllers/tests/units/ObjectsController.php +++ b/tests/GaletteObjectsLend/Controllers/tests/units/ObjectsController.php @@ -15,7 +15,7 @@ use GaletteObjectsLend\Entity\LendObject; use GaletteObjectsLend\Entity\LendRent; use GaletteObjectsLend\Entity\LendStatus; -use GaletteObjectsLend\Entity\Preferences; +use GaletteObjectsLend\LendPreferences; /** * Objects controller tests @@ -40,8 +40,10 @@ public function setUp(): void { parent::setUp(); - $prefs = new Preferences($this->zdb); - $this->orig_prefs = $prefs->getPreferences(); + $this->orig_prefs = []; + foreach (array_keys(LendPreferences::getSchema()) as $name) { + $this->orig_prefs[$name] = $this->preferences->getPluginValue($name); + } $status = new LendStatus($this->zdb); $status->setText('In stock'); @@ -70,8 +72,9 @@ public function tearDown(): void { $this->login->logout(); - $prefs = new Preferences($this->zdb); - $prefs->store($this->orig_prefs); + foreach ($this->orig_prefs as $name => $value) { + $this->preferences->setValue($name, $value, $this->login); + } $update = $this->zdb->update(LEND_PREFIX . LendObject::TABLE); $update->set([LendRent::PK => null]); @@ -102,13 +105,15 @@ public function tearDown(): void */ private function setPrefs(bool $member_rent, bool $auto_contrib = false): void { - $prefs = new Preferences($this->zdb); - $values = $this->orig_prefs; - $values[Preferences::PARAM_ENABLE_MEMBER_RENT_OBJECT] = (int)$member_rent; - $values[Preferences::PARAM_AUTO_GENERATE_CONTRIBUTION] = (int)$auto_contrib; - $values[Preferences::PARAM_GENERATED_CONTRIBUTION_TYPE_ID] = 5; - $values[Preferences::PARAM_GENERATED_CONTRIB_INFO_TEXT] = 'Rent of {NAME} ({SERIAL_NUMBER})'; - $this->assertTrue($prefs->store($values)); + $values = [ + LendPreferences::ENABLE_MEMBER_RENT_OBJECT => (int)$member_rent, + LendPreferences::AUTO_GENERATE_CONTRIBUTION => (int)$auto_contrib, + LendPreferences::GENERATED_CONTRIBUTION_TYPE_ID => 5, + LendPreferences::GENERATED_CONTRIB_INFO_TEXT => 'Rent of {NAME} ({SERIAL_NUMBER})', + ]; + foreach ($values as $name => $value) { + $this->assertTrue($this->preferences->setValue($name, $value, $this->login)); + } } /** @@ -465,7 +470,7 @@ public function testFilterOnField(): void $filters = $this->session->plugin_objectslend_objects_filter; $this->assertSame(\GaletteObjectsLend\Repository\Objects::FILTER_SERIAL, $filters->field_filter); - $objects = new \GaletteObjectsLend\Repository\Objects($this->zdb, $this->preferences, $this->login, new Preferences($this->zdb), $filters); + $objects = new \GaletteObjectsLend\Repository\Objects($this->zdb, $this->preferences, $this->login, new LendPreferences($this->preferences), $filters); $list = $objects->getObjectsList(true); $this->assertCount(1, $list); } diff --git a/tests/GaletteObjectsLend/Entity/tests/units/LendObject.php b/tests/GaletteObjectsLend/Entity/tests/units/LendObject.php index 2a09ce8c..e2657171 100644 --- a/tests/GaletteObjectsLend/Entity/tests/units/LendObject.php +++ b/tests/GaletteObjectsLend/Entity/tests/units/LendObject.php @@ -20,6 +20,7 @@ class LendObject extends GaletteTestCase { protected int $seed = 20240522000325; + protected bool $load_plugins = true; private int $active_category_id; private int $inactive_category_id; @@ -93,7 +94,7 @@ public function testCrud(): void $this->zdb, $this->preferences, $this->login, - new \GaletteObjectsLend\Entity\Preferences($this->zdb) + new \GaletteObjectsLend\LendPreferences($this->preferences) ); $object = new \GaletteObjectsLend\Entity\LendObject($this->zdb); diff --git a/tests/GaletteObjectsLend/Entity/tests/units/Preferences.php b/tests/GaletteObjectsLend/Entity/tests/units/Preferences.php deleted file mode 100644 index 6b584c4f..00000000 --- a/tests/GaletteObjectsLend/Entity/tests/units/Preferences.php +++ /dev/null @@ -1,118 +0,0 @@ - - */ -class Preferences extends GaletteTestCase -{ - protected int $seed = 20240519131740; - - /** - * Test defaults - */ - public function testDefaults(): void - { - $prefs = new \GaletteObjectsLend\Entity\Preferences($this->zdb); - $this->assertSame(128, $prefs->getThumbWidth()); - $this->assertSame(128, $prefs->getThumbHeight()); - $this->assertTrue($prefs->imagesInLists()); - $this->assertTrue($prefs->showFullsize()); - - $all_prefs = $prefs->getPreferences(); - $this->assertCount(17, $all_prefs); - foreach ($all_prefs as $code => $value) { - $this->assertSame($value, $prefs->$code); - } - - $prefs = new \GaletteObjectsLend\Entity\Preferences($this->zdb, false); - $this->assertTrue($prefs->load()); - - $prefs = new \GaletteObjectsLend\Entity\Preferences($this->zdb, false); - $this->assertCount(17, $prefs->getPreferences()); - - $this->expectException(\RuntimeException::class); - $this->assertSame(null, $prefs->NON_EXISTING); // @phpstan-ignore property.notFound - } - - /** - * Test add and update - */ - public function testCrud(): void - { - $prefs = new \GaletteObjectsLend\Entity\Preferences($this->zdb); - $orig_prefs = $prefs->getPreferences(); - $this->assertCount(17, $orig_prefs); - - $all_prefs = $orig_prefs; - - $this->assertSame(128, (int)$all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_THUMB_MAX_WIDTH]); - $this->assertSame(128, (int)$all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_THUMB_MAX_HEIGHT]); - $all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_THUMB_MAX_WIDTH] = 256; - $all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_THUMB_MAX_HEIGHT] = 256; - - $this->assertTrue($prefs->store($all_prefs)); - $all_prefs = $prefs->getPreferences(); - $this->assertCount(17, $all_prefs); - $this->assertSame(256, (int)$all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_THUMB_MAX_WIDTH]); - $this->assertSame(256, (int)$all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_THUMB_MAX_HEIGHT]); - - $this->assertTrue($prefs->store($orig_prefs)); - } - - /** - * Stored values are read back from the database - */ - public function testReload(): void - { - $prefs = new \GaletteObjectsLend\Entity\Preferences($this->zdb); - $orig_prefs = $prefs->getPreferences(); - - $all_prefs = $orig_prefs; - $all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_ENABLE_MEMBER_RENT_OBJECT] = 0; - $all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_AUTO_GENERATE_CONTRIBUTION] = 1; - $all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_GENERATED_CONTRIBUTION_TYPE_ID] = 3; - $all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_GENERATED_CONTRIB_INFO_TEXT] = 'Rent of {NAME}'; - $all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_THUMB_MAX_WIDTH] = 200; - $this->assertTrue($prefs->store($all_prefs)); - - try { - $prefs = new \GaletteObjectsLend\Entity\Preferences($this->zdb); - $this->assertFalse((bool)$prefs->{\GaletteObjectsLend\Entity\Preferences::PARAM_ENABLE_MEMBER_RENT_OBJECT}); - $this->assertTrue((bool)$prefs->{\GaletteObjectsLend\Entity\Preferences::PARAM_AUTO_GENERATE_CONTRIBUTION}); - $this->assertSame( - 3, - (int)$prefs->{\GaletteObjectsLend\Entity\Preferences::PARAM_GENERATED_CONTRIBUTION_TYPE_ID} - ); - $this->assertSame( - 'Rent of {NAME}', - $prefs->{\GaletteObjectsLend\Entity\Preferences::PARAM_GENERATED_CONTRIB_INFO_TEXT} - ); - $this->assertSame(200, $prefs->getThumbWidth()); - $this->assertSame(128, $prefs->getThumbHeight()); - $this->assertCount(17, $prefs->getPreferences()); - } finally { - $this->assertTrue($prefs->store($orig_prefs)); - } - - $prefs = new \GaletteObjectsLend\Entity\Preferences($this->zdb); - $this->assertSame(128, $prefs->getThumbWidth()); - $this->assertSame( - (string)$orig_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_GENERATED_CONTRIB_INFO_TEXT], - (string)$prefs->{\GaletteObjectsLend\Entity\Preferences::PARAM_GENERATED_CONTRIB_INFO_TEXT} - ); - } -} diff --git a/tests/GaletteObjectsLend/Repository/tests/units/Objects.php b/tests/GaletteObjectsLend/Repository/tests/units/Objects.php index 2931298f..5bd2fb7b 100644 --- a/tests/GaletteObjectsLend/Repository/tests/units/Objects.php +++ b/tests/GaletteObjectsLend/Repository/tests/units/Objects.php @@ -20,8 +20,9 @@ class Objects extends GaletteTestCase { protected int $seed = 20240526224135; + protected bool $load_plugins = true; - protected \GaletteObjectsLend\Entity\Preferences $lend_prefs; + protected \GaletteObjectsLend\LendPreferences $lend_prefs; /** * Set up tests @@ -29,7 +30,7 @@ class Objects extends GaletteTestCase public function setUp(): void { parent::setUp(); - $this->lend_prefs = new \GaletteObjectsLend\Entity\Preferences($this->zdb); + $this->lend_prefs = new \GaletteObjectsLend\LendPreferences($this->preferences); } /** @@ -153,17 +154,18 @@ public function testGetList(): void $this->assertSame(2, $objects->getCount()); //disable view description - $orig_prefs = $this->lend_prefs->getPreferences(); - $all_prefs = $orig_prefs; - $all_prefs[\GaletteObjectsLend\Entity\Preferences::PARAM_VIEW_DESCRIPTION] = 0; - $this->assertTrue($this->lend_prefs->store($all_prefs)); + $this->assertTrue( + $this->preferences->setValue(\GaletteObjectsLend\LendPreferences::VIEW_DESCRIPTION, 0, $this->login) + ); //only one result (first in name only) $this->assertCount(1, $objects->getObjectsList(true)); $this->assertSame(1, $objects->getCount()); //reset prefs - $this->assertTrue($this->lend_prefs->store($orig_prefs)); + $this->assertTrue( + $this->preferences->setValue(\GaletteObjectsLend\LendPreferences::VIEW_DESCRIPTION, 1, $this->login) + ); $filters->field_filter = \GaletteObjectsLend\Repository\Objects::FILTER_ID; $filters->filter_str = (string)$third_object_id; diff --git a/tests/GaletteObjectsLend/tests/units/LendPreferences.php b/tests/GaletteObjectsLend/tests/units/LendPreferences.php new file mode 100644 index 00000000..865035e1 --- /dev/null +++ b/tests/GaletteObjectsLend/tests/units/LendPreferences.php @@ -0,0 +1,95 @@ + + */ +class LendPreferences extends GaletteTestCase +{ + protected int $seed = 20240519131740; + protected bool $load_plugins = true; + + /** + * Preferences are declared to core, with their defaults + */ + public function testDefaults(): void + { + $schema = \GaletteObjectsLend\LendPreferences::getSchema(); + $this->assertCount(17, $schema); + foreach (array_keys($schema) as $name) { + $this->assertStringStartsWith(\GaletteObjectsLend\LendPreferences::PREFIX, $name); + $this->assertSame('objectslend', PreferencesSchema::getOwner($name)); + } + + $prefs = new \GaletteObjectsLend\LendPreferences($this->preferences); + $this->assertSame(128, $prefs->getThumbWidth()); + $this->assertSame(128, $prefs->getThumbHeight()); + $this->assertTrue($prefs->imagesInLists()); + $this->assertTrue($prefs->isEnabled(\GaletteObjectsLend\LendPreferences::ENABLE_MEMBER_RENT_OBJECT)); + $this->assertFalse($prefs->isEnabled(\GaletteObjectsLend\LendPreferences::VIEW_SERIAL)); + $this->assertSame(5, $prefs->getContributionTypeId()); + $this->assertSame('Location de {NAME} {DESCRIPTION} {SERIAL_NUMBER}', $prefs->getContributionText()); + + $values = $prefs->toArray(); + $this->assertCount(17, $values); + $this->assertSame(128, $values['thumb_max_width']); + $this->assertFalse($values['view_serial']); + } + + /** + * Values are read from core preferences + */ + public function testChange(): void + { + $prefs = new \GaletteObjectsLend\LendPreferences($this->preferences); + $this->assertTrue( + $this->preferences->setValue(\GaletteObjectsLend\LendPreferences::THUMB_MAX_WIDTH, 256, $this->login) + ); + $this->assertTrue( + $this->preferences->setValue(\GaletteObjectsLend\LendPreferences::VIEW_SERIAL, 1, $this->login) + ); + $this->assertSame(256, $prefs->getThumbWidth()); + $this->assertTrue($prefs->isEnabled(\GaletteObjectsLend\LendPreferences::VIEW_SERIAL)); + + $this->assertFalse( + $this->preferences->setValue(\GaletteObjectsLend\LendPreferences::THUMB_MAX_HEIGHT, -3, $this->login) + ); + //an invalid value is not stored + $this->preferences->load(); + $this->assertSame(128, $prefs->getThumbHeight()); + } + + /** + * Pictures follow core images size unless the plugin sets its own + */ + public function testUploadSize(): void + { + $prefs = new \GaletteObjectsLend\LendPreferences($this->preferences); + $this->assertSame(\Galette\IO\File::MAX_FILE_SIZE, $prefs->getUploadSize()); + + $this->assertTrue($this->preferences->setValue('pref_upload_size_images', 4096, $this->login)); + $this->assertSame(4096, $prefs->getUploadSize()); + + $this->assertTrue( + $this->preferences->setValue(\GaletteObjectsLend\LendPreferences::UPLOAD_SIZE_IMAGES, 512, $this->login) + ); + $this->assertSame(512, $prefs->getUploadSize()); + + $picture = (new \GaletteObjectsLend\Entity\ObjectPicture())->setMaxLength($prefs->getUploadSize()); + $this->assertSame(512, $picture->getMaxLength()); + } +} diff --git a/tests/GaletteObjectsLend/tests/units/LendService.php b/tests/GaletteObjectsLend/tests/units/LendService.php index daf44a04..4b8beabf 100644 --- a/tests/GaletteObjectsLend/tests/units/LendService.php +++ b/tests/GaletteObjectsLend/tests/units/LendService.php @@ -15,7 +15,7 @@ use GaletteObjectsLend\Entity\LendObject; use GaletteObjectsLend\Entity\LendRent; use GaletteObjectsLend\Entity\LendStatus; -use GaletteObjectsLend\Entity\Preferences; +use GaletteObjectsLend\LendPreferences; use GaletteObjectsLend\LendException; /** @@ -29,6 +29,7 @@ class LendService extends GaletteTestCase { protected int $seed = 20260923081245; protected bool $db_transactions = false; + protected bool $load_plugins = true; private int $instock_status; private int $lent_status; @@ -43,14 +44,19 @@ public function setUp(): void { parent::setUp(); - $prefs = new Preferences($this->zdb); - $this->orig_prefs = $prefs->getPreferences(); - $values = $this->orig_prefs; - $values[Preferences::PARAM_ENABLE_MEMBER_RENT_OBJECT] = 1; - $values[Preferences::PARAM_AUTO_GENERATE_CONTRIBUTION] = 1; - $values[Preferences::PARAM_GENERATED_CONTRIBUTION_TYPE_ID] = 5; - $values[Preferences::PARAM_GENERATED_CONTRIB_INFO_TEXT] = 'Service rent of {NAME}'; - $this->assertTrue($prefs->store($values)); + $this->orig_prefs = []; + foreach (array_keys(LendPreferences::getSchema()) as $name) { + $this->orig_prefs[$name] = $this->preferences->getPluginValue($name); + } + $values = [ + LendPreferences::ENABLE_MEMBER_RENT_OBJECT => 1, + LendPreferences::AUTO_GENERATE_CONTRIBUTION => 1, + LendPreferences::GENERATED_CONTRIBUTION_TYPE_ID => 5, + LendPreferences::GENERATED_CONTRIB_INFO_TEXT => 'Service rent of {NAME}', + ]; + foreach ($values as $name => $value) { + $this->assertTrue($this->preferences->setValue($name, $value, $this->login)); + } $status = new LendStatus($this->zdb); $status->setText('In stock'); @@ -80,8 +86,9 @@ public function tearDown(): void { $this->login->logout(); - $prefs = new Preferences($this->zdb); - $prefs->store($this->orig_prefs); + foreach ($this->orig_prefs as $name => $value) { + $this->preferences->setValue($name, $value, $this->login); + } $update = $this->zdb->update(LEND_PREFIX . LendObject::TABLE) ->set([LendRent::PK => null]); @@ -109,7 +116,7 @@ public function tearDown(): void */ private function getService(): \GaletteObjectsLend\LendService { - return new \GaletteObjectsLend\LendService($this->zdb, $this->preferences, $this->login, new Preferences($this->zdb)); + return new \GaletteObjectsLend\LendService($this->zdb, $this->preferences, $this->login, new LendPreferences($this->preferences)); } /** diff --git a/tests/GaletteObjectsLend/tests/units/SampleData.php b/tests/GaletteObjectsLend/tests/units/SampleData.php new file mode 100644 index 00000000..393c186a --- /dev/null +++ b/tests/GaletteObjectsLend/tests/units/SampleData.php @@ -0,0 +1,151 @@ + + */ +class SampleData extends GaletteTestCase +{ + protected int $seed = 20260924143015; + protected bool $load_plugins = true; + + /** + * Set up tests: sample data need an empty catalog + */ + public function setUp(): void + { + parent::setUp(); + $this->emptyCatalog(); + } + + /** + * Cleanup after each test method + */ + public function tearDown(): void + { + $this->emptyCatalog(); + parent::tearDown(); + } + + /** + * Remove objects, rents, categories and statuses + */ + private function emptyCatalog(): void + { + $this->zdb->execute($this->zdb->update(LEND_PREFIX . LendObject::TABLE)->set([LendRent::PK => null])); + foreach ([LendRent::TABLE, LendObject::TABLE, LendCategory::TABLE, LendStatus::TABLE] as $table) { + $this->zdb->execute($this->zdb->delete(LEND_PREFIX . $table)); + } + } + + /** + * Count rows of a plugin table + * + * @param string $table Table name, without prefixes + */ + private function countRows(string $table): int + { + return $this->zdb->execute($this->zdb->select(LEND_PREFIX . $table))->count(); + } + + /** + * Load sample data + */ + public function testLoad(): void + { + $data = json_decode( + (string)file_get_contents(\GaletteObjectsLend\SampleData::FILE), + true, + flags: JSON_THROW_ON_ERROR + ); + $this->assertIsArray($data); + + //an existing category is reused + $category = new LendCategory($this->zdb); + $category->setName($data['categories'][0]['name'])->setActive(true); + $category->store(); + + $member = $this->getMemberOne(); + $sample = new \GaletteObjectsLend\SampleData($this->zdb); + $this->assertFalse($sample->hasObjects()); + + $created = $sample->load([(int)$member->id]); + $this->assertTrue($sample->hasObjects()); + + $nb_rents = array_sum(array_map(fn($o) => count($o['rents']), $data['objects'])); + $this->assertSame( + [ + 'categories' => count($data['categories']) - 1, + 'statuses' => count($data['statuses']), + 'objects' => count($data['objects']), + 'rents' => $nb_rents + ], + $created + ); + $this->assertSame(count($data['categories']), $this->countRows(LendCategory::TABLE)); + $this->assertSame(count($data['objects']), $this->countRows(LendObject::TABLE)); + $this->assertSame($nb_rents, $this->countRows(LendRent::TABLE)); + + //every object has a current rent, the last one, still open + $rents_repo = new Rents($this->zdb); + foreach ($this->zdb->execute($this->zdb->select(LEND_PREFIX . LendObject::TABLE)) as $row) { + $this->assertNotNull($row->rent_id, $row->name); + $rents = $rents_repo->getForObject((int)$row->object_id); + $open = array_filter($rents, fn(LendRent $rent) => $rent->getDateEnd() === ''); + $this->assertCount(1, $open, $row->name); + $this->assertSame((int)$row->rent_id, array_values($open)[0]->getId()); + } + + //rents with a member are given to the only one available + $select = $this->zdb->select(LEND_PREFIX . LendRent::TABLE); + $select->where->isNotNull('adherent_id'); + foreach ($this->zdb->execute($select) as $row) { + $this->assertSame((int)$member->id, (int)$row->adherent_id); + } + } + + /** + * Rents are anonymous without members + */ + public function testLoadWithoutMembers(): void + { + (new \GaletteObjectsLend\SampleData($this->zdb))->load([]); + $select = $this->zdb->select(LEND_PREFIX . LendRent::TABLE); + $select->where->isNotNull('adherent_id'); + $this->assertSame(0, $this->zdb->execute($select)->count()); + $this->assertGreaterThan(0, $this->countRows(LendRent::TABLE)); + } + + /** + * Nothing is kept when loading fails + */ + public function testInvalidFile(): void + { + $file = tempnam(sys_get_temp_dir(), 'lend'); + file_put_contents($file, '{"categories": []}'); + try { + $this->expectException(\RuntimeException::class); + (new \GaletteObjectsLend\SampleData($this->zdb, $file))->load([]); + } finally { + unlink($file); + } + } +}