'/document/{hash}',
[PdfController::class, 'directlinkDocument']
)->setName('get-directlink');
+
+$app->get(
+ '/contribution/mass-add/choose-type',
+ [Crud\ContributionsController::class, 'massAddChooseType']
+)->setName('massAddContributionsChooseType')->add($authenticate);
+
+$app->post(
+ '/contribution/mass-add',
+ [Crud\ContributionsController::class, 'massAddContributions']
+)->setName('massAddContributions')->add($authenticate);
+
+$app->post(
+ '/contribution/do-mass-add',
+ [Crud\ContributionsController::class, 'doMassAddContributions']
+)->setName('doMassAddContributions')->add($authenticate);
use Galette\Entity\Adherent;
use Galette\Entity\Contribution;
use Galette\Entity\Transaction;
-use Galette\Repository\Contributions;
-use Galette\Repository\Transactions;
use Galette\Repository\Members;
use Galette\Entity\ContributionsTypes;
-use Galette\Core\GaletteMail;
-use Galette\IO\PdfMembersCards;
use Galette\Repository\PaymentTypes;
-use Analog\Analog;
/**
* Galette contributions controller
$ct = new ContributionsTypes($this->zdb);
$contributions_types = $ct->getList($type === 'fee');
- $disabled = array();
-
- if (!is_int($contrib->id)) {
- // initialiser la structure contribution à vide (nouvelle contribution)
- $contribution['duree_mois_cotis'] = $this->preferences->pref_membership_ext;
- }
-
// template variable declaration
$title = null;
if ($type === 'fee') {
$title .= ' (' . _T("creation") . ')';
}
- // required fields
- $required = [
- 'id_type_cotis' => 1,
- 'id_adh' => 1,
- 'date_enreg' => 1,
- 'date_debut_cotis' => 1,
- 'date_fin_cotis' => $contrib->isCotis(),
- 'montant_cotis' => $contrib->isCotis() ? 1 : 0
- ];
-
$params = [
'page_title' => $title,
- 'required' => $required,
- 'disabled' => $disabled,
+ 'required' => $contrib->getRequired(),
'contribution' => $contrib,
'adh_selected' => $contrib->member,
'type' => $type
$m = new Members();
$members = $m->getSelectizedMembers(
$this->zdb,
- isset($contrib) && $contrib->member > 0 ? $contrib->member : null
+ $contrib->member > 0 ? $contrib->member : null
);
$params['members'] = [
}
$ext_membership = '';
- if (isset($contrib) && $contrib->isCotis() || !isset($contrib) && $type === 'fee') {
+ if ($contrib->isCotis() || !isset($contrib) && $type === 'fee') {
$ext_membership = $this->preferences->pref_membership_ext;
}
$params['pref_membership_ext'] = $ext_membership;
/**
* Add page
*
- * @param Request $request PSR Request
- * @param Response $response PSR Response
- * @param string $type Contribution type
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ * @param string|null $type Contribution type
*
* @return Response
*/
return $this->store($request, $response, 'add', $type);
}
+ /**
+ * Choose contribution type to mass add contribution
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ *
+ * @return Response
+ */
+ public function massAddChooseType(Request $request, Response $response): Response
+ {
+ $filters = $this->session->filter_members;
+ $data = [
+ 'id' => $filters->selected,
+ 'redirect_uri' => $this->router->pathFor('members')
+ ];
+
+ // display page
+ $this->view->render(
+ $response,
+ 'mass_choose_type.tpl',
+ array(
+ 'mode' => $request->isXhr() ? 'ajax' : '',
+ 'page_title' => str_replace(
+ '%count',
+ count($data['id']),
+ _T('Mass add contribution on %count members')
+ ),
+ 'data' => $data,
+ 'form_url' => $this->router->pathFor('massAddContributions'),
+ 'cancel_uri' => $this->router->pathFor('members')
+ )
+ );
+ return $response;
+ }
+
+ /**
+ * Massive change page
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ *
+ * @return Response
+ */
+ public function massAddContributions(Request $request, Response $response): Response
+ {
+ $post = $request->getParsedBody();
+ $filters = $this->session->filter_members;
+ $contribution = new Contribution($this->zdb, $this->login);
+
+ $type = $post['type'];
+ $data = [
+ 'id' => $filters->selected,
+ 'redirect_uri' => $this->router->pathFor('members'),
+ 'type' => $type
+ ];
+
+ // contribution types
+ $ct = new ContributionsTypes($this->zdb);
+ $contributions_types = $ct->getList($type === 'fee');
+
+ // display page
+ $this->view->render(
+ $response,
+ 'mass_add_contribution.tpl',
+ array(
+ 'mode' => $request->isXhr() ? 'ajax' : '',
+ 'page_title' => str_replace(
+ '%count',
+ count($data['id']),
+ _T('Mass add contribution on %count members')
+ ),
+ 'form_url' => $this->router->pathFor('doMassAddContributions'),
+ 'cancel_uri' => $this->router->pathFor('members'),
+ 'data' => $data,
+ 'contribution' => $contribution,
+ 'type' => $type,
+ 'require_mass' => true,
+ 'required' => $contribution->getRequired(),
+ 'type_cotis_options' => $contributions_types
+ )
+ );
+ return $response;
+ }
+
+ /**
+ * Do massive contribution add
+ *
+ * @param Request $request PSR Request
+ * @param Response $response PSR Response
+ *
+ * @return Response
+ */
+ public function doMassAddContributions(Request $request, Response $response): Response
+ {
+ $post = $request->getParsedBody();
+ $members_ids = $post['id'];
+ unset($post['id']);
+
+ $error_detected = [];
+
+ // flagging required fields for first step only
+ $disabled = [];
+ $success = 0;
+ $errors = 0;
+
+ foreach ($members_ids as $member_id) {
+ $post[Adherent::PK] = (int)$member_id;
+ $contrib = new Contribution($this->zdb, $this->login);
+
+ // regular fields
+ $valid = $contrib->check($post, $contrib->getRequired(), $disabled);
+ if ($valid !== true) {
+ $error_detected = array_merge($error_detected, $valid);
+ }
+
+ //all goes well, we can proceed
+ if (count($error_detected) == 0) {
+ $store = $contrib->store();
+ if ($store === true) {
+ ++$success;
+ $files_res = $contrib->handleFiles($_FILES);
+ if (is_array($files_res)) {
+ $error_detected = array_merge($error_detected, $files_res);
+ }
+ } else {
+ ++$errors;
+ }
+ }
+ }
+
+ if (count($error_detected) == 0) {
+ $redirect_url = $this->router->pathFor('members');
+ } else {
+ //something went wrong.
+ //store entity in session
+ $redirect_url = $this->router->pathFor('massAddContributions');
+ //report errors
+ foreach ($error_detected as $error) {
+ $this->flash->addMessage(
+ 'error_detected',
+ $error
+ );
+ }
+ }
+
+ //redirect to calling action
+ return $response
+ ->withStatus(301)
+ ->withHeader('Location', $redirect_url);
+ }
+
// /CRUD - Create
// CRUD - Read
}
}
- // flagging required fields for first step only
- $required = [
- 'id_type_cotis' => 1,
- 'id_adh' => 1,
- 'date_enreg' => 1,
- 'montant_cotis' => 1, //TODO: not always required, see #196
- 'date_debut_cotis' => 1,
- 'date_fin_cotis' => ($type === 'fee')
- ];
$disabled = [];
// regular fields
- $valid = $contrib->check($post, $required, $disabled);
+ $valid = $contrib->check($post, $contrib->getRequired(), $disabled);
if ($valid !== true) {
$error_detected = array_merge($error_detected, $valid);
}
->withHeader('Location', $this->router->pathFor('masschangeMembers'));
}
+ if (isset($post['masscontributions'])) {
+ return $response
+ ->withStatus(301)
+ ->withHeader('Location', $this->router->pathFor('massAddContributionsChooseType'));
+ }
+
throw new \RuntimeException('Does not know what to batch :(');
} else {
$this->flash->addMessage(
return true;
}
}
+
+ /**
+ * Get required fields list
+ *
+ * @return array
+ */
+ public function getRequired(): array
+ {
+ // required fields
+ $required = [
+ 'id_type_cotis' => 1,
+ 'id_adh' => 1,
+ 'date_enreg' => 1,
+ 'date_debut_cotis' => 1,
+ 'date_fin_cotis' => $this->isCotis(),
+ 'montant_cotis' => $this->isCotis() ? 1 : 0
+ ];
+ return $required;
+ }
}
-{extends file="page.tpl"}
+{if isset($mode) && $mode eq 'ajax'}
+ {assign var="extend" value='ajax.tpl'}
+{else}
+ {assign var="extend" value='page.tpl'}
+{/if}
+{extends file=$extend}
{block name="content"}
-{if isset($members.list)}
+{if isset($members.list) || $require_mass}
<form action="{if $contribution->id}{path_for name="doEditContribution" data=["type" => $type, "id" => $contribution->id]}{else}{path_for name="doAddContribution" data=["type" => $type]}{/if}" enctype="multipart/form-data" method="post">
<div class="bigtable">
{if $contribution->isTransactionPart()}
</a>
{/if}
</legend>
+ {if !$require_mass}
<p>
<label for="id_adh" class="bline">{_T string="Contributor:"}</label>
- <select name="id_adh" id="id_adh" class="nochosen"{if isset($disabled.id_adh)} {$disabled.id_adh}{/if}>
+ <select name="id_adh" id="id_adh" class="nochosen">
{if $adh_selected eq 0}
<option value="">{_T string="Search for name or ID and pick member"}</option>
{/if}
{/foreach}
</select>
</p>
+ {/if}
<p>
<label for="id_type_cotis" class="bline">{_T string="Contribution type:"}</label>
- <select name="id_type_cotis" id="id_type_cotis"{if $required.id_type_cotis eq 1} required="required"{/if}>
+ <select name="id_type_cotis" id="id_type_cotis"{if isset($required.id_type_cotis) && ($required.id_type_cotis eq 1)} required="required"{/if}>
{if $contribution->type}
{assign var="selectedid" value=$contribution->type->id}
{else}
<legend class="ui-state-active ui-corner-top">{if $type eq "fee"}{_T string="Details of membership fee"}{else}{_T string="Details of donation"}{/if}</legend>
<p>
<label class="bline" for="montant_cotis">{_T string="Amount:"}</label>
- <input type="text" name="montant_cotis" id="montant_cotis" value="{$contribution->amount}" maxlength="10"{if $required.montant_cotis eq 1} required="required"{/if}/>
+ <input type="text" name="montant_cotis" id="montant_cotis" value="{$contribution->amount}" maxlength="10"{if isset($required.montant_cotis) && ($required.montant_cotis eq 1)} required="required"{/if}/>
</p>
{* payment type *}
{assign var="ptype" value=$contribution->payment_type}
<label class="bline" for="date_enreg">
{_T string="Record date:"}
</label>
- <input class="past-date-pick" type="text" name="date_enreg" id="date_enreg" value="{$contribution->date}" maxlength="10"{if $required.date_enreg eq 1} required="required"{/if}/>
+ <input class="past-date-pick" type="text" name="date_enreg" id="date_enreg" value="{$contribution->date}" maxlength="10"{if isset($required.date_enreg) && ($required.date_enreg eq 1)} required="required"{/if}/>
<span class="exemple">{_T string="(yyyy-mm-dd format)"}</span>
</p>
{_T string="Date of contribution:"}
{/if}
</label>
- <input class="past-date-pick" type="text" name="date_debut_cotis" id="date_debut_cotis" value="{$contribution->begin_date}" maxlength="10"{if $required.date_debut_cotis eq 1} required="required"{/if}/>
+ <input class="past-date-pick" type="text" name="date_debut_cotis" id="date_debut_cotis" value="{$contribution->begin_date}" maxlength="10"{if isset($required.date_debut_cotis) && ($required.date_debut_cotis eq 1)} required="required"{/if}/>
<span class="exemple">{_T string="(yyyy-mm-dd format)"}</span>
</p>
{if $type eq "fee"}
<p>
- {if $pref_membership_ext != ""}
+ {if $preferences->pref_membership_ext != ""}
<label class="bline" for="duree_mois_cotis">{_T string="Membership extension:"}</label>
- <input type="text" name="duree_mois_cotis" id="duree_mois_cotis" value="{$contribution->duration}" maxlength="3"{if $required.date_fin_cotis eq 1} required="required"{/if}/>
+ <input type="text" name="duree_mois_cotis" id="duree_mois_cotis" value="{$contribution->duration}" maxlength="3"{if isset($required.date_fin_cotis) && ($required.date_fin_cotis eq 1)} required="required"{/if}/>
<span class="exemple">{_T string="months"}</span>
{else}
<label class="bline" for="date_fin_cotis">{_T string="End date of membership:"}</label>
- <input type="text" name="date_fin_cotis" id="date_fin_cotis" value="{$contribution->end_date}" maxlength="10"{if $required.date_fin_cotis eq 1} required="required"{/if}/>
+ <input type="text" name="date_fin_cotis" id="date_fin_cotis" value="{$contribution->end_date}" maxlength="10"{if isset($required.date_fin_cotis) && ($required.date_fin_cotis eq 1)} required="required"{/if}/>
<span class="exemple">{_T string="(yyyy-mm-dd format)"}</span>
{/if}
</p>
{/if}
{include file="edit_dynamic_fields.tpl" object=$contribution}
{if not $contribution->id and $pref_mail_method neq constant('Galette\Core\GaletteMail::METHOD_DISABLED')}
+ {if !$require_mass}
<p>
<label for="mail_confirm">{_T string="Notify member"}</label>
<input type="checkbox" name="mail_confirm" id="mail_confirm" value="1" {if $preferences->pref_bool_mailowner || isset($smarty.post.mail_confirm) and $smarty.post.mail_confirm != ""}checked="checked"{/if}/>
<br/><span class="exemple">{_T string="Member will receive a notification by email, if he has an address."}</span>
</p>
+ {/if}
{/if}
</div>
+ {if !$require_mass}
<div class="button-container">
<button type="submit" name="valid" class="action">
<i class="fas fa-save fa-fw"></i> {_T string="Save"}
<input type="hidden" name="valid" value="1"/>
<input type="hidden" name="trans_id" value="{if $contribution->transaction neq NULL}{$contribution->transaction->id}{/if}"/>
</div>
+ {/if}
</form>
{else} {* No members *}
<div class="center" id="warningbox">
<i class="fas fa-user-edit fa-fw"></i> {_T string="Mass change"}
</button>
</li>
+ <li>
+ <button type="submit" id="masscontributions" name="masscontributions" class="action">
+ <i class="fas fa-cookie-bite fa-fw"></i> {_T string="Mass add contributions"}
+ </button>
+ </li>
{if $pref_mail_method neq constant('Galette\Core\GaletteMail::METHOD_DISABLED')}
<li>
<button type="submit" id="sendmail" name="mailing">
_bind_check();
_bind_legend();
- $('.selection_menu *[type="submit"], .selection_menu *[type="button"]').click(function(){
- if ( this.id == 'delete' ) {
+ $('.selection_menu *[type="submit"], .selection_menu *[type="button"]').click(function(event){
+ event.preventDefault();
+ if ( this.id == 'delete' || this.id == 'masschange' ) {
//mass removal is handled from 2 steps removal
+ //mass change is specifically handled below
return;
}
_attendance_sheet_details();
return false;
}
+
+ if (this.id == 'masscontributions') {
+ $.ajax({
+ url: '{path_for name="batch-memberslist"}',
+ type: "POST",
+ data: {
+ ajax: true,
+ masscontributions: true,
+ member_sel: $('#listform input[type=\"checkbox\"]:checked').map(function(){
+ return $(this).val();
+ }).get()
+ },
+ datatype: 'json',
+ {include file="js_loader.tpl"},
+ success: function(res){
+ var _res = $(res);
+ _bindmassres(_res);
+ $('body').append(_res);
+
+ _initTooltips('#mass_contributions');
+ _massCheckboxes('#mass_contributions');
+
+ _res.dialog({
+ width: 'auto',
+ modal: true,
+ close: function(event, ui){
+ $(this).dialog('destroy').remove()
+ }
+ });
+ },
+ error: function() {
+ alert("{_T string="An error occurred :(" escape="js"}");
+ }
+ });
+ }
+
return true;
}
});
res.find('input[type=submit]')
.button();
+
+ res.find('select:not(.nochosen)').selectize({
+ maxItems: 1
+ });
}
$('#masschange').off('click').on('click', function(event) {
--- /dev/null
+{extends file='ajouter_contribution.tpl'}
+{block name="content"}
+ <div id="mass_contributions"{if $mode neq 'ajax'} class="center"{else} title="{$page_title}"{/if}>
+ <form action="{$form_url}" method="post">
+ {if $mode neq 'ajax'}<h2>{$page_title}</h2>{/if}
+ <div class="button-container">
+ {$smarty.block.parent}
+ <input type="submit" id="masschange" class="button" value="{if !isset($changes)}{_T string="Edit"}{else}{_T string="OK"}{/if}"/>
+ <a href="{$cancel_uri}" class="button" id="btncancel">{_T string="Cancel"}</a>
+ <input type="hidden" name="confirm" value="1"/>
+ {if $mode eq 'ajax'}<input type="hidden" name="ajax" value="true"/>{/if}
+ {foreach $data as $key=>$value}
+ {if is_array($value)}
+ {foreach $value as $val}
+ <input type="hidden" name="{$key}[]" value="{$val}"/>
+ {/foreach}
+ {else}
+ <input type="hidden" name="{$key}" value="{$value}"/>
+ {/if}
+ {/foreach}
+ </div>
+ </form>
+ </div>
+{/block}
--- /dev/null
+{if isset($mode) && $mode eq 'ajax'}
+ {assign var="extend" value='ajax.tpl'}
+{else}
+ {assign var="extend" value='page.tpl'}
+{/if}
+{extends file=$extend}
+
+{block name="content"}
+ <div id="mass_contributions"{if $mode neq 'ajax'} class="center"{else} title="{$page_title}"{/if}>
+ <form action="{$form_url}" method="post">
+ {if $mode neq 'ajax'}<h2>{$page_title}</h2>{/if}
+ <label for="type">{_T string="Contribution type"}</label>
+ <select name="type" id="type">
+ <option value="{constant('Galette\Entity\Contribution::TYPE_FEE')}">{_T string="Membership"}</option>
+ <option value="{constant('Galette\Entity\Contribution::TYPE_DONATION')}">{_T string="Donation"}</option>
+ </select>
+ <div class="button-container">
+ <input type="submit" id="masschange" class="button" value="{_T string="OK"}"/>
+ <a href="{$cancel_uri}" class="button" id="btncancel">{_T string="Cancel"}</a>
+ {if $mode eq 'ajax'}<input type="hidden" name="ajax" value="true"/>{/if}
+ {foreach $data as $key=>$value}
+ {if is_array($value)}
+ {foreach $value as $val}
+ <input type="hidden" name="{$key}[]" value="{$val}"/>
+ {/foreach}
+ {else}
+ <input type="hidden" name="{$key}" value="{$value}"/>
+ {/if}
+ {/foreach}
+ </div>
+ </form>
+ </div>
+{/block}