4 /* HELPER functions FOR FEES {{{ */
6 // build an empty structure for holding monthly information
7 function _fees_build_month_array($start_date, $end_date, $preset = array())
9 $start_info = getdate($start_date);
10 $end_info = getdate($end_date);
12 if ($start_info['year'] > $end_info['year']) {
15 if ($start_info['year'] == $end_info['year'] && $start_info['mon'] > $end_info['mon']) {
20 for ($year = $start_info['year']; $year <= $end_info['year']; $year++) {
21 if (!isset($ret[$year])) {
22 $ret[$year] = array();
25 $month = (($year == $start_info['year']) ? $start_info['mon'] : 1);
26 $month <= (($year == $end_info['year']) ? $end_info['mon'] : 12);
29 if (isset($ret[$year][$month])) {
32 $ret[$year][$month] = array(
34 'member_type' => null,
36 'payment_interval' => null
43 function _fees_apply_event_information(&$info, $events)
45 if (!empty($events)) {
47 foreach ($events as $event) {
49 $timestamp = db_date2unixtime($event['event_date']);
50 if ($timestamp < FOUNDING_DATE) {
51 $timestamp = FOUNDING_DATE;
53 $date_info = getdate($timestamp);
55 if (!isset($info[$date_info['year']])) {
58 if (!isset($info[$date_info['year']][$date_info['mon']])) {
62 if ($event['fee'] !== null) {
63 $info[$date_info['year']][$date_info['mon']]['fee'] = $event['fee'];
65 if ($event['member_type'] !== null) {
66 $info[$date_info['year']][$date_info['mon']]['member_type'] = $event['member_type'];
68 if ($event['event_type'] !== 'changed') {
69 $info[$date_info['year']][$date_info['mon']]['is_member'] = ($event['event_type'] == 'joined' ? 1 : 0);
71 if ($event['payment_interval'] !== null) {
72 $info[$date_info['year']][$date_info['mon']]['payment_interval'] = $event['payment_interval'];
80 $payment_interval = null;
82 foreach (array_keys($info) as $year) {
83 foreach (array_keys($info[$year]) as $month) {
84 if (!isset($info[$year][$month]['fee'])) {
85 $info[$year][$month]['fee'] = $fee;
87 $fee = $info[$year][$month]['fee'];
90 if (!isset($info[$year][$month]['member_type'])) {
91 $info[$year][$month]['member_type'] = $member_type;
93 $member_type = $info[$year][$month]['member_type'];
96 if (!isset($info[$year][$month]['is_member'])) {
97 $info[$year][$month]['is_member'] = $is_member;
99 $is_member = $info[$year][$month]['is_member'];
102 if (!isset($info[$year][$month]['payment_interval'])) {
103 $info[$year][$month]['payment_interval'] = $payment_interval;
105 $payment_interval = $info[$year][$month]['payment_interval'];
112 function fees_get_list_for_member($member_id, $end_date)
115 static $cache = array();
117 $end_date = mktime(0, 0, 0, date('m', $end_date) + 1, 0, date('Y', $end_date)); // last day of given month
119 if (isset($cache[$member_id][$end_date])) {
120 return $cache[$member_id][$end_date];
122 if (isset($cache[$member_id])) {
123 foreach (array_reverse(array_keys($cache[$member_id])) as $cache_date) {
124 if ($cache_date <= $end_date) {
125 $ret = _fees_build_month_array($cache_date /* XXX einen Monat später wäre an dieser Stelle richtiger*/,
126 $end_date, $cache[$member_id][$cache_date]);
127 _fees_apply_event_information($ret, db_get_events_for_member($member_id, $cache_date, $end_date));
128 $cache[$member_id][$end_date] = $ret;
134 $ret = _fees_build_month_array(FOUNDING_DATE, $end_date);
139 _fees_apply_event_information($ret, db_get_events_for_member($member_id));
141 $cache[$member_id][$end_date] = $ret;
145 function fees_sum_for_member($member_id, $end_date)
147 $membership_info = fees_get_list_for_member($member_id, $end_date);
151 foreach ($membership_info as $year => $months) {
152 foreach ($months as $month => $info) {
153 if ($info['is_member']) {
154 $total = bcadd($total, $info['fee']);
161 function fees_for_member_at_date($member_id, $end_date)
163 $membership_info = fees_get_list_for_member($member_id, $end_date);
165 $this_year = array_pop($membership_info);
166 $this_month = array_pop($this_year);
167 if ($this_month['is_member']) {
168 return $this_month['fee'];
173 function fees_info_for_member($member_id, $end_date)
175 $membership_info = fees_get_list_for_member($member_id, $end_date);
177 $this_year = array_pop($membership_info);
178 return array_pop($this_year);
181 function fees_sum_by_month($end_date)
183 $members = db_get_members();
185 if (empty($members)) {
188 foreach ($members as $member) {
189 $membership_info = fees_get_list_for_member($member['id'], $end_date);
190 foreach ($membership_info as $year => $months) {
191 foreach ($months as $month => $info) {
192 if (!isset($fees[$year][$month])) {
193 $fees[$year][$month] = '0';
195 if ($info['is_member']) {
196 $fees[$year][$month] = bcadd($fees[$year][$month], $info['fee']);
204 function fees_get_list_for_month($year, $month)
206 $members = db_get_members();
208 foreach ($members as $member) {
209 $membership_info = fees_get_list_for_member($member['id'], mktime(0, 0, 0, $month, 1, $year));
211 if (empty($membership_info)) {
214 $member['fee'] = $membership_info[$year][$month]['fee'];
215 $member['is_member'] = $membership_info[$year][$month]['is_member'];
221 function fee_next_directdebit_for_member($member_id, $max_date = null)
224 $member = db_get_member_with_id($member_id);
225 if (!$member['directdebit']) {
229 $sum_old_fees = fees_sum_for_member($member_id, DIRECTDEBIT_DATE - 86400);
230 $sum_new_paid = finance_get_paid_fees_for_member($member_id);
231 $year = date('Y', DIRECTDEBIT_DATE);
232 $month = date('n', DIRECTDEBIT_DATE);
237 $start_date = mktime(0, 0, 0, $month, $day, $year);
238 if (isset($max_date) && $start_date > $max_date) {
242 // check if fee is zero at the moment and skip to next event
243 // quit searching if theres no event in future
244 $current_fee = fees_for_member_at_date($member_id, $start_date);
245 if (empty($current_fee)) {
246 $events = db_get_events_for_member($member_id, $start_date + 86400);
247 if (empty($events)) {
250 $start_date = db_date2unixtime($events[0]['event_date']);
251 $day = date('j', $start_date);
252 $month = date('n', $start_date);
253 $year = date('Y', $start_date);
257 $sum_fees = fees_sum_for_member($member_id, $start_date);
258 $sum_new_fees = bcsub($sum_fees, $sum_old_fees);
259 if (bccomp($sum_new_fees, $sum_new_paid) == 1) {
260 $info = fees_get_list_for_member($member_id, $start_date);
263 'date' => $start_date,
264 'value' => bcsub($sum_new_fees, $sum_new_paid),
267 switch ($info[$year][$month]['payment_interval']) {
282 $ret['info'] = dtaus_string(sprintf('CCCFFM %d, %s', $member['number'], format_month($start_date)));
285 $end_date = mktime(0, 0, 0, $month + $months - 1, 1, $year);
286 $sum_fee_end = fees_sum_for_member($member_id, $end_date);
287 $ret['value'] = bcadd($ret['value'], bcsub($sum_fee_end, $sum_fees));
288 $ret['info'] = dtaus_string(sprintf('CCCFFM %d, %s-%s', $member['number'], format_month($start_date),
289 format_month($end_date)));
306 function action_fees()
309 if (isset($_REQUEST['member_id'])) {
310 render_fees_for_member($_REQUEST['member_id']);
313 if (isset($_REQUEST['year']) && isset($_REQUEST['month'])) {
314 render_accrued_fees_for_month($_REQUEST['year'], $_REQUEST['month']);
318 render_fees_by_member();
319 render_accrued_fees_by_month();
320 render_next_direct_debit();
321 render_future_fees();
325 function render_fees_by_member()
327 $members = db_get_members();
329 <h2>Mitgliedsbeiträge nach Mitglied</h2>
332 <th>Mitgliedsnummer</th>
334 <th style="text-align: right;">Angefallene Beiträge</th>
335 <th style="text-align: right;">Aktueller Beitrag</th>
336 <th style="text-align: right;">Offener Beitrag</th>
338 <?php if (empty($members)) {
341 <?php foreach ($members as $member) : ?>
343 $current_fee = fees_for_member_at_date($member['id'], time());
345 $sum_fees = fees_sum_for_member($member['id'], time());
346 $sum_old_fees = fees_sum_for_member($member['id'], DIRECTDEBIT_DATE - 86400);
347 $sum_old_paid = finance_get_paid_fees_for_member($member['id'], true);
348 $sum_new_paid = finance_get_paid_fees_for_member($member['id']);
349 $sum_new_fees = bcsub($sum_fees, $sum_old_fees);
350 $open_fees = bcadd(bcsub($sum_old_fees, $sum_old_paid), max(bcsub($sum_new_fees, $sum_new_paid), 0));
353 <td><a href="<?= html_escape(link_to('fees',
354 array('member_id' => $member['id']))) ?>"><?= html_escape($member['number']) ?></a></td>
355 <td><?= html_escape($member['nickname']) ?></td>
356 <td style="text-align: right;"><?= format_money($sum_fees) ?></td>
357 <td style="text-align: right;"><?= isset($current_fee) ? format_money($current_fee) : '-' ?></td>
358 <td style="text-align: right;"><?= $open_fees > 0 ? format_money($open_fees) : '-' ?></td>
365 function render_future_fees()
367 $total_paid = finance_get_total_paid_fees();
368 $this_year = date('Y');
369 $this_month = date('m');
370 $fees = fees_sum_by_month(mktime(0, 0, 0, date('m') + 6, date('d'), date('Y') + 1));
372 foreach ($fees as $year => $months) {
373 foreach ($months as $month => $fee) {
374 $total = bcadd($total, $fee);
375 $fees[$year][$month] = array('total' => $total, 'fee' => $fee);
378 $fees = array_reverse($fees, true);
380 <h2>Beitragsprognose nach Monat</h2>
384 <th style="text-align: right;">Mitgliedsbeiträge</th>
385 <th style="text-align: right;">kummuliert</th>
386 <th style="text-align: right;">eingenommen</th>
387 <th style="text-align: right;"><strong>offen</strong></th>
389 <?php foreach ($fees as $year => $months) : ?>
390 <?php $months = array_reverse($months, true); ?>
391 <?php foreach ($months as $month => $data) : ?>
392 <tr<?php if ($year == $this_year && $month == $this_month) : ?> class="current"<?php endif ?>>
393 <td><a href="<?= html_escape(link_to('fees',
394 array('year' => $year, 'month' => $month))) ?>"><?= html_escape(format_month($year,
396 <td style="text-align: right;"><?= html_escape(format_money($data['fee'])) ?></td>
397 <td style="text-align: right;"><?= html_escape(format_money($data['total'])) ?></td>
398 <td style="text-align: right;">
399 <?php if ($year == $this_year && $month == $this_month) : ?>
400 <?= html_escape(format_money($total_paid)) ?>
403 <td style="text-align: right;">
404 <?php if ($year > $this_year || ($year >= $this_year && $month >= $this_month)) : ?>
405 <?= html_escape(format_money(bcsub($data['total'], $total_paid))) ?>
415 function render_accrued_fees_by_month()
417 $fees = fees_sum_by_month(time());
418 $fees = array_reverse($fees, true);
420 <h2>Angefallene Mitgliedsbeiträge nach Monat</h2>
424 <th style="text-align: right;">Mitgliedsbeitrag</th>
426 <?php foreach ($fees as $year => $months) : ?>
427 <?php $months = array_reverse($months, true); ?>
428 <?php foreach ($months as $month => $fee) : ?>
430 <td><a href="<?= html_escape(link_to('fees',
431 array('year' => $year, 'month' => $month))) ?>"><?= html_escape(format_month($year,
433 <td style="text-align: right;"><?= html_escape(format_money($fee)) ?></td>
441 function render_accrued_fees_for_month($year, $month)
443 $fees = fees_get_list_for_month($year, $month);
445 <h2>Angefallene Mitgliedsbeiträge für <?= format_month($year, $month) ?></h2>
448 <th>Mitgliedsnummer</th>
450 <th style="text-align: right;">Mitgliedsbeitrag</th>
452 <?php foreach ($fees as $info) : ?>
454 <td><a href="<?= html_escape(link_to('fees',
455 array('member_id' => $info['id']))) ?>"><?= html_escape($info['number']) ?></a></td>
456 <td><?= html_escape($info['nickname']) ?></td>
457 <td style="text-align: right;"><?= html_escape($info['is_member'] ? format_money($info['fee']) : '-') ?></td>
461 <p><a href="<?= html_escape(link_to('fees')) ?>">Alle angefallenen Mitgliedsbeiträge</a></p>
465 function render_next_direct_debit()
467 $members = db_get_members();
469 <h2>Nächste Abbuchungen nach Mitglied</h2>
472 <th>Mitgliedsnummer</th>
474 <th style="text-align: right;">Verwendungszweck</th>
475 <th style="text-align: right;">Betrag</th>
477 <?php if (empty($members)) {
480 <?php foreach ($members as $member) : ?>
481 <?php $next_debit = fee_next_directdebit_for_member($member['id']); ?>
483 <td><a href="<?= html_escape(link_to('fees',
484 array('member_id' => $member['id']))) ?>"><?= html_escape($member['number']) ?></a></td>
485 <td><?= html_escape($member['nickname']) ?></td>
486 <?php if (empty($next_debit)) : ?>
488 <td style="text-align: right;">-</td>
490 <td><?= html_escape($next_debit['info']) ?></td>
491 <td style="text-align: right;"><?= format_money($next_debit['value']) ?></td>
499 function render_fees_for_member($member_id)
501 global $MEMBER_TYPES, $EARNING_TYPES, $EXPENSE_TYPES;
503 $member = db_get_member_with_id($member_id);
504 if (!isset($member)) {
505 redirect(link_to('fees'));
508 $membership_info = fees_get_list_for_member($member_id, time());
509 $membership_info = array_reverse($membership_info, true);
511 $paid_fees = finance_list_paid_fees_for_member($member_id, time(), true);
513 $sum_new_paid = finance_get_paid_fees_for_member($member_id);
514 $sum_old_paid = finance_get_paid_fees_for_member($member_id, true);
515 $sum_old_fees = fees_sum_for_member($member_id, DIRECTDEBIT_DATE - 86400);
516 $sum_fees = fees_sum_for_member($member_id, time());
517 $sum_new_fees = bcsub($sum_fees, $sum_old_fees);
522 if (bccomp($sum_new_fees, $sum_new_paid) == 1) {
525 if (bccomp($sum_old_fees, $sum_old_paid) == 1) {
529 if ($new_open && $old_open) {
530 $state = sprintf('Es sind noch %1$s Mitgliedsbeitrag offen, davon %2$s für die Zeit vor dem %3$s und %4$s für danach.',
531 format_money(bcadd(bcsub($sum_old_fees, $sum_old_paid), bcsub($sum_new_fees, $sum_new_paid))),
532 format_money(bcsub($sum_old_fees, $sum_old_paid)),
533 format_date(DIRECTDEBIT_DATE),
534 format_money(bcsub($sum_new_fees, $sum_new_paid))
536 } elseif ($new_open) {
537 $state = sprintf('Es sind noch %1$s Mitgliedsbeitrag offen.',
538 format_money(bcsub($sum_new_fees, $sum_new_paid)));
539 } elseif ($old_open) {
540 $state = sprintf('Für die Zeit vor dem %1$s sind noch %2$s Mitgliedsbeitrag offen.',
541 format_date(DIRECTDEBIT_DATE), format_money(bcsub($sum_old_fees, $sum_old_paid)));
544 $next_debit = fee_next_directdebit_for_member($member_id);
547 <h2>Mitgliedsbeiträge
548 von <?= html_escape(!empty($member['nickname']) ? $member['nickname'] : sprintf('Mitglied Nr. %d',
549 $member['number'])) ?></h2>
550 <h3>Mitgliedsdetails</h3>
553 <th>Mitgliedsnummer</th>
558 <td><a href="<?= html_escape(link_to('view_member',
559 array('id' => $member['id']))) ?>"><?= html_escape($member['number']) ?></a></td>
560 <td><?= html_escape($member['nickname']) ?></strong></p></td>
562 <?php if (empty($state)) : ?>
563 Kein Beitragsrückstand
565 <?= wordwrap(html_escape($state), 70, '<br/>') ?>
570 <div style="float: left">
571 <h3>Angefallene Mitgliedsbeiträge</h3>
575 <th>Mitgliedsart</th>
576 <th style="text-align: right;">Mitgliedsbeitrag</th>
578 <?php foreach ($membership_info as $year => $months) : ?>
579 <?php $months = array_reverse($months, true); ?>
580 <?php foreach ($months as $month => $info) : ?>
582 <td><?= html_escape(format_month($year, $month)) ?></td>
583 <td><?= html_escape($info['is_member'] ? $MEMBER_TYPES[$info['member_type']] : 'Kein Mitglied') ?></td>
584 <td style="text-align: right;"><?= html_escape($info['is_member'] ? format_money($info['fee']) : '-') ?></td>
589 <p><a href="<?= html_escape(link_to('fees')) ?>">Alle angefallenen Mitgliedsbeiträge</a></p>
591 <div style="float: left; margin-left: 1em;">
592 <h3>Nächste Abbuchung</h3>
595 <th>Verwendungszweck</th>
596 <th style="text-align: right;">Betrag</th>
598 <?php if (empty($next_debit)) : ?>
600 <td style="text-align: right;">-</td>
602 <td><?= html_escape($next_debit['info']) ?></td>
603 <td style="text-align: right;"><?= format_money($next_debit['value']) ?></td>
606 <h3>Bezahlte Mitgliedsbeiträge</h3>
610 <th style="text-align: right;">Typ</th>
611 <th style="text-align: right;">Betrag</th>
613 <?php foreach ($paid_fees as $payment) : ?>
615 <td><?= html_escape(format_date(db_date2unixtime($payment['date']))) ?></td>
616 <td><?= ($payment['value'] < 0) ? $EXPENSE_TYPES[$payment['type']] : $EARNING_TYPES[$payment['type']] ?></td>
617 <td style="text-align: right;"><?= format_money($payment['value']) ?></td>
622 <br style="clear: left;"/>