Compare commits
416 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14fdc54503 | ||
|
|
d028087d23 | ||
|
|
7723e484bc | ||
|
|
b168c93b9f | ||
|
|
ee3c0c0d5f | ||
|
|
1e60c99c49 | ||
|
|
b7888e4d8c | ||
|
|
3fe52985fc | ||
|
|
d27898f72c | ||
|
|
8da8becb58 | ||
|
|
35460e93f5 | ||
|
|
0051a86b60 | ||
|
|
56eaa6d4f4 | ||
|
|
f5cd3248cd | ||
|
|
a0c01b10d5 | ||
|
|
9599619962 | ||
|
|
6818f27ece | ||
|
|
7c272b29f9 | ||
|
|
9f092baec7 | ||
|
|
50d17c837c | ||
|
|
e3ae3a203d | ||
|
|
efe78a258c | ||
|
|
d0f86b3245 | ||
|
|
31869e3d27 | ||
|
|
6a6fb112e3 | ||
|
|
dda2e21629 | ||
|
|
a19a531a67 | ||
|
|
abd4ccb739 | ||
|
|
91d162de90 | ||
|
|
03ed295623 | ||
|
|
db968b2785 | ||
|
|
fca939f2dd | ||
|
|
08442513c8 | ||
|
|
b1a2eb94c1 | ||
|
|
64f597261c | ||
|
|
005cef7faa | ||
|
|
4d10e21238 | ||
|
|
cc146f5fb6 | ||
|
|
df94bff0c1 | ||
|
|
3a57ccf180 | ||
|
|
09117c0b13 | ||
|
|
28aee541f0 | ||
|
|
49b4571fb5 | ||
|
|
32e45e16d4 | ||
|
|
f953e5119d | ||
|
|
6a4e65b0c3 | ||
|
|
bd1878b4d8 | ||
|
|
262d7616cd | ||
|
|
fc17b216f0 | ||
|
|
647f7470bc | ||
|
|
44d6301f0a | ||
|
|
6a39c0e79d | ||
|
|
00481c1c2d | ||
|
|
15dff5e87b | ||
|
|
62cfac8a0c | ||
|
|
e1ef840aee | ||
|
|
f816109188 | ||
|
|
19afd681b1 | ||
|
|
3acdf0bb9c | ||
|
|
d0b173396c | ||
|
|
3347aa1273 | ||
|
|
9f1adfe8cc | ||
|
|
de3f12316f | ||
|
|
261dc5e024 | ||
|
|
e7975d299e | ||
|
|
efa5bee9d4 | ||
|
|
5baec305f3 | ||
|
|
b07a95780f | ||
|
|
ac823a7d7f | ||
|
|
19fe1bf3b1 | ||
|
|
529aabc4d0 | ||
|
|
4e905437e4 | ||
|
|
274e5d43b8 | ||
|
|
3e723f96e9 | ||
|
|
42e7e7b65e | ||
|
|
f2572f3807 | ||
|
|
d4c5c17163 | ||
|
|
389d95a10b | ||
|
|
634d77a3a5 | ||
|
|
d255fca519 | ||
|
|
18a76f125f | ||
|
|
93f41ef221 | ||
|
|
5f14099d24 | ||
|
|
da84bdf0b7 | ||
|
|
859c3e89f9 | ||
|
|
25870c8f92 | ||
|
|
eb69dd142e | ||
|
|
8174ccfcfa | ||
|
|
606aa81e06 | ||
|
|
78f150ca81 | ||
|
|
e548add25e | ||
|
|
29cb318a5d | ||
|
|
6f27ab88c8 | ||
|
|
55e2d41044 | ||
|
|
e619b0469a | ||
|
|
b122af9c5a | ||
|
|
596f706b4e | ||
|
|
a2d89b96cf | ||
|
|
e43d8fccd6 | ||
|
|
a3e5e8ffad | ||
|
|
d8d15ae131 | ||
|
|
8f5127e249 | ||
|
|
61cb73d8d9 | ||
|
|
30468b50e2 | ||
|
|
7aa677353c | ||
|
|
1981c558eb | ||
|
|
d4fe43b48d | ||
|
|
725e12fc00 | ||
|
|
bd4a6524a9 | ||
|
|
6bb25c5227 | ||
|
|
e24ed45971 | ||
|
|
bacc5735b3 | ||
|
|
f780579ddc | ||
|
|
04f164d0e4 | ||
|
|
6c1452a33b | ||
|
|
fcc8a3479f | ||
|
|
743335fc00 | ||
|
|
641c62ea6f | ||
|
|
18a29a5779 | ||
|
|
d950435408 | ||
|
|
42299b89a1 | ||
|
|
572224846e | ||
|
|
d3bc199411 | ||
|
|
b06b3a0761 | ||
|
|
02f88f2486 | ||
|
|
d87b74491b | ||
|
|
cff97da124 | ||
|
|
13603c3f3a | ||
|
|
e7b711958b | ||
|
|
2f4d8f3a6e | ||
|
|
923bb88f1b | ||
|
|
005ff63f19 | ||
|
|
7c2cd011ab | ||
|
|
0fce8ee236 | ||
|
|
5c079a9fbd | ||
|
|
d3ae90cf99 | ||
|
|
54fe697cf7 | ||
|
|
153daf8524 | ||
|
|
a489a8aa2b | ||
|
|
e530d41f91 | ||
|
|
f2f5dbeff9 | ||
|
|
09bd3a37ea | ||
|
|
d9745d07c1 | ||
|
|
8d16a02eb4 | ||
|
|
ef79222280 | ||
|
|
06f5844fd6 | ||
|
|
773229bb11 | ||
|
|
dfe90153e9 | ||
|
|
9abfa123f4 | ||
|
|
0253a89125 | ||
|
|
cc217bda73 | ||
|
|
a86bbc4613 | ||
|
|
d90fab2df4 | ||
|
|
40fe8d8d03 | ||
|
|
ad61923abf | ||
|
|
d619899374 | ||
|
|
c1229cb7fa | ||
|
|
fbd6c31885 | ||
|
|
5c3bd20b5b | ||
|
|
52c8096b5f | ||
|
|
65be0db2fe | ||
|
|
b963224404 | ||
|
|
3cb1fa6d89 | ||
|
|
aff74592d2 | ||
|
|
2c2ab81ab8 | ||
|
|
169ff8c1ca | ||
|
|
30548031a2 | ||
|
|
a4ba5c77c4 | ||
|
|
3f6a6dd87b | ||
|
|
2c0ebe8c30 | ||
|
|
8844a13164 | ||
|
|
2cb5fa8a2d | ||
|
|
5fa3553b2d | ||
|
|
3ea898ab56 | ||
|
|
8e67aa841a | ||
|
|
02ec112b24 | ||
|
|
861d857c54 | ||
|
|
e1759263f0 | ||
|
|
88b52a79af | ||
|
|
7d0d1b073f | ||
|
|
6657267f63 | ||
|
|
9b0dca1ee7 | ||
|
|
953ea5bb97 | ||
|
|
836436c73d | ||
|
|
2ee7d21b3e | ||
|
|
7107ecb750 | ||
|
|
16dccbe723 | ||
|
|
b83d8d97c7 | ||
|
|
8afa2b6291 | ||
|
|
8740f2ec0d | ||
|
|
f6799092d3 | ||
|
|
0164fea8af | ||
|
|
3bb37ca7ad | ||
|
|
38564d381e | ||
|
|
1d6c80250b | ||
|
|
d6d914783a | ||
|
|
755cd30809 | ||
|
|
dfbcd95a4f | ||
|
|
b6165276f6 | ||
|
|
72c2824975 | ||
|
|
c239ec4ef6 | ||
|
|
a64cc2916e | ||
|
|
98899101eb | ||
|
|
06cd2c9614 | ||
|
|
0bdfc5a34d | ||
|
|
e96396ca70 | ||
|
|
7f2da475af | ||
|
|
cd51a8c0f1 | ||
|
|
e0002a22a7 | ||
|
|
fe35d51a31 | ||
|
|
3860109dd7 | ||
|
|
ad761b3d27 | ||
|
|
806af52910 | ||
|
|
5fb7f05e8c | ||
|
|
894c6b0b7b | ||
|
|
dc7dafeb1e | ||
|
|
53d3e9ed07 | ||
|
|
c579c6f797 | ||
|
|
16f63c8f01 | ||
|
|
0f4e768852 | ||
|
|
54d083969c | ||
|
|
6b20ddb354 | ||
|
|
d139a8d5e0 | ||
|
|
ce58510613 | ||
|
|
a49c18f241 | ||
|
|
b9c7d7cfb8 | ||
|
|
a218e5e248 | ||
|
|
d7f9ce89f4 | ||
|
|
109aa148f8 | ||
|
|
58cdfb76b8 | ||
|
|
5cf827df48 | ||
|
|
74c1970c38 | ||
|
|
b02fb0125c | ||
|
|
09f69b1fb5 | ||
|
|
8bf22a6086 | ||
|
|
f6fc7c2b22 | ||
|
|
9af08779fb | ||
|
|
fc544c3239 | ||
|
|
861c3a10e3 | ||
|
|
848bd7ce0c | ||
|
|
278f43f4af | ||
|
|
2e8ce70712 | ||
|
|
62f2bfa9f8 | ||
|
|
5f5f8cc601 | ||
|
|
ae442b98ae | ||
|
|
1ff48c9bee | ||
|
|
88d1032422 | ||
|
|
e5726d221b | ||
|
|
bf7ebb04b9 | ||
|
|
4f0878bade | ||
|
|
a5c902d01d | ||
|
|
23a61e9769 | ||
|
|
92265bfe8e | ||
|
|
7b25330b4c | ||
|
|
1950457b24 | ||
|
|
b8804472b8 | ||
|
|
33730dc1ee | ||
|
|
6577afc1b1 | ||
|
|
f8fa4883a8 | ||
|
|
750a102e3c | ||
|
|
68ff1bfe27 | ||
|
|
fdc3678022 | ||
|
|
4f28f39128 | ||
|
|
0dd8f9fec7 | ||
|
|
35c2f24515 | ||
|
|
e2aac9d2bb | ||
|
|
0f63dd4bb0 | ||
|
|
dda33a00ea | ||
|
|
62b4fa0c90 | ||
|
|
946e616d14 | ||
|
|
02732e223e | ||
|
|
bc7c431605 | ||
|
|
4ec642da55 | ||
|
|
51c3e1eaf7 | ||
|
|
165a2bd4f7 | ||
|
|
24475c4e89 | ||
|
|
5387da1f8b | ||
|
|
2277546932 | ||
|
|
4b0b01d9d0 | ||
|
|
ac0e159f5e | ||
|
|
12a29fc22d | ||
|
|
2a6ecc0469 | ||
|
|
b58fa2ef06 | ||
|
|
ce60cb0420 | ||
|
|
a3eacc1edf | ||
|
|
a0279b5985 | ||
|
|
836723b1a0 | ||
|
|
efb3be7b8c | ||
|
|
e024a73e0d | ||
|
|
bc8a11aae5 | ||
|
|
571da045db | ||
|
|
a8d7ee3800 | ||
|
|
948b8272d3 | ||
|
|
985f25628f | ||
|
|
e4eb5eb28b | ||
|
|
7896477820 | ||
|
|
82d93388f4 | ||
|
|
87d1b75ccb | ||
|
|
6b69eb28a7 | ||
|
|
accb72bfc4 | ||
|
|
f76e1ec14f | ||
|
|
0e3688df22 | ||
|
|
87071ff4f2 | ||
|
|
a54e15196f | ||
|
|
957a21eaf8 | ||
|
|
80e85f5a31 | ||
|
|
3df4a9a075 | ||
|
|
e5d48a7ee3 | ||
|
|
5ea32f4156 | ||
|
|
370736acae | ||
|
|
b883d350da | ||
|
|
7e49baad76 | ||
|
|
ffeb4f0199 | ||
|
|
361806a473 | ||
|
|
8ddb69ac34 | ||
|
|
6ca9b7fe61 | ||
|
|
0bcefdc50b | ||
|
|
ff3ab2d4b5 | ||
|
|
5aefc63a88 | ||
|
|
75f0049cf8 | ||
|
|
84bbb4c0e3 | ||
|
|
b7c11ddaee | ||
|
|
b160a190a6 | ||
|
|
7941d34310 | ||
|
|
8b961977c6 | ||
|
|
63c12f195f | ||
|
|
d3e44f7463 | ||
|
|
70159861e2 | ||
|
|
dee38dca39 | ||
|
|
55ea88dac8 | ||
|
|
0149efa29e | ||
|
|
4d0ca068b8 | ||
|
|
03abf9f3aa | ||
|
|
45565d8b40 | ||
|
|
46bf0a6b78 | ||
|
|
2b165749a0 | ||
|
|
49ba435328 | ||
|
|
51f081c9dd | ||
|
|
287f7d8a97 | ||
|
|
42f4666907 | ||
|
|
d4c8344052 | ||
|
|
199b3e830e | ||
|
|
beb42c4108 | ||
|
|
ada700e1a8 | ||
|
|
2c2dde63d4 | ||
|
|
c027be2259 | ||
|
|
8487f1cf3b | ||
|
|
fe88ed5c8d | ||
|
|
6f11f6353a | ||
|
|
f01148df11 | ||
|
|
c62ac34df6 | ||
|
|
48a8f4eef3 | ||
|
|
745aea12af | ||
|
|
1a3ff04f85 | ||
|
|
1c6f73a454 | ||
|
|
6a64e6a26f | ||
|
|
b5d2b458be | ||
|
|
4a5a68c95d | ||
|
|
a97fc8404f | ||
|
|
38124040cc | ||
|
|
94d6c76773 | ||
|
|
b00bf440e9 | ||
|
|
6201d273fb | ||
|
|
2f5ef9a31d | ||
|
|
d703bfb666 | ||
|
|
139e0470e6 | ||
|
|
9746bc2eed | ||
|
|
ae607e65be | ||
|
|
de3f0952c6 | ||
|
|
40e32b5da9 | ||
|
|
c1895f39be | ||
|
|
375ca023d2 | ||
|
|
84d3bd6b09 | ||
|
|
fb7bbea3ce | ||
|
|
d81d77079a | ||
|
|
a1cfca14e2 | ||
|
|
5b60efe3c6 | ||
|
|
74f49f8d0a | ||
|
|
cbc1c63b31 | ||
|
|
a261fed9ed | ||
|
|
5bbaef5629 | ||
|
|
a687e8a3bc | ||
|
|
f1a797a69f | ||
|
|
69ff7bc194 | ||
|
|
b800cad272 | ||
|
|
419afb0d55 | ||
|
|
23d0278be5 | ||
|
|
d040c96e25 | ||
|
|
229ba8e5a0 | ||
|
|
09fa2f47e3 | ||
|
|
67e54f1420 | ||
|
|
3d35fc4dfd | ||
|
|
b025898bea | ||
|
|
0f7ebb61cc | ||
|
|
21f86e9b5d | ||
|
|
b1255e30e8 | ||
|
|
0e64c36e6c | ||
|
|
e412673c45 | ||
|
|
f5cac29af8 | ||
|
|
fc4dda3cc6 | ||
|
|
2ed1afa12b | ||
|
|
207bdd9aba | ||
|
|
c60857067b | ||
|
|
8e5a7cc285 | ||
|
|
8f66760133 | ||
|
|
80e03fb478 | ||
|
|
80e9b13121 | ||
|
|
1e8be4e525 | ||
|
|
d0c5d403e0 | ||
|
|
69a100d69d | ||
|
|
974032f903 | ||
|
|
8554264e72 | ||
|
|
095d3f357c | ||
|
|
20f41478eb | ||
|
|
893ca2c987 | ||
|
|
94df0f19c8 |
@ -188,7 +188,7 @@ pt.renewalReminder.days = mon,tue,wed,thu,fri
|
|||||||
pt.renewalReminder.clientTypes = 1,2
|
pt.renewalReminder.clientTypes = 1,2
|
||||||
# renewal_date | policy_end_date
|
# renewal_date | policy_end_date
|
||||||
pt.renewalReminder.dateField = renewal_date
|
pt.renewalReminder.dateField = renewal_date
|
||||||
pt.renewalReminder.daysBefore = 30, 15, 7, 1, -2, -5, -10
|
pt.renewalReminder.daysBefore = 30
|
||||||
pt.renewalReminder.includeClientEmail = true
|
pt.renewalReminder.includeClientEmail = true
|
||||||
# When true, also send for policies whose dateField is already past (overdue)
|
# When true, also send for policies whose dateField is already past (overdue)
|
||||||
pt.renewalReminder.includeOverdue = false
|
pt.renewalReminder.includeOverdue = false
|
||||||
|
|||||||
@ -20,8 +20,7 @@ class PtRenewalReminderConfig extends BaseConfig
|
|||||||
/** Whitelisted: renewal_date | policy_end_date */
|
/** Whitelisted: renewal_date | policy_end_date */
|
||||||
public string $dateField = 'renewal_date';
|
public string $dateField = 'renewal_date';
|
||||||
|
|
||||||
/** @var int[] Reminder offsets: positive = days before due, negative = days after due */
|
public int $daysBefore = 30;
|
||||||
public array $daysBeforeList = [30];
|
|
||||||
|
|
||||||
public bool $includeClientEmail = false;
|
public bool $includeClientEmail = false;
|
||||||
|
|
||||||
@ -62,7 +61,7 @@ class PtRenewalReminderConfig extends BaseConfig
|
|||||||
'days' => env('pt.renewalReminder.days', 'mon,tue,wed,thu,fri'),
|
'days' => env('pt.renewalReminder.days', 'mon,tue,wed,thu,fri'),
|
||||||
'clientTypes' => env('pt.renewalReminder.clientTypes', '2'),
|
'clientTypes' => env('pt.renewalReminder.clientTypes', '2'),
|
||||||
'dateField' => env('pt.renewalReminder.dateField', 'renewal_date'),
|
'dateField' => env('pt.renewalReminder.dateField', 'renewal_date'),
|
||||||
'daysBefore' => env('pt.renewalReminder.daysBefore', '30'),
|
'daysBefore' => env('pt.renewalReminder.daysBefore', 30),
|
||||||
'includeClientEmail' => env('pt.renewalReminder.includeClientEmail', 'true'),
|
'includeClientEmail' => env('pt.renewalReminder.includeClientEmail', 'true'),
|
||||||
'includeOverdue' => env('pt.renewalReminder.includeOverdue', 'false'),
|
'includeOverdue' => env('pt.renewalReminder.includeOverdue', 'false'),
|
||||||
'toEmails' => env('pt.renewalReminder.toEmails', ''),
|
'toEmails' => env('pt.renewalReminder.toEmails', ''),
|
||||||
@ -166,7 +165,7 @@ class PtRenewalReminderConfig extends BaseConfig
|
|||||||
}
|
}
|
||||||
$this->dateField = $dateField;
|
$this->dateField = $dateField;
|
||||||
|
|
||||||
$this->daysBeforeList = self::parseDaysBeforeList($merged['daysBefore'] ?? 30);
|
$this->daysBefore = max(0, (int) ($merged['daysBefore'] ?? 30));
|
||||||
|
|
||||||
$this->includeClientEmail = $this->toBool($merged['includeClientEmail'] ?? true);
|
$this->includeClientEmail = $this->toBool($merged['includeClientEmail'] ?? true);
|
||||||
$this->includeOverdue = $this->toBool($merged['includeOverdue'] ?? false);
|
$this->includeOverdue = $this->toBool($merged['includeOverdue'] ?? false);
|
||||||
@ -197,74 +196,9 @@ class PtRenewalReminderConfig extends BaseConfig
|
|||||||
return $this->dateField;
|
return $this->dateField;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* First / primary offset (legacy callers).
|
|
||||||
*/
|
|
||||||
public function getDaysBefore(): int
|
public function getDaysBefore(): int
|
||||||
{
|
{
|
||||||
return $this->daysBeforeList[0] ?? 30;
|
return $this->daysBefore;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int[]
|
|
||||||
*/
|
|
||||||
public function getDaysBeforeList(): array
|
|
||||||
{
|
|
||||||
return $this->daysBeforeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDaysBeforeDisplay(): string
|
|
||||||
{
|
|
||||||
return implode(', ', $this->daysBeforeList);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map each offset to the calendar date it matches (today + offset).
|
|
||||||
*
|
|
||||||
* @return array<int, string> offset => Y-m-d
|
|
||||||
*/
|
|
||||||
public function getTargetDates(?string $today = null): array
|
|
||||||
{
|
|
||||||
$today = $today ?: date('Y-m-d');
|
|
||||||
$map = [];
|
|
||||||
foreach ($this->daysBeforeList as $offset) {
|
|
||||||
$map[(int) $offset] = date('Y-m-d', strtotime($today . ' ' . sprintf('%+d days', (int) $offset)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parse "30, 15, 7, 1, -2, -5, -10" (or a single int / array) into unique offsets.
|
|
||||||
*
|
|
||||||
* @return int[]
|
|
||||||
*/
|
|
||||||
public static function parseDaysBeforeList(mixed $value): array
|
|
||||||
{
|
|
||||||
if (is_int($value) || is_float($value)) {
|
|
||||||
$raw = [(int) $value];
|
|
||||||
} elseif (is_array($value)) {
|
|
||||||
$raw = $value;
|
|
||||||
} else {
|
|
||||||
$raw = preg_split('/[,\s]+/', trim((string) $value), -1, PREG_SPLIT_NO_EMPTY) ?: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$out = [];
|
|
||||||
$seen = [];
|
|
||||||
foreach ($raw as $item) {
|
|
||||||
$item = trim((string) $item);
|
|
||||||
if ($item === '' || ! is_numeric($item)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$n = (int) $item;
|
|
||||||
if ($n > 365 || $n < -365 || isset($seen[$n])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$seen[$n] = true;
|
|
||||||
$out[] = $n;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $out !== [] ? $out : [30];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -337,7 +271,7 @@ class PtRenewalReminderConfig extends BaseConfig
|
|||||||
'days' => implode(',', $this->days),
|
'days' => implode(',', $this->days),
|
||||||
'clientTypes' => $this->clientTypes,
|
'clientTypes' => $this->clientTypes,
|
||||||
'dateField' => $this->dateField,
|
'dateField' => $this->dateField,
|
||||||
'daysBefore' => $this->getDaysBeforeDisplay(),
|
'daysBefore' => $this->daysBefore,
|
||||||
'includeClientEmail' => $this->includeClientEmail,
|
'includeClientEmail' => $this->includeClientEmail,
|
||||||
'includeOverdue' => $this->includeOverdue,
|
'includeOverdue' => $this->includeOverdue,
|
||||||
'toEmails' => implode(',', $this->toEmails),
|
'toEmails' => implode(',', $this->toEmails),
|
||||||
|
|||||||
@ -6014,7 +6014,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
$this->logRenewalReminderStep(
|
$this->logRenewalReminderStep(
|
||||||
$logPrefix . ' Fetching candidates for dateField='
|
$logPrefix . ' Fetching candidates for dateField='
|
||||||
. $config->getDateField()
|
. $config->getDateField()
|
||||||
. ', daysBefore=' . $config->getDaysBeforeDisplay()
|
. ', daysBefore=' . $config->getDaysBefore()
|
||||||
. ', includeOverdue=' . ($config->includeOverdue() ? 'true' : 'false')
|
. ', includeOverdue=' . ($config->includeOverdue() ? 'true' : 'false')
|
||||||
. ', clientTypes=' . json_encode($config->getClientTypes())
|
. ', clientTypes=' . json_encode($config->getClientTypes())
|
||||||
);
|
);
|
||||||
@ -6023,7 +6023,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
try {
|
try {
|
||||||
$candidates = $this->policyTransactionModel->getRenewalReminderCandidates(
|
$candidates = $this->policyTransactionModel->getRenewalReminderCandidates(
|
||||||
$config->getDateField(),
|
$config->getDateField(),
|
||||||
$config->getDaysBeforeList(),
|
$config->getDaysBefore(),
|
||||||
$config->getClientTypes(),
|
$config->getClientTypes(),
|
||||||
$config->includeOverdue()
|
$config->includeOverdue()
|
||||||
);
|
);
|
||||||
@ -6066,7 +6066,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
$this->logRenewalReminderStep(
|
$this->logRenewalReminderStep(
|
||||||
$logPrefix . ' ' . $message
|
$logPrefix . ' ' . $message
|
||||||
. ' | dateField=' . $config->getDateField()
|
. ' | dateField=' . $config->getDateField()
|
||||||
. ' | daysBefore=' . $config->getDaysBeforeDisplay()
|
. ' | daysBefore=' . $config->getDaysBefore()
|
||||||
. ' | includeOverdue=' . ($config->includeOverdue() ? 'true' : 'false')
|
. ' | includeOverdue=' . ($config->includeOverdue() ? 'true' : 'false')
|
||||||
);
|
);
|
||||||
if ($isCli) {
|
if ($isCli) {
|
||||||
@ -6081,7 +6081,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
try {
|
try {
|
||||||
$diagnostics = $this->policyTransactionModel->diagnoseRenewalReminderCandidates(
|
$diagnostics = $this->policyTransactionModel->diagnoseRenewalReminderCandidates(
|
||||||
$config->getDateField(),
|
$config->getDateField(),
|
||||||
$config->getDaysBeforeList(),
|
$config->getDaysBefore(),
|
||||||
$config->getClientTypes(),
|
$config->getClientTypes(),
|
||||||
$config->includeOverdue()
|
$config->includeOverdue()
|
||||||
);
|
);
|
||||||
@ -6119,9 +6119,6 @@ class PolicyTransactionController extends BaseController
|
|||||||
$emptyPayload['config'] = $configSnapshot;
|
$emptyPayload['config'] = $configSnapshot;
|
||||||
$emptyPayload['detailed_summary'] = $detailedSummary;
|
$emptyPayload['detailed_summary'] = $detailedSummary;
|
||||||
$emptyPayload['why_empty'] = $diagnostics;
|
$emptyPayload['why_empty'] = $diagnostics;
|
||||||
if (! empty($detailedSummary['client_mail'])) {
|
|
||||||
$emptyPayload['summary']['client_mail'] = $detailedSummary['client_mail'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->renewalReminderResponse($emptyPayload, $isCli);
|
return $this->renewalReminderResponse($emptyPayload, $isCli);
|
||||||
@ -6228,8 +6225,6 @@ class PolicyTransactionController extends BaseController
|
|||||||
'policy_start_date' => $row['policy_start_date'] ?? null,
|
'policy_start_date' => $row['policy_start_date'] ?? null,
|
||||||
'policy_end_date' => $row['policy_end_date'] ?? null,
|
'policy_end_date' => $row['policy_end_date'] ?? null,
|
||||||
'reminder_date' => ! empty($reminderRaw) ? date('Y-m-d', strtotime((string) $reminderRaw)) : null,
|
'reminder_date' => ! empty($reminderRaw) ? date('Y-m-d', strtotime((string) $reminderRaw)) : null,
|
||||||
'days_before' => isset($row['reminder_offset']) ? (int) $row['reminder_offset'] : null,
|
|
||||||
'reminder_offset' => isset($row['reminder_offset']) ? (int) $row['reminder_offset'] : null,
|
|
||||||
'is_overdue' => $isOverdue,
|
'is_overdue' => $isOverdue,
|
||||||
'status' => 'dry_run',
|
'status' => 'dry_run',
|
||||||
'message' => 'Dry run — mail not sent, status not updated',
|
'message' => 'Dry run — mail not sent, status not updated',
|
||||||
@ -6290,10 +6285,9 @@ class PolicyTransactionController extends BaseController
|
|||||||
if (isset($res->status) && $res->status === 'success') {
|
if (isset($res->status) && $res->status === 'success') {
|
||||||
$successCount++;
|
$successCount++;
|
||||||
try {
|
try {
|
||||||
$this->policyTransactionModel->markRenewalReminderSent(
|
$this->policyTransactionModel->update((int) $ptId, [
|
||||||
(int) $ptId,
|
'renewal_status' => 'renewal_mail_send',
|
||||||
isset($row['reminder_offset']) ? (int) $row['reminder_offset'] : null
|
]);
|
||||||
);
|
|
||||||
$this->logRenewalReminderStep(
|
$this->logRenewalReminderStep(
|
||||||
$logPrefix . " PT {$ptId}: renewal_status set to renewal_mail_send"
|
$logPrefix . " PT {$ptId}: renewal_status set to renewal_mail_send"
|
||||||
);
|
);
|
||||||
@ -6413,9 +6407,6 @@ class PolicyTransactionController extends BaseController
|
|||||||
$responsePayload['config'] = $configSnapshot;
|
$responsePayload['config'] = $configSnapshot;
|
||||||
$responsePayload['detailed_summary'] = $detailedSummary;
|
$responsePayload['detailed_summary'] = $detailedSummary;
|
||||||
$responsePayload['message'] = $detailedSummary['headline'] ?? $summaryMessage;
|
$responsePayload['message'] = $detailedSummary['headline'] ?? $summaryMessage;
|
||||||
if (! empty($detailedSummary['client_mail'])) {
|
|
||||||
$responsePayload['summary']['client_mail'] = $detailedSummary['client_mail'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->renewalReminderResponse($responsePayload, $isCli);
|
return $this->renewalReminderResponse($responsePayload, $isCli);
|
||||||
@ -6504,8 +6495,6 @@ class PolicyTransactionController extends BaseController
|
|||||||
$policiesWithoutPolicyNo = 0;
|
$policiesWithoutPolicyNo = 0;
|
||||||
$policiesWithClientEmailInTo = 0;
|
$policiesWithClientEmailInTo = 0;
|
||||||
$policiesMissingClientEmail = 0;
|
$policiesMissingClientEmail = 0;
|
||||||
$policiesWithClientEmailOnRecord = 0;
|
|
||||||
$policiesConfiguredToOnly = 0;
|
|
||||||
$clientEmailsInTo = [];
|
$clientEmailsInTo = [];
|
||||||
|
|
||||||
foreach ($results as $item) {
|
foreach ($results as $item) {
|
||||||
@ -6537,11 +6526,6 @@ class PolicyTransactionController extends BaseController
|
|||||||
$policiesWithoutPolicyNo++;
|
$policiesWithoutPolicyNo++;
|
||||||
}
|
}
|
||||||
|
|
||||||
$clientEmailRaw = trim((string) ($item['client_email'] ?? ''));
|
|
||||||
if ($clientEmailRaw !== '') {
|
|
||||||
$policiesWithClientEmailOnRecord++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$clientEmailIncluded = ! empty($item['client_email_included']);
|
$clientEmailIncluded = ! empty($item['client_email_included']);
|
||||||
if ($clientEmailIncluded) {
|
if ($clientEmailIncluded) {
|
||||||
$policiesWithClientEmailInTo++;
|
$policiesWithClientEmailInTo++;
|
||||||
@ -6555,20 +6539,15 @@ class PolicyTransactionController extends BaseController
|
|||||||
$policiesMissingClientEmail++;
|
$policiesMissingClientEmail++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($status === 'dry_run' && ! $clientEmailIncluded && (int) ($item['to_count'] ?? count($item['to'] ?? [])) > 0) {
|
|
||||||
$policiesConfiguredToOnly++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$compact = [
|
$compact = [
|
||||||
'pt_id' => $item['pt_id'] ?? null,
|
'pt_id' => $item['pt_id'] ?? null,
|
||||||
'policy_no' => $policyNo !== '' ? $policyNo : 'Not Assigned',
|
'policy_no' => $policyNo !== '' ? $policyNo : 'Not Assigned',
|
||||||
'client_name' => $item['client_name'] ?? null,
|
'client_name' => $item['client_name'] ?? null,
|
||||||
'client_email' => $clientEmailRaw !== '' ? $clientEmailRaw : null,
|
'client_email' => $item['client_email'] ?? null,
|
||||||
'client_email_included' => $clientEmailIncluded,
|
'client_email_included' => $clientEmailIncluded,
|
||||||
'include_client_email_enabled' => ! empty($item['include_client_email_enabled']),
|
'include_client_email_enabled' => ! empty($item['include_client_email_enabled']),
|
||||||
'policy_type' => $item['policy_type'] ?? null,
|
'policy_type' => $item['policy_type'] ?? null,
|
||||||
'reminder_date' => $item['reminder_date'] ?? ($item['renewal_date'] ?? $item['policy_end_date'] ?? null),
|
'reminder_date' => $item['reminder_date'] ?? ($item['renewal_date'] ?? $item['policy_end_date'] ?? null),
|
||||||
'days_before' => $item['days_before'] ?? ($item['reminder_offset'] ?? null),
|
|
||||||
'is_overdue' => $isOverdue,
|
'is_overdue' => $isOverdue,
|
||||||
'to' => $item['to'] ?? [],
|
'to' => $item['to'] ?? [],
|
||||||
'to_count' => (int) ($item['to_count'] ?? count($item['to'] ?? [])),
|
'to_count' => (int) ($item['to_count'] ?? count($item['to'] ?? [])),
|
||||||
@ -6611,8 +6590,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$targetDates = $config->getTargetDates();
|
$targetDate = date('Y-m-d', strtotime('+' . max(0, (int) $config->getDaysBefore()) . ' days'));
|
||||||
$targetDate = implode(', ', array_values($targetDates));
|
|
||||||
|
|
||||||
$headline = sprintf(
|
$headline = sprintf(
|
||||||
'DRY RUN — %d policy(ies) pending for renewal reminder: %d ready to mail, %d skipped (no email), %d failed | client email in TO: %d, missing client email: %d',
|
'DRY RUN — %d policy(ies) pending for renewal reminder: %d ready to mail, %d skipped (no email), %d failed | client email in TO: %d, missing client email: %d',
|
||||||
@ -6633,7 +6611,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
'Policies with client email in TO: ' . $policiesWithClientEmailInTo,
|
'Policies with client email in TO: ' . $policiesWithClientEmailInTo,
|
||||||
'Policies missing/invalid client email: ' . $policiesMissingClientEmail,
|
'Policies missing/invalid client email: ' . $policiesMissingClientEmail,
|
||||||
'Unique client emails in TO: ' . count($clientEmailsInTo),
|
'Unique client emails in TO: ' . count($clientEmailsInTo),
|
||||||
'Due on target date(s) (' . $targetDate . '): ' . $dueCount,
|
'Due on target date (' . $targetDate . '): ' . $dueCount,
|
||||||
'Overdue policies included: ' . $overdueCount,
|
'Overdue policies included: ' . $overdueCount,
|
||||||
'Unique TO recipients: ' . count($uniqueTo),
|
'Unique TO recipients: ' . count($uniqueTo),
|
||||||
'Unique CC recipients: ' . count($uniqueCc),
|
'Unique CC recipients: ' . count($uniqueCc),
|
||||||
@ -6641,24 +6619,13 @@ class PolicyTransactionController extends BaseController
|
|||||||
'Total TO deliveries (per mail): ' . $totalToHits,
|
'Total TO deliveries (per mail): ' . $totalToHits,
|
||||||
'Policies without policy no: ' . $policiesWithoutPolicyNo,
|
'Policies without policy no: ' . $policiesWithoutPolicyNo,
|
||||||
'Date field: ' . $config->getDateField(),
|
'Date field: ' . $config->getDateField(),
|
||||||
'Days before: ' . $config->getDaysBeforeDisplay(),
|
'Days before: ' . $config->getDaysBefore(),
|
||||||
'Include overdue: ' . ($config->includeOverdue() ? 'yes' : 'no'),
|
'Include overdue: ' . ($config->includeOverdue() ? 'yes' : 'no'),
|
||||||
];
|
];
|
||||||
|
|
||||||
$clientMailSummary = [
|
|
||||||
'enabled' => $config->includeClientEmail(),
|
|
||||||
'policies_with_email_on_record' => $policiesWithClientEmailOnRecord,
|
|
||||||
'policies_included_in_to' => $policiesWithClientEmailInTo,
|
|
||||||
'policies_missing_or_invalid' => $policiesMissingClientEmail,
|
|
||||||
'policies_configured_to_only' => $policiesConfiguredToOnly,
|
|
||||||
'unique_client_emails_in_to' => count($clientEmailsInTo),
|
|
||||||
'client_emails_in_to' => array_keys($clientEmailsInTo),
|
|
||||||
];
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'headline' => $headline,
|
'headline' => $headline,
|
||||||
'lines' => $lines,
|
'lines' => $lines,
|
||||||
'client_mail' => $clientMailSummary,
|
|
||||||
'counts' => [
|
'counts' => [
|
||||||
'policies_pending_renewal' => $candidateCount,
|
'policies_pending_renewal' => $candidateCount,
|
||||||
'policies_ready_to_mail' => $wouldSendCount,
|
'policies_ready_to_mail' => $wouldSendCount,
|
||||||
@ -6669,10 +6636,8 @@ class PolicyTransactionController extends BaseController
|
|||||||
'policies_without_policy_no' => $policiesWithoutPolicyNo,
|
'policies_without_policy_no' => $policiesWithoutPolicyNo,
|
||||||
'policies_without_email' => $policiesWithoutEmail,
|
'policies_without_email' => $policiesWithoutEmail,
|
||||||
'include_client_email_enabled' => $config->includeClientEmail(),
|
'include_client_email_enabled' => $config->includeClientEmail(),
|
||||||
'policies_with_client_email_on_record' => $policiesWithClientEmailOnRecord,
|
|
||||||
'policies_with_client_email_in_to' => $policiesWithClientEmailInTo,
|
'policies_with_client_email_in_to' => $policiesWithClientEmailInTo,
|
||||||
'policies_missing_client_email' => $policiesMissingClientEmail,
|
'policies_missing_client_email' => $policiesMissingClientEmail,
|
||||||
'policies_configured_to_only' => $policiesConfiguredToOnly,
|
|
||||||
'unique_client_emails_in_to' => count($clientEmailsInTo),
|
'unique_client_emails_in_to' => count($clientEmailsInTo),
|
||||||
'unique_to_recipients' => count($uniqueTo),
|
'unique_to_recipients' => count($uniqueTo),
|
||||||
'unique_cc_recipients' => count($uniqueCc),
|
'unique_cc_recipients' => count($uniqueCc),
|
||||||
@ -6684,9 +6649,8 @@ class PolicyTransactionController extends BaseController
|
|||||||
],
|
],
|
||||||
'filters' => [
|
'filters' => [
|
||||||
'date_field' => $config->getDateField(),
|
'date_field' => $config->getDateField(),
|
||||||
'days_before' => $config->getDaysBeforeList(),
|
'days_before' => $config->getDaysBefore(),
|
||||||
'target_date' => $targetDates[$config->getDaysBefore()] ?? reset($targetDates),
|
'target_date' => $targetDate,
|
||||||
'target_dates' => $targetDates,
|
|
||||||
'include_overdue' => $config->includeOverdue(),
|
'include_overdue' => $config->includeOverdue(),
|
||||||
'include_client_email' => $config->includeClientEmail(),
|
'include_client_email' => $config->includeClientEmail(),
|
||||||
'client_types' => $config->getClientTypes(),
|
'client_types' => $config->getClientTypes(),
|
||||||
@ -6758,9 +6722,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
$dateField = $config->getDateField();
|
$dateField = $config->getDateField();
|
||||||
$reminderRaw = $data[$dateField] ?? ($data['renewal_date'] ?? $data['policy_end_date'] ?? null);
|
$reminderRaw = $data[$dateField] ?? ($data['renewal_date'] ?? $data['policy_end_date'] ?? null);
|
||||||
$reminderDate = ! empty($reminderRaw) ? date('d-m-Y', strtotime((string) $reminderRaw)) : '';
|
$reminderDate = ! empty($reminderRaw) ? date('d-m-Y', strtotime((string) $reminderRaw)) : '';
|
||||||
$daysBefore = isset($data['reminder_offset']) && $data['reminder_offset'] !== '' && $data['reminder_offset'] !== null
|
$daysBefore = $config->getDaysBefore();
|
||||||
? (int) $data['reminder_offset']
|
|
||||||
: $config->getDaysBefore();
|
|
||||||
$isOverdue = ! empty($data['is_overdue'])
|
$isOverdue = ! empty($data['is_overdue'])
|
||||||
|| (! empty($reminderRaw) && strtotime((string) $reminderRaw) < strtotime(date('Y-m-d')));
|
|| (! empty($reminderRaw) && strtotime((string) $reminderRaw) < strtotime(date('Y-m-d')));
|
||||||
$policyLabel = $policyNo !== '' ? "Policy #{$policyNo}" : 'Policy Pending';
|
$policyLabel = $policyNo !== '' ? "Policy #{$policyNo}" : 'Policy Pending';
|
||||||
@ -7115,7 +7077,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
'days' => trim((string) $this->request->getPost('days')),
|
'days' => trim((string) $this->request->getPost('days')),
|
||||||
'clientTypes' => [2],
|
'clientTypes' => [2],
|
||||||
'dateField' => trim((string) $this->request->getPost('dateField')),
|
'dateField' => trim((string) $this->request->getPost('dateField')),
|
||||||
'daysBefore' => PtRenewalReminderConfig::parseDaysBeforeList($this->request->getPost('daysBefore')),
|
'daysBefore' => (int) $this->request->getPost('daysBefore'),
|
||||||
'includeClientEmail' => $this->request->getPost('includeClientEmail'),
|
'includeClientEmail' => $this->request->getPost('includeClientEmail'),
|
||||||
'includeOverdue' => $this->request->getPost('includeOverdue'),
|
'includeOverdue' => $this->request->getPost('includeOverdue'),
|
||||||
'toEmails' => trim((string) $this->request->getPost('toEmails')),
|
'toEmails' => trim((string) $this->request->getPost('toEmails')),
|
||||||
@ -7132,7 +7094,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
if (! in_array($payload['dateField'], ['renewal_date', 'policy_end_date'], true)) {
|
if (! in_array($payload['dateField'], ['renewal_date', 'policy_end_date'], true)) {
|
||||||
$payload['dateField'] = 'renewal_date';
|
$payload['dateField'] = 'renewal_date';
|
||||||
}
|
}
|
||||||
$payload['daysBefore'] = implode(', ', $payload['daysBefore']);
|
$payload['daysBefore'] = max(0, $payload['daysBefore']);
|
||||||
|
|
||||||
$updated = $this->notificationModel->update((int) $row['id'], [
|
$updated = $this->notificationModel->update((int) $row['id'], [
|
||||||
'config_json' => json_encode($payload),
|
'config_json' => json_encode($payload),
|
||||||
@ -7164,12 +7126,6 @@ class PolicyTransactionController extends BaseController
|
|||||||
$config['days'] = implode(',', $config['days']);
|
$config['days'] = implode(',', $config['days']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_array($config['daysBefore'] ?? null)) {
|
|
||||||
$config['daysBefore'] = implode(', ', $config['daysBefore']);
|
|
||||||
} elseif (isset($config['daysBefore'])) {
|
|
||||||
$config['daysBefore'] = implode(', ', PtRenewalReminderConfig::parseDaysBeforeList($config['daysBefore']));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (['toEmails', 'ccEmails', 'bccEmails'] as $field) {
|
foreach (['toEmails', 'ccEmails', 'bccEmails'] as $field) {
|
||||||
if (is_array($config[$field] ?? null)) {
|
if (is_array($config[$field] ?? null)) {
|
||||||
$config[$field] = implode(',', $config[$field]);
|
$config[$field] = implode(',', $config[$field]);
|
||||||
@ -7401,7 +7357,7 @@ class PolicyTransactionController extends BaseController
|
|||||||
'days' => 'mon,tue,wed,thu,fri',
|
'days' => 'mon,tue,wed,thu,fri',
|
||||||
'clientTypes' => [2],
|
'clientTypes' => [2],
|
||||||
'dateField' => 'renewal_date',
|
'dateField' => 'renewal_date',
|
||||||
'daysBefore' => '30',
|
'daysBefore' => 30,
|
||||||
'includeClientEmail' => true,
|
'includeClientEmail' => true,
|
||||||
'includeOverdue' => false,
|
'includeOverdue' => false,
|
||||||
'toEmails' => '',
|
'toEmails' => '',
|
||||||
|
|||||||
@ -63,7 +63,6 @@
|
|||||||
'edate',
|
'edate',
|
||||||
'renewal_date',
|
'renewal_date',
|
||||||
'renewal_status',
|
'renewal_status',
|
||||||
'renewal_last_reminder_offset',
|
|
||||||
'rollover_date',
|
'rollover_date',
|
||||||
'policy_holder_name',
|
'policy_holder_name',
|
||||||
'same_as_proposer',
|
'same_as_proposer',
|
||||||
@ -2132,15 +2131,15 @@
|
|||||||
/**
|
/**
|
||||||
* Candidates for policy_transaction renewal reminder cron.
|
* Candidates for policy_transaction renewal reminder cron.
|
||||||
*
|
*
|
||||||
* @param string $dateField Whitelisted column: renewal_date | policy_end_date
|
* @param string $dateField Whitelisted column: renewal_date | policy_end_date
|
||||||
* @param int|string|int[] $daysBefore Comma-separated or list of offsets (negative = days after due)
|
* @param int $daysBefore Days ahead of today to match
|
||||||
* @param int[] $clientTypes Client type ids (1=Group, 2=Individual)
|
* @param int[] $clientTypes Client type ids (1=Group, 2=Individual)
|
||||||
* @param bool $includeOverdue When true, also include rows where dateField < today
|
* @param bool $includeOverdue When true, also include rows where dateField < today
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getRenewalReminderCandidates(
|
public function getRenewalReminderCandidates(
|
||||||
string $dateField,
|
string $dateField,
|
||||||
$daysBefore,
|
int $daysBefore,
|
||||||
array $clientTypes,
|
array $clientTypes,
|
||||||
bool $includeOverdue = false
|
bool $includeOverdue = false
|
||||||
): array {
|
): array {
|
||||||
@ -2157,10 +2156,10 @@
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$offsets = $this->normalizeReminderOffsets($daysBefore);
|
$daysBefore = max(0, (int) $daysBefore);
|
||||||
$today = date('Y-m-d');
|
$targetDate = date('Y-m-d', strtotime('+' . $daysBefore . ' days'));
|
||||||
$targetDates = $this->reminderTargetDates($offsets, $today);
|
$today = date('Y-m-d');
|
||||||
$column = 'pt.' . $dateField;
|
$column = 'pt.' . $dateField;
|
||||||
|
|
||||||
$query = $this->db->table('policy_transaction pt')
|
$query = $this->db->table('policy_transaction pt')
|
||||||
->select("
|
->select("
|
||||||
@ -2178,7 +2177,6 @@
|
|||||||
c.common_mails,
|
c.common_mails,
|
||||||
policy_type.policy_type,
|
policy_type.policy_type,
|
||||||
i.name AS insurer_name,
|
i.name AS insurer_name,
|
||||||
DATEDIFF({$column}, CURDATE()) AS reminder_offset,
|
|
||||||
CASE WHEN {$column} < '{$today}' THEN 1 ELSE 0 END AS is_overdue
|
CASE WHEN {$column} < '{$today}' THEN 1 ELSE 0 END AS is_overdue
|
||||||
")
|
")
|
||||||
->join('clients c', 'pt.client_id = c.id', 'left')
|
->join('clients c', 'pt.client_id = c.id', 'left')
|
||||||
@ -2187,9 +2185,18 @@
|
|||||||
->join('policy_type', 'pt.policy_type_id = policy_type.id', 'left')
|
->join('policy_type', 'pt.policy_type_id = policy_type.id', 'left')
|
||||||
->where('pt.is_active', 1)
|
->where('pt.is_active', 1)
|
||||||
->where('pt.action_type', 'inception')
|
->where('pt.action_type', 'inception')
|
||||||
->whereIn('c.client_type', $clientTypes);
|
->whereIn('c.client_type', $clientTypes)
|
||||||
|
->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'renewed', 'reminded')", null, false)
|
||||||
|
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)");
|
||||||
|
|
||||||
$this->applyRenewalReminderCandidateFilters($query, $column, $targetDates, $today, $includeOverdue);
|
if ($includeOverdue) {
|
||||||
|
$query->groupStart()
|
||||||
|
->where($column, $targetDate)
|
||||||
|
->orWhere("{$column} <", $today)
|
||||||
|
->groupEnd();
|
||||||
|
} else {
|
||||||
|
$query->where($column, $targetDate);
|
||||||
|
}
|
||||||
|
|
||||||
$query->groupBy('pt.id')
|
$query->groupBy('pt.id')
|
||||||
->orderBy('pt.id', 'DESC');
|
->orderBy('pt.id', 'DESC');
|
||||||
@ -2200,13 +2207,12 @@
|
|||||||
/**
|
/**
|
||||||
* Explain why renewal reminder candidates may be empty (for dry-run diagnostics).
|
* Explain why renewal reminder candidates may be empty (for dry-run diagnostics).
|
||||||
*
|
*
|
||||||
* @param int|string|int[] $daysBefore
|
|
||||||
* @param int[] $clientTypes
|
* @param int[] $clientTypes
|
||||||
* @return array<string, mixed>
|
* @return array<string, mixed>
|
||||||
*/
|
*/
|
||||||
public function diagnoseRenewalReminderCandidates(
|
public function diagnoseRenewalReminderCandidates(
|
||||||
string $dateField,
|
string $dateField,
|
||||||
$daysBefore,
|
int $daysBefore,
|
||||||
array $clientTypes,
|
array $clientTypes,
|
||||||
bool $includeOverdue = false
|
bool $includeOverdue = false
|
||||||
): array {
|
): array {
|
||||||
@ -2219,11 +2225,10 @@
|
|||||||
return $type > 0;
|
return $type > 0;
|
||||||
})));
|
})));
|
||||||
|
|
||||||
$offsets = $this->normalizeReminderOffsets($daysBefore);
|
$daysBefore = max(0, (int) $daysBefore);
|
||||||
$today = date('Y-m-d');
|
$targetDate = date('Y-m-d', strtotime('+' . $daysBefore . ' days'));
|
||||||
$targetDates = $this->reminderTargetDates($offsets, $today);
|
$today = date('Y-m-d');
|
||||||
$column = 'pt.' . $dateField;
|
$column = 'pt.' . $dateField;
|
||||||
$targetLabel = implode(', ', $targetDates);
|
|
||||||
|
|
||||||
$base = function () {
|
$base = function () {
|
||||||
return $this->db->table('policy_transaction pt')
|
return $this->db->table('policy_transaction pt')
|
||||||
@ -2250,25 +2255,25 @@
|
|||||||
|
|
||||||
$countAfterStatus = (int) $base()
|
$countAfterStatus = (int) $base()
|
||||||
->whereIn('c.client_type', $clientTypes)
|
->whereIn('c.client_type', $clientTypes)
|
||||||
->where("IFNULL(pt.renewal_status, '') !=", 'renewed')
|
->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'renewed', 'reminded')", null, false)
|
||||||
->countAllResults();
|
->countAllResults();
|
||||||
|
|
||||||
$countAfterNotRenewedExists = (int) $base()
|
$countAfterNotRenewedExists = (int) $base()
|
||||||
->whereIn('c.client_type', $clientTypes)
|
->whereIn('c.client_type', $clientTypes)
|
||||||
->where("IFNULL(pt.renewal_status, '') !=", 'renewed')
|
->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'renewed', 'reminded')", null, false)
|
||||||
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)")
|
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)")
|
||||||
->countAllResults();
|
->countAllResults();
|
||||||
|
|
||||||
$countExactTargetDate = (int) $base()
|
$countExactTargetDate = (int) $base()
|
||||||
->whereIn('c.client_type', $clientTypes)
|
->whereIn('c.client_type', $clientTypes)
|
||||||
->where("IFNULL(pt.renewal_status, '') !=", 'renewed')
|
->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'renewed', 'reminded')", null, false)
|
||||||
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)")
|
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)")
|
||||||
->whereIn($column, $targetDates)
|
->where($column, $targetDate)
|
||||||
->countAllResults();
|
->countAllResults();
|
||||||
|
|
||||||
$countOverdue = (int) $base()
|
$countOverdue = (int) $base()
|
||||||
->whereIn('c.client_type', $clientTypes)
|
->whereIn('c.client_type', $clientTypes)
|
||||||
->where("IFNULL(pt.renewal_status, '') !=", 'renewed')
|
->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'renewed', 'reminded')", null, false)
|
||||||
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)")
|
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)")
|
||||||
->where("{$column} <", $today)
|
->where("{$column} <", $today)
|
||||||
->where("{$column} IS NOT NULL", null, false)
|
->where("{$column} IS NOT NULL", null, false)
|
||||||
@ -2276,16 +2281,27 @@
|
|||||||
->countAllResults();
|
->countAllResults();
|
||||||
|
|
||||||
$finalBuilder = $base()
|
$finalBuilder = $base()
|
||||||
->whereIn('c.client_type', $clientTypes);
|
->whereIn('c.client_type', $clientTypes)
|
||||||
$this->applyRenewalReminderCandidateFilters($finalBuilder, $column, $targetDates, $today, $includeOverdue);
|
->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'renewed', 'reminded')", null, false)
|
||||||
|
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)");
|
||||||
|
|
||||||
|
if ($includeOverdue) {
|
||||||
|
$finalBuilder->groupStart()
|
||||||
|
->where($column, $targetDate)
|
||||||
|
->orWhere("{$column} <", $today)
|
||||||
|
->groupEnd();
|
||||||
|
} else {
|
||||||
|
$finalBuilder->where($column, $targetDate);
|
||||||
|
}
|
||||||
$countFinal = (int) $finalBuilder->countAllResults();
|
$countFinal = (int) $finalBuilder->countAllResults();
|
||||||
|
|
||||||
|
// Sample nearest upcoming renewals (next 60 days) for debugging
|
||||||
$from = $today;
|
$from = $today;
|
||||||
$to = date('Y-m-d', strtotime('+60 days'));
|
$to = date('Y-m-d', strtotime('+60 days'));
|
||||||
$nearestSample = $base()
|
$nearestSample = $base()
|
||||||
->select("pt.id, pt.policy_no, c.client_name, c.email AS client_email, pt.renewal_date, pt.policy_end_date, pt.renewal_status, {$column} AS match_date, DATEDIFF({$column}, CURDATE()) AS reminder_offset")
|
->select("pt.id, pt.policy_no, c.client_name, c.email AS client_email, pt.renewal_date, pt.policy_end_date, pt.renewal_status, {$column} AS match_date")
|
||||||
->whereIn('c.client_type', $clientTypes)
|
->whereIn('c.client_type', $clientTypes)
|
||||||
->where("IFNULL(pt.renewal_status, '') !=", 'renewed')
|
->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'renewed', 'reminded')", null, false)
|
||||||
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)")
|
->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)")
|
||||||
->where("{$column} >=", $from)
|
->where("{$column} >=", $from)
|
||||||
->where("{$column} <=", $to)
|
->where("{$column} <=", $to)
|
||||||
@ -2302,16 +2318,16 @@
|
|||||||
if ($countAfterClientType === 0) {
|
if ($countAfterClientType === 0) {
|
||||||
$reasons[] = 'No active inception policies found for configured client type(s).';
|
$reasons[] = 'No active inception policies found for configured client type(s).';
|
||||||
} elseif ($countAfterStatus === 0) {
|
} elseif ($countAfterStatus === 0) {
|
||||||
$reasons[] = 'All matching policies already have renewal_status of renewed.';
|
$reasons[] = 'All matching policies already have renewal_status of renewal_mail_send/renewed/reminded.';
|
||||||
} elseif ($countAfterNotRenewedExists === 0) {
|
} elseif ($countAfterNotRenewedExists === 0) {
|
||||||
$reasons[] = 'All remaining policies already have an active renewed policy linked via source_client_policy_id.';
|
$reasons[] = 'All remaining policies already have an active renewed policy linked via source_client_policy_id.';
|
||||||
} elseif (! $includeOverdue && $countExactTargetDate === 0) {
|
} elseif (! $includeOverdue && $countExactTargetDate === 0) {
|
||||||
$reasons[] = "No policies have {$dateField} equal to any target date ({$targetLabel}) for offsets [" . implode(', ', $offsets) . '].';
|
$reasons[] = "No policies have {$dateField} exactly equal to target date {$targetDate} (today + {$daysBefore} days). includeOverdue is false, so only exact date matches are selected.";
|
||||||
if ($countOverdue > 0) {
|
if ($countOverdue > 0) {
|
||||||
$reasons[] = "There are {$countOverdue} overdue policies that would be included if includeOverdue=true, or if a negative offset matches today.";
|
$reasons[] = "There are {$countOverdue} overdue policies that would be included if includeOverdue=true.";
|
||||||
}
|
}
|
||||||
} elseif ($includeOverdue && $countFinal === 0) {
|
} elseif ($includeOverdue && $countFinal === 0) {
|
||||||
$reasons[] = "No policies match target dates ({$targetLabel}) or overdue (< {$today}) on {$dateField}.";
|
$reasons[] = "No policies match target date {$targetDate} or overdue (< {$today}) on {$dateField}.";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($reasons) && $countFinal === 0) {
|
if (empty($reasons) && $countFinal === 0) {
|
||||||
@ -2320,15 +2336,14 @@
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
'today' => $today,
|
'today' => $today,
|
||||||
'target_date' => $targetDates[0] ?? null,
|
'target_date' => $targetDate,
|
||||||
'target_dates' => $targetDates,
|
|
||||||
'date_field' => $dateField,
|
'date_field' => $dateField,
|
||||||
'days_before' => $offsets,
|
'days_before' => $daysBefore,
|
||||||
'include_overdue' => $includeOverdue,
|
'include_overdue' => $includeOverdue,
|
||||||
'client_types' => $clientTypes,
|
'client_types' => $clientTypes,
|
||||||
'counts' => [
|
'counts' => [
|
||||||
'active_inception_for_client_types' => $countAfterClientType,
|
'active_inception_for_client_types' => $countAfterClientType,
|
||||||
'after_excluding_renewed' => $countAfterStatus,
|
'after_excluding_sent_or_renewed' => $countAfterStatus,
|
||||||
'after_excluding_already_renewed_policy' => $countAfterNotRenewedExists,
|
'after_excluding_already_renewed_policy' => $countAfterNotRenewedExists,
|
||||||
'exact_target_date_matches' => $countExactTargetDate,
|
'exact_target_date_matches' => $countExactTargetDate,
|
||||||
'overdue_matches' => $countOverdue,
|
'overdue_matches' => $countOverdue,
|
||||||
@ -2337,106 +2352,11 @@
|
|||||||
'reasons' => $reasons,
|
'reasons' => $reasons,
|
||||||
'nearest_upcoming_sample_60_days' => $nearestSample,
|
'nearest_upcoming_sample_60_days' => $nearestSample,
|
||||||
'hint' => $includeOverdue
|
'hint' => $includeOverdue
|
||||||
? 'Candidates = any configured offset date OR overdue on selected date field.'
|
? 'Candidates = exact target date OR overdue on selected date field.'
|
||||||
: "Candidates require {$dateField} IN ({$targetLabel}). Negative offsets send after the due date. Change daysBefore in Mail Config.",
|
: "Candidates require {$dateField} = {$targetDate} exactly. Enable includeOverdue to also include past dates, or change daysBefore / dateField in Mail Config.",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark a policy as mailed for the current reminder offset.
|
|
||||||
*/
|
|
||||||
public function markRenewalReminderSent(int $ptId, ?int $offset = null): bool
|
|
||||||
{
|
|
||||||
$data = ['renewal_status' => 'renewal_mail_send'];
|
|
||||||
if ($offset !== null && $this->hasRenewalLastReminderOffsetColumn()) {
|
|
||||||
$data['renewal_last_reminder_offset'] = $offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (bool) $this->update($ptId, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int|string|int[] $daysBefore
|
|
||||||
* @return int[]
|
|
||||||
*/
|
|
||||||
private function normalizeReminderOffsets($daysBefore): array
|
|
||||||
{
|
|
||||||
return \Config\PtRenewalReminderConfig::parseDaysBeforeList($daysBefore);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int[] $offsets
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
private function reminderTargetDates(array $offsets, string $today): array
|
|
||||||
{
|
|
||||||
$dates = [];
|
|
||||||
foreach ($offsets as $offset) {
|
|
||||||
$dates[] = date('Y-m-d', strtotime($today . ' ' . sprintf('%+d days', (int) $offset)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_values(array_unique($dates));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function hasRenewalLastReminderOffsetColumn(): bool
|
|
||||||
{
|
|
||||||
static $exists = null;
|
|
||||||
if ($exists === null) {
|
|
||||||
$exists = $this->db->fieldExists('renewal_last_reminder_offset', $this->table);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $exists;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param object $query Query builder
|
|
||||||
* @param string[] $targetDates
|
|
||||||
*/
|
|
||||||
private function applyRenewalReminderCandidateFilters(
|
|
||||||
$query,
|
|
||||||
string $column,
|
|
||||||
array $targetDates,
|
|
||||||
string $today,
|
|
||||||
bool $includeOverdue
|
|
||||||
): void {
|
|
||||||
$query->where("NOT EXISTS (SELECT 1 FROM policy_transaction p2 WHERE p2.source_client_policy_id = pt.client_policy_id AND p2.is_active = 1)");
|
|
||||||
|
|
||||||
if ($this->hasRenewalLastReminderOffsetColumn()) {
|
|
||||||
$query->where("IFNULL(pt.renewal_status, '') !=", 'renewed');
|
|
||||||
$offsetSkip = "(pt.renewal_last_reminder_offset IS NULL OR pt.renewal_last_reminder_offset != DATEDIFF({$column}, CURDATE()))";
|
|
||||||
|
|
||||||
if ($includeOverdue) {
|
|
||||||
$query->groupStart()
|
|
||||||
->groupStart()
|
|
||||||
->whereIn($column, $targetDates)
|
|
||||||
->where($offsetSkip, null, false)
|
|
||||||
->groupEnd()
|
|
||||||
->orGroupStart()
|
|
||||||
->where("{$column} <", $today)
|
|
||||||
->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'reminded')", null, false)
|
|
||||||
->where('pt.renewal_last_reminder_offset', null)
|
|
||||||
->groupEnd()
|
|
||||||
->groupEnd();
|
|
||||||
} else {
|
|
||||||
$query->whereIn($column, $targetDates)
|
|
||||||
->where($offsetSkip, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$query->where("IFNULL(pt.renewal_status, '') NOT IN ('renewal_mail_send', 'renewed', 'reminded')", null, false);
|
|
||||||
|
|
||||||
if ($includeOverdue) {
|
|
||||||
$query->groupStart()
|
|
||||||
->whereIn($column, $targetDates)
|
|
||||||
->orWhere("{$column} <", $today)
|
|
||||||
->groupEnd();
|
|
||||||
} else {
|
|
||||||
$query->whereIn($column, $targetDates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server-side DataTables list for Retail Policy (individual clients only).
|
* Server-side DataTables list for Retail Policy (individual clients only).
|
||||||
*
|
*
|
||||||
|
|||||||
@ -261,14 +261,12 @@
|
|||||||
<label class="custom-control-label" for="cfg_includeClientEmail">Include Client Email</label>
|
<label class="custom-control-label" for="cfg_includeClientEmail">Include Client Email</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!--
|
|
||||||
<div class="form-group col-md-3 col-sm-6">
|
<div class="form-group col-md-3 col-sm-6">
|
||||||
<div class="custom-control custom-switch">
|
<div class="custom-control custom-switch">
|
||||||
<input type="checkbox" class="custom-control-input" id="cfg_includeOverdue" name="includeOverdue" value="1">
|
<input type="checkbox" class="custom-control-input" id="cfg_includeOverdue" name="includeOverdue" value="1">
|
||||||
<label class="custom-control-label" for="cfg_includeOverdue">Include Overdue</label>
|
<label class="custom-control-label" for="cfg_includeOverdue">Include Overdue</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
@ -285,14 +283,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-md-8">
|
<div class="form-group col-md-3">
|
||||||
<label for="cfg_daysBefore">Days Before</label>
|
<label for="cfg_daysBefore">Days Before</label>
|
||||||
<input type="text" class="form-control" id="cfg_daysBefore" name="daysBefore" value="30" placeholder="30, 15, 7, 1, -2, -5, -10">
|
<input type="number" min="0" class="form-control" id="cfg_daysBefore" name="daysBefore" value="30">
|
||||||
<small class="form-text text-muted">Comma-separated offsets. Positive = days before due, negative = days after due.</small>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="form-group col-md-5">
|
||||||
<div class="form-row">
|
|
||||||
<div class="form-group col-md-12">
|
|
||||||
<label for="cfg_days">Weekdays (used when daily is off)</label>
|
<label for="cfg_days">Weekdays (used when daily is off)</label>
|
||||||
<input type="text" class="form-control" id="cfg_days" name="days" placeholder="mon,tue,wed,thu,fri">
|
<input type="text" class="form-control" id="cfg_days" name="days" placeholder="mon,tue,wed,thu,fri">
|
||||||
</div>
|
</div>
|
||||||
@ -483,7 +478,7 @@
|
|||||||
$('#cfg_daily').prop('checked', boolVal(data.daily));
|
$('#cfg_daily').prop('checked', boolVal(data.daily));
|
||||||
$('#cfg_includeClientEmail').prop('checked', boolVal(data.includeClientEmail));
|
$('#cfg_includeClientEmail').prop('checked', boolVal(data.includeClientEmail));
|
||||||
$('#cfg_includeOverdue').prop('checked', boolVal(data.includeOverdue));
|
$('#cfg_includeOverdue').prop('checked', boolVal(data.includeOverdue));
|
||||||
$('#cfg_daysBefore').val(listVal(data.daysBefore) || '30');
|
$('#cfg_daysBefore').val(data.daysBefore != null ? data.daysBefore : 30);
|
||||||
setRetailDateField(data.dateField || 'renewal_date');
|
setRetailDateField(data.dateField || 'renewal_date');
|
||||||
$('#cfg_days').val(listVal(data.days) || 'mon,tue,wed,thu,fri');
|
$('#cfg_days').val(listVal(data.days) || 'mon,tue,wed,thu,fri');
|
||||||
$('#cfg_toEmails').val(listVal(data.toEmails));
|
$('#cfg_toEmails').val(listVal(data.toEmails));
|
||||||
|
|||||||
@ -2,16 +2,10 @@
|
|||||||
-- Run manually against the application database.
|
-- Run manually against the application database.
|
||||||
|
|
||||||
-- 1) policy_transaction.renewal_status
|
-- 1) policy_transaction.renewal_status
|
||||||
-- Skip this ALTER if the column already exists.
|
|
||||||
ALTER TABLE `policy_transaction`
|
ALTER TABLE `policy_transaction`
|
||||||
ADD COLUMN `renewal_status` VARCHAR(50) NULL DEFAULT NULL
|
ADD COLUMN `renewal_status` VARCHAR(50) NULL DEFAULT NULL
|
||||||
AFTER `renewal_date`;
|
AFTER `renewal_date`;
|
||||||
|
|
||||||
-- Required for multi-offset reminders (30, 15, 7, ...). Skip if the column already exists.
|
|
||||||
ALTER TABLE `policy_transaction`
|
|
||||||
ADD COLUMN `renewal_last_reminder_offset` INT NULL DEFAULT NULL
|
|
||||||
AFTER `renewal_status`;
|
|
||||||
|
|
||||||
-- 2) notifications.config_json
|
-- 2) notifications.config_json
|
||||||
ALTER TABLE `notifications`
|
ALTER TABLE `notifications`
|
||||||
ADD COLUMN `config_json` LONGTEXT NULL
|
ADD COLUMN `config_json` LONGTEXT NULL
|
||||||
@ -32,7 +26,7 @@ SELECT
|
|||||||
'Policy Renewal Reminder',
|
'Policy Renewal Reminder',
|
||||||
'',
|
'',
|
||||||
1,
|
1,
|
||||||
'{"enabled":true,"daily":true,"days":"mon,tue,wed,thu,fri","clientTypes":[2],"dateField":"renewal_date","daysBefore":"30, 15, 7, 1, -2, -5, -10","includeClientEmail":true,"includeOverdue":false,"toEmails":"","ccEmails":"","bccEmails":"","fromMail":""}'
|
'{"enabled":true,"daily":true,"days":"mon,tue,wed,thu,fri","clientTypes":[2],"dateField":"renewal_date","daysBefore":30,"includeClientEmail":true,"includeOverdue":false,"toEmails":"","ccEmails":"","bccEmails":"","fromMail":""}'
|
||||||
FROM DUAL
|
FROM DUAL
|
||||||
WHERE NOT EXISTS (
|
WHERE NOT EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user