Commit 6140d228 authored by Administrator's avatar Administrator

Update 1 files via Son of Anton

parent 275ded0f
......@@ -8,63 +8,216 @@ use App\Core\App;
final class MemberNumberGenerator
{
/**
* Generate the next available membership number.
* Sequential, gap-free, atomic to prevent duplicates.
* Only called after payment confirmation.
* Assign the next membership number to a member.
* Called ONLY after membership value payment is confirmed.
* Number is based on payment priority — first to pay gets next number.
*/
public static function next(): string
public static function assign(int $memberId): ?string
{
$db = App::getInstance()->db();
// Use a transaction with locking to prevent concurrent duplicates
$db->beginTransaction();
try {
// Get the current maximum membership number
$row = $db->selectOne(
"SELECT MAX(CAST(membership_number AS UNSIGNED)) as max_num FROM members WHERE membership_number IS NOT NULL AND membership_number REGEXP '^[0-9]+$' FOR UPDATE"
// Check member exists and doesn't already have a number
$member = $db->selectOne(
"SELECT id, membership_number FROM members WHERE id = ? AND is_archived = 0",
[$memberId]
);
if (!$member) return null;
if (!empty($member['membership_number'])) return $member['membership_number'];
// Get next available membership number
$nextNumber = self::getNextMembershipNumber();
if ($nextNumber === null) return null;
$startNumber = 10012; // Default starting number (1001/2 per spec — interpreted as 10012)
$numberStr = (string) $nextNumber;
$db->update(
'members',
['membership_number' => $numberStr, 'updated_at' => date('Y-m-d H:i:s')],
'`id` = ?',
[$memberId]
);
// Check system config for starting number
$configRow = $db->selectOne("SELECT config_value FROM system_config WHERE config_key = 'membership_start_number'");
if ($configRow && $configRow['config_value']) {
$startNumber = (int) $configRow['config_value'];
return $numberStr;
}
$maxNum = $row['max_num'] ? (int) $row['max_num'] : ($startNumber - 1);
$nextNum = max($maxNum + 1, $startNumber);
/**
* Get next available membership number.
* Membership numbers are separate from form numbers.
* Format: sequential integers starting from system config.
*/
public static function getNextMembershipNumber(): ?int
{
$db = App::getInstance()->db();
$db->commit();
// Check existing membership numbers
$last = $db->selectOne(
"SELECT MAX(CAST(membership_number AS UNSIGNED)) as last_num
FROM members
WHERE membership_number IS NOT NULL
AND membership_number REGEXP '^[0-9]+$'"
);
$lastNum = (int) ($last['last_num'] ?? 0);
return (string) $nextNum;
} catch (\Throwable $e) {
$db->rollBack();
throw $e;
if ($lastNum > 0) {
return $lastNum + 1;
}
// Check system config for starting membership number
$config = $db->selectOne(
"SELECT config_value FROM system_config WHERE config_key = ?",
['membership.number_start']
);
if ($config && is_numeric($config['config_value']) && (int) $config['config_value'] > 0) {
return (int) $config['config_value'];
}
// Default starting number per spec: 1001/2 format = 1001
return 1001;
}
/**
* Assign a membership number to a member.
* Called only after payment is confirmed (Phase 11).
* Set the starting membership number (separate from form number).
*/
public static function assign(int $memberId): string
public static function setMembershipNumberStart(int $number): void
{
$db = App::getInstance()->db();
$existing = $db->selectOne(
"SELECT id FROM system_config WHERE config_key = ?",
['membership.number_start']
);
if ($existing) {
$db->update('system_config', [
'config_value' => (string) $number,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [(int) $existing['id']]);
} else {
$db->insert('system_config', [
'config_key' => 'membership.number_start',
'config_value' => (string) $number,
'config_type' => 'integer',
'group_name' => 'membership',
'description_ar' => 'رقم بداية العضويات (رقم العضوية وليس الاستمارة)',
'description_en' => 'Starting Membership Number',
'is_editable' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
}
// ── Form Number Methods (unchanged) ──
public static function next(): ?int
{
$db = App::getInstance()->db();
$last = $db->selectOne(
"SELECT MAX(CAST(form_number AS UNSIGNED)) as last_num
FROM members
WHERE form_number IS NOT NULL
AND form_number REGEXP '^[0-9]+$'"
);
$lastNum = (int) ($last['last_num'] ?? 0);
// Check member doesn't already have a number
$member = $db->selectOne("SELECT membership_number FROM members WHERE id = ?", [$memberId]);
if ($member && $member['membership_number']) {
return $member['membership_number'];
if ($lastNum > 0) {
return $lastNum + 1;
}
$number = self::next();
$config = $db->selectOne(
"SELECT config_value FROM system_config WHERE config_key = ?",
['membership.form_start_number']
);
if ($config && is_numeric($config['config_value']) && (int) $config['config_value'] > 0) {
return (int) $config['config_value'];
}
return null;
}
$db->update('members', [
'membership_number' => $number,
public static function setStartNumber(int $number): void
{
$db = App::getInstance()->db();
$existing = $db->selectOne(
"SELECT id FROM system_config WHERE config_key = ?",
['membership.form_start_number']
);
if ($existing) {
$db->update('system_config', [
'config_value' => (string) $number,
'updated_at' => date('Y-m-d H:i:s'),
], '`id` = ?', [$memberId]);
], '`id` = ?', [(int) $existing['id']]);
} else {
$db->insert('system_config', [
'config_key' => 'membership.form_start_number',
'config_value' => (string) $number,
'config_type' => 'integer',
'group_name' => 'membership',
'description_ar' => 'رقم بداية الاستمارات',
'description_en' => 'Starting Form Number',
'is_editable' => 1,
'created_at' => date('Y-m-d H:i:s'),
'updated_at' => date('Y-m-d H:i:s'),
]);
}
}
public static function isConfigured(): bool
{
return self::next() !== null;
}
public static function getFormFee(): string
{
try {
$db = App::getInstance()->db();
$fee = $db->selectOne(
"SELECT base_amount FROM service_catalog WHERE service_code = 'FORM_NEW_MEMBERSHIP' AND is_active = 1 LIMIT 1"
);
if ($fee && $fee['base_amount']) {
return $fee['base_amount'];
}
} catch (\Throwable $e) {}
return '505.00';
}
/**
* Get the membership value based on branch and qualification.
* Per spec: High qual = 150,000, Medium = 225,000, No qual = 300,000
*/
public static function getMembershipValue(int $branchId, ?int $qualificationId): string
{
if (!$qualificationId) return '300000.00'; // No qualification default
try {
$db = App::getInstance()->db();
$pricing = $db->selectOne(
"SELECT price FROM pricing_configs
WHERE branch_id = ? AND qualification_id = ? AND membership_type = 'working'
AND is_active = 1 AND effective_from <= CURDATE()
AND (effective_to IS NULL OR effective_to >= CURDATE())
ORDER BY effective_from DESC LIMIT 1",
[$branchId, $qualificationId]
);
if ($pricing) return $pricing['price'];
} catch (\Throwable $e) {}
// Fallback based on qualification code
try {
$db = App::getInstance()->db();
$qual = $db->selectOne("SELECT code FROM qualifications WHERE id = ?", [$qualificationId]);
if ($qual) {
return match ($qual['code'] ?? '') {
'high' => '150000.00',
'medium' => '225000.00',
'none' => '300000.00',
default => '150000.00',
};
}
} catch (\Throwable $e) {}
return $number;
return '150000.00';
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment