Comment: | Add ability to copy users from a service to the other, also abiliy to subscribe multiple users at once |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk | stable |
Files: | files | file ages | folders |
SHA3-256: |
30c4d002309ac94f05c5d21af231674f |
User & Date: | bohwaz on 2021-12-22 19:36:02 |
Other Links: | manifest | tags |
2021-12-22
| ||
20:01 | • Fixed ticket [05f066665f]: Pouvoir inscrire plusieurs membres à une activité en une fois plus 6 other changes artifact: 57b5d1f5bb user: bohwaz | |
2021-12-22
| ||
19:39 | Fix link to subscribe service from user context check-in: c5cd56ee67 user: bohwaz tags: trunk, stable | |
19:36 | Add ability to copy users from a service to the other, also abiliy to subscribe multiple users at once check-in: 30c4d00230 user: bohwaz tags: trunk, stable | |
17:59 | Fix analytical account order in new transaction form check-in: 2a41a4de0c user: bohwaz tags: trunk, stable | |
Modified src/include/lib/Garradin/Entities/Services/Service.php from [1865b4e6a7] to [93f0e77858].
1 2 3 4 5 6 7 8 9 10 11 12 | <?php namespace Garradin\Entities\Services; use Garradin\Config; use Garradin\DynamicList; use Garradin\Entity; use Garradin\ValidationException; use Garradin\Utils; use Garradin\Services\Fees; class Service extends Entity | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 | <?php namespace Garradin\Entities\Services; use Garradin\Config; use Garradin\DB; use Garradin\DynamicList; use Garradin\Entity; use Garradin\ValidationException; use Garradin\Utils; use Garradin\Services\Fees; class Service extends Entity |
︙ | ︙ | |||
128 129 130 131 132 133 134 | public function expiredUsersList(): DynamicList { $list = $this->paidUsersList(); $conditions = sprintf('su.id_service = %d AND su.expiry_date < date() AND m.id_category NOT IN (SELECT id FROM users_categories WHERE hidden = 1)', $this->id()); $list->setConditions($conditions); return $list; } | | > > > > > > | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | public function expiredUsersList(): DynamicList { $list = $this->paidUsersList(); $conditions = sprintf('su.id_service = %d AND su.expiry_date < date() AND m.id_category NOT IN (SELECT id FROM users_categories WHERE hidden = 1)', $this->id()); $list->setConditions($conditions); return $list; } public function getUsersIds(bool $paid_only = false) { $where = $paid_only ? 'AND paid = 1' : ''; $sql = sprintf('SELECT id_user, id_user FROM services_users WHERE id_service = ? %s;', $where); return DB::getInstance()->getAssoc($sql, $this->id()); } } |
Modified src/include/lib/Garradin/Entities/Services/Service_User.php from [70686305f9] to [976537b27f].
︙ | ︙ | |||
137 138 139 140 141 142 143 | $transaction->importFromNewForm($source); $transaction->save(); $transaction->linkToUser($this->id_user, $this->id()); return $transaction; } | | > | | | > | | | | | | | | | | | | | | | | > | 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | $transaction->importFromNewForm($source); $transaction->save(); $transaction->linkToUser($this->id_user, $this->id()); return $transaction; } static public function createFromForm(array $users, int $creator_id, ?array $source = null) { if (null === $source) { $source = $_POST; } $db = DB::getInstance(); $db->begin(); foreach ($users as $id) { $su = new self; $su->date = new \DateTime; $su->importForm($source); $su->id_user = (int) $id; if ($su->id_fee && $su->fee()->id_account && $su->id_user) { $su->expected_amount = $su->fee()->getAmountForUser($su->id_user); } $su->save(); if ($su->id_fee && $su->fee()->id_account && !empty($source['amount']) && !empty($source['create_payment'])) { try { $su->addPayment($creator_id, $source); } catch (ValidationException $e) { if ($e->getMessage() == 'Il n\'est pas possible de créer ou modifier une écriture dans un exercice clôturé') { throw new ValidationException('Impossible d\'enregistrer l\'inscription : ce tarif d\'activité est lié à un exercice clôturé. Merci de modifier le tarif et choisir un autre exercice.', 0, $e); } else { throw $e; } } } } $db->commit(); return $su; } } |
Modified src/include/lib/Garradin/Template.php from [d782a14099] to [4d1a94bb07].
︙ | ︙ | |||
404 405 406 407 408 409 410 411 412 413 414 415 416 417 | array_walk($attributes_string, function (&$v, $k) { $v = sprintf('%s="%s"', $k, $v); }); $attributes_string = implode(' ', $attributes_string); if ($type == 'select') { $input = sprintf('<select %s>', $attributes_string); foreach ($options as $_key => $_value) { $input .= sprintf('<option value="%s"%s>%s</option>', $_key, $current_value == $_key ? ' selected="selected"' : '', $this->escape($_value)); } | > > > > > > > | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 | array_walk($attributes_string, function (&$v, $k) { $v = sprintf('%s="%s"', $k, $v); }); $attributes_string = implode(' ', $attributes_string); if ($type == 'radio-btn') { $radio = self::formInput(array_merge($params, ['type' => 'radio', 'label' => null, 'help' => null])); $out = sprintf('<dd class="radio-btn">%s <label for="f_%s_%s"><div><h3>%s</h3>%s</div></label> </dd>', $radio, htmlspecialchars($name), htmlspecialchars($value), htmlspecialchars($label), isset($params['help']) ? '<p>' . htmlspecialchars($params['help']) . '</p>' : ''); return $out; } if ($type == 'select') { $input = sprintf('<select %s>', $attributes_string); foreach ($options as $_key => $_value) { $input .= sprintf('<option value="%s"%s>%s</option>', $_key, $current_value == $_key ? ' selected="selected"' : '', $this->escape($_value)); } |
︙ | ︙ |
Modified src/templates/admin/membres/_list_actions.tpl from [9f86dc3d7a] to [899906bde5].
1 2 3 4 5 6 7 8 9 10 11 12 13 | <tfoot> <tr> {if $session->canAccess($session::SECTION_USERS, $session::ACCESS_ADMIN)}<td class="check"><input type="checkbox" value="Tout cocher / décocher" id="f_all2" /><label for="f_all2"></label></td>{/if} <td class="actions" colspan="{$colspan}"> <em>Pour les membres cochés :</em> {csrf_field key="membres_action"} <select name="action"> <option value="">— Choisir une action à effectuer —</option> <option value="move">Changer de catégorie</option> {if !isset($export) || $export != false} <option value="csv">Exporter en tableau CSV</option> <option value="ods">Exporter en classeur Office</option> {/if} | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <tfoot> <tr> {if $session->canAccess($session::SECTION_USERS, $session::ACCESS_ADMIN)}<td class="check"><input type="checkbox" value="Tout cocher / décocher" id="f_all2" /><label for="f_all2"></label></td>{/if} <td class="actions" colspan="{$colspan}"> <em>Pour les membres cochés :</em> {csrf_field key="membres_action"} <select name="action"> <option value="">— Choisir une action à effectuer —</option> <option value="move">Changer de catégorie</option> {if !isset($export) || $export != false} <option value="csv">Exporter en tableau CSV</option> <option value="ods">Exporter en classeur Office</option> {/if} <option value="delete">Supprimer le membre</option> </select> <noscript> <input type="submit" value="OK" /> </noscript> </td> </tr> </tfoot> |
Modified src/templates/services/user/_service_user_form.tpl from [10a2edc910] to [75a8977fdd].
1 2 3 4 | <?php assert(isset($create) && is_bool($create)); assert(isset($has_past_services) && is_bool($has_past_services)); assert(isset($current_only) && is_bool($current_only)); | < | 1 2 3 4 5 6 7 8 9 10 11 | <?php assert(isset($create) && is_bool($create)); assert(isset($has_past_services) && is_bool($has_past_services)); assert(isset($current_only) && is_bool($current_only)); assert(isset($form_url) && is_string($form_url)); assert(isset($today) && $today instanceof \DateTimeInterface); assert($create === false || isset($account_targets)); assert(isset($grouped_services) && is_array($grouped_services)); ?> <form method="post" action="{$self_url}" data-focus="1" data-create="{$create|escape:json}"> |
︙ | ︙ | |||
20 21 22 23 24 25 26 | </nav> {/if} <fieldset> <legend>Inscrire un membre à une activité</legend> <dl> | > | > > | > > > > > > > > > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | </nav> {/if} <fieldset> <legend>Inscrire un membre à une activité</legend> <dl> {if $create && $users} <dt>Membres inscrits</dt> {if count($users) <= 10} {foreach from=$users key="id" item="name"} <dd>{$name}<input type="hidden" name="users[{$id}]" value="{$name}" /></dd> {/foreach} {else} <dd>{$users|count} membres sélectionnés</dd> {/if} {elseif $create && $copy_service} <dt>Recopier depuis l'activité</dt> <dd><strong>{$copy_service.label}</strong><input type="hidden" name="copy_service" value="{$copy_service.id}" /></dd> <dd><em>{if $copy_service_only_paid}(seulement les inscriptions marquées comme payées){else}(toutes les inscriptions){/if}</em><input type="hidden" name="copy_service_only_paid" value="{$copy_service_only_paid}" /></dd> {/if} <dt><label for="f_service_ID">Activité</label> <b>(obligatoire)</b></dt> {foreach from=$grouped_services item="service"} <dd class="radio-btn"> {input type="radio" name="id_service" value=$service.id data-duration=$service.duration data-expiry=$service.expiry_date|date_short label=null source=$service_user} <label for="f_id_service_{$service.id}"> |
︙ | ︙ | |||
65 66 67 68 69 70 71 | {foreach from=$service.fees key="service_id" item="fee"} <dd class="radio-btn"> {input type="radio" name="id_fee" value=$fee.id data-user-amount=$fee.user_amount data-account=$fee.id_account data-year=$fee.id_year label=null source=$service_user } <label for="f_id_fee_{$fee.id}"> <div> <h3>{$fee.label}</h3> <p> | < < | > > > > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | {foreach from=$service.fees key="service_id" item="fee"} <dd class="radio-btn"> {input type="radio" name="id_fee" value=$fee.id data-user-amount=$fee.user_amount data-account=$fee.id_account data-year=$fee.id_year label=null source=$service_user } <label for="f_id_fee_{$fee.id}"> <div> <h3>{$fee.label}</h3> <p> {if $fee.user_amount && $fee.formula} <strong>{$fee.user_amount|raw|money_currency}</strong> (montant calculé) {elseif $fee.formula} montant calculé, variable selon les membres {elseif $fee.user_amount} <strong>{$fee.user_amount|raw|money_currency}</strong> {else} prix libre ou gratuit {/if} </p> {if $fee.description} <p class="help"> {$fee.description|escape|nl2br} </p> {/if} |
︙ | ︙ | |||
118 119 120 121 122 123 124 | </fieldset> {/if} <p class="submit"> {csrf_field key=$csrf_key} {button type="submit" name="save" label="Enregistrer" shape="right" class="main"} | | | 131 132 133 134 135 136 137 138 139 140 141 142 143 | </fieldset> {/if} <p class="submit"> {csrf_field key=$csrf_key} {button type="submit" name="save" label="Enregistrer" shape="right" class="main"} {if $create && $users && count($users) == 1} {button type="submit" name="save_and_add_payment" class="accounting" label="Enregistrer et ajouter un autre règlement" shape="plus"} {/if} </p> </form> |
Modified src/templates/services/user/add.tpl from [e38a83f3e8] to [027f0b9b0f].
1 2 3 4 5 6 | {include file="admin/_head.tpl" title="Inscrire à une activité" current="membres/services"} {include file="services/_nav.tpl" current="save" fee=null service=null} {form_errors} | < | | > > > > | > > > > > > > > > > > > < | > > > > > > | < > > > | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | {include file="admin/_head.tpl" title="Inscrire à une activité" current="membres/services"} {include file="services/_nav.tpl" current="save" fee=null service=null} {form_errors} <form method="post" action="subscribe.php" data-focus="button"> <fieldset> <legend>Inscrire à une activité</legend> <dl> {input type="radio-btn" name="choice" value="1" label="Sélectionner des membres" default=1} {input type="radio-btn" name="choice" value="2" label="Recopier depuis une activité" help="Utile si vous avez une cotisation par année civile par exemple : copie les membres inscrits l'année précédente dans la nouvelle année."} </dl> </fieldset> <fieldset class="c1"> <legend>Inscrire des membres</legend> <dl> {input type="list" name="users" required=true label="Membres à inscrire" target="membres/selector.php" multiple=true} </dl> </fieldset> <fieldset class="c2"> <legend>Recopier depuis une activité</legend> <dl> {input type="select" name="copy_service" label="Activité à recopier" options=$services required=true} {input type="checkbox" name="copy_service_only_paid" value="1" label="Ne recopier que les membres dont l'inscription est payée"} </dl> </fieldset> <p class="submit"> {button type="submit" name="next" label="Continuer" shape="right" class="main"} </p> </form> <script type="text/javascript"> {literal} function selectChoice() { let choice = $('#f_choice_1').form.choice.value; g.toggle('.c1', choice == 1); g.toggle('.c2', choice == 2); } selectChoice(); $('#f_choice_1').onchange = selectChoice; $('#f_choice_2').onchange = selectChoice; {/literal} </script> {include file="admin/_foot.tpl"} |
Added src/templates/services/user/subscribe.tpl version [8f9805b504].
> > > > > > > > > | 1 2 3 4 5 6 7 8 9 | {include file="admin/_head.tpl" title="Inscrire à une activité" current="membres/services"} {include file="services/_nav.tpl" current="save" fee=null service=null} {form_errors} {include file="services/user/_service_user_form.tpl" create=true} {include file="admin/_foot.tpl"} |
Modified src/www/admin/services/user/_form.php from [355c6ae19e] to [9911775664].
1 2 3 4 5 6 7 8 9 10 11 | <?php namespace Garradin; use Garradin\Services\Services; if (!defined('\Garradin\ROOT')) { die(); } | | > > > > > > | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | <?php namespace Garradin; use Garradin\Services\Services; if (!defined('\Garradin\ROOT')) { die(); } assert(isset($tpl, $form_url, $create)); $current_only = !qg('past_services'); // If there is only one user selected we can calculate the amount $single_user_id = isset($users) && count($users) == 1 ? key($users) : null; $copy_service ??= null; $copy_service_only_paid ??= null; $users ??= null; $grouped_services = Services::listGroupedWithFees($single_user_id, $current_only); if (!count($grouped_services)) { Utils::redirect($form_url . 'past_services=' . (int) $current_only); } if (!isset($count_all)) { $count_all = Services::count(); } $has_past_services = count($grouped_services) != $count_all; $today = new \DateTime; $tpl->assign([ 'custom_js' => ['service_form.js'], ]); $tpl->assign(compact('form_url', 'today', 'grouped_services', 'current_only', 'has_past_services', 'create', 'copy_service', 'copy_service_only_paid', 'users')); |
Modified src/www/admin/services/user/add.php from [614b57c4f4] to [1b2cc90329].
1 2 3 4 | <?php namespace Garradin; use Garradin\Services\Services; | < < < < | < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php namespace Garradin; use Garradin\Services\Services; require_once __DIR__ . '/../_inc.php'; $session->requireAccess($session::SECTION_USERS, $session::ACCESS_WRITE); // This controller allows to either select a user if none has been provided in the query string // or subscribe a user to an activity (create a new Service_User entity) // If $user_id is null then the form is just a select to choose a user $count_all = Services::count(); if (!$count_all) { Utils::redirect(ADMIN_URL . 'services/?CREATE'); } $services = Services::listAssoc(); $tpl->assign(compact('services')); $tpl->display('services/user/add.tpl'); |
Modified src/www/admin/services/user/edit.php from [b0b3db3c77] to [f09c2d97e9].
︙ | ︙ | |||
10 11 12 13 14 15 16 | $su = Services_User::get((int) qg('id')); if (!$su) { throw new UserException("Cette inscription n'existe pas"); } $csrf_key = 'su_edit_' . $su->id(); | < | > | | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | $su = Services_User::get((int) qg('id')); if (!$su) { throw new UserException("Cette inscription n'existe pas"); } $csrf_key = 'su_edit_' . $su->id(); $users = [$su->id_user => (new Membres)->getNom($su->id_user)]; $form_url = sprintf('edit.php?id=%d&', $su->id()); $create = false; require __DIR__ . '/_form.php'; $form->runIf('save', function () use ($su) { $su->importForm(); $su->save(); }, $csrf_key, ADMIN_URL . 'services/user/?id=' . $su->id_user); $service_user = $su; $tpl->assign(compact('csrf_key', 'service_user')); $tpl->display('services/user/edit.tpl'); |
Added src/www/admin/services/user/subscribe.php version [a6566f023b].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | <?php namespace Garradin; use Garradin\Services\Services; use Garradin\Entities\Services\Service_User; use Garradin\Entities\Accounting\Account; use Garradin\Entities\Accounting\Transaction; require_once __DIR__ . '/../_inc.php'; $session->requireAccess($session::SECTION_USERS, $session::ACCESS_WRITE); // This controller allows to either select a user if none has been provided in the query string // or subscribe a user to an activity (create a new Service_User entity) // If $user_id is null then the form is just a select to choose a user $count_all = Services::count(); if (!$count_all) { Utils::redirect(ADMIN_URL . 'services/?CREATE'); } $users = null; $copy_service = null; $copy_service_only_paid = null; if (qg('user') && ($name = (new Membres)->getNom((int)qg('user')))) { $users = [(int)qg('user') => $name]; } elseif (f('users') && is_array(f('users'))) { $users = f('users'); $users = array_filter($users, 'intval', \ARRAY_FILTER_USE_KEY); } elseif (f('copy_service') && $copy_service = Services::get((int)f('copy_service'))) { $copy_service_only_paid = (bool) f('copy_service_only_paid'); } else { throw new UserException('Aucun membre n\'a été sélectionné'); } $form_url = '?'; $csrf_key = 'service_save'; $create = true; // Only load the form if a user has been selected require __DIR__ . '/_form.php'; $form->runIf(f('save') || f('save_and_add_payment'), function () use ($session, $users, $copy_service, $copy_service_only_paid) { if ($copy_service) { $users = $copy_service->getUsersIds($copy_service_only_paid); } else { $users = array_keys($users); } $su = Service_User::createFromForm($users, $session->getUser()->id); if (count($users) > 1) { $url = ADMIN_URL . 'services/details.php?id=' . $su->id_service; } elseif (f('save_and_add_payment')) { $url = ADMIN_URL . 'services/user/payment.php?id=' . $su->id; } else { $url = ADMIN_URL . 'services/user/?id=' . $su->id_user; } Utils::redirect($url); }, $csrf_key); $types_details = Transaction::getTypesDetails(); $account_targets = $types_details[Transaction::TYPE_REVENUE]->accounts[1]->targets_string; $service_user = null; $tpl->assign(compact('csrf_key', 'users', 'account_targets', 'service_user')); $tpl->display('services/user/subscribe.tpl'); |