feat: implement deactivation of conflicting active contract templates during state updates
This commit is contained in:
parent
deb94c028b
commit
aa98d9ac37
@ -144,6 +144,49 @@ class DocumentTemplateRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deactivate other active contract templates that would conflict with the one being activated.
|
||||||
|
// Conflict dimensions:
|
||||||
|
// - same type='contract'
|
||||||
|
// - same contract_type ('contract' | 'gdpr')
|
||||||
|
// - same lang
|
||||||
|
// - overlapping user_type (personal/company/both)
|
||||||
|
async deactivateOtherActiveContracts({ excludeId, contract_type, lang, user_type }, conn) {
|
||||||
|
logger.info('DocumentTemplateRepository.deactivateOtherActiveContracts:start', { excludeId, contract_type, lang, user_type });
|
||||||
|
const safeContractType = (contract_type === 'gdpr' || contract_type === 'contract') ? contract_type : 'contract';
|
||||||
|
const safeLang = (lang === 'en' || lang === 'de') ? lang : 'en';
|
||||||
|
const safeUserType = (user_type === 'personal' || user_type === 'company' || user_type === 'both') ? user_type : 'both';
|
||||||
|
|
||||||
|
// user_type overlap rules
|
||||||
|
const overlap = safeUserType === 'both'
|
||||||
|
? ['personal', 'company', 'both']
|
||||||
|
: [safeUserType, 'both'];
|
||||||
|
|
||||||
|
const query = `
|
||||||
|
UPDATE document_templates
|
||||||
|
SET state = 'inactive', updatedAt = NOW()
|
||||||
|
WHERE id <> ?
|
||||||
|
AND type = 'contract'
|
||||||
|
AND contract_type = ?
|
||||||
|
AND lang = ?
|
||||||
|
AND state = 'active'
|
||||||
|
AND user_type IN (${overlap.map(() => '?').join(', ')})
|
||||||
|
`;
|
||||||
|
const params = [excludeId, safeContractType, safeLang, ...overlap];
|
||||||
|
try {
|
||||||
|
if (conn) {
|
||||||
|
const [res] = await conn.execute(query, params);
|
||||||
|
logger.info('DocumentTemplateRepository.deactivateOtherActiveContracts:success', { affected: res?.affectedRows });
|
||||||
|
return res?.affectedRows || 0;
|
||||||
|
}
|
||||||
|
const res = await db.execute(query, params);
|
||||||
|
logger.info('DocumentTemplateRepository.deactivateOtherActiveContracts:success', { affected: res?.affectedRows });
|
||||||
|
return res?.affectedRows || 0;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('DocumentTemplateRepository.deactivateOtherActiveContracts:error', { error: error.message });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async delete(id, conn) {
|
async delete(id, conn) {
|
||||||
logger.info('DocumentTemplateRepository.delete:start', { id });
|
logger.info('DocumentTemplateRepository.delete:start', { id });
|
||||||
const query = `DELETE FROM document_templates WHERE id = ?`;
|
const query = `DELETE FROM document_templates WHERE id = ?`;
|
||||||
|
|||||||
@ -116,7 +116,25 @@ class DocumentTemplateService {
|
|||||||
const uow = new UnitOfWork();
|
const uow = new UnitOfWork();
|
||||||
try {
|
try {
|
||||||
await uow.start();
|
await uow.start();
|
||||||
|
const current = await DocumentTemplateRepository.findById(id, uow.connection);
|
||||||
|
if (!current) {
|
||||||
|
logger.warn('DocumentTemplateService.updateTemplateState:not_found', { id });
|
||||||
|
await uow.rollback();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
await DocumentTemplateRepository.updateState(id, state, uow.connection);
|
await DocumentTemplateRepository.updateState(id, state, uow.connection);
|
||||||
|
|
||||||
|
// Enforce singleton active template per (contract_type + lang + overlapping user scope)
|
||||||
|
if (state === 'active' && current.type === 'contract') {
|
||||||
|
await DocumentTemplateRepository.deactivateOtherActiveContracts({
|
||||||
|
excludeId: id,
|
||||||
|
contract_type: current.contract_type || 'contract',
|
||||||
|
lang: current.lang,
|
||||||
|
user_type: current.user_type || 'both'
|
||||||
|
}, uow.connection);
|
||||||
|
}
|
||||||
|
|
||||||
const updated = await DocumentTemplateRepository.findById(id, uow.connection);
|
const updated = await DocumentTemplateRepository.findById(id, uow.connection);
|
||||||
await uow.commit();
|
await uow.commit();
|
||||||
logger.info('DocumentTemplateService.updateTemplateState:success', { id, state });
|
logger.info('DocumentTemplateService.updateTemplateState:success', { id, state });
|
||||||
@ -158,6 +176,16 @@ class DocumentTemplateService {
|
|||||||
uow.connection
|
uow.connection
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// If new template is active and is a contract template, deactivate other conflicting actives
|
||||||
|
if (nextState === 'active' && (data.type === 'contract' || previous.type === 'contract')) {
|
||||||
|
await DocumentTemplateRepository.deactivateOtherActiveContracts({
|
||||||
|
excludeId: created.id,
|
||||||
|
contract_type: (data.contract_type || previous.contract_type || 'contract'),
|
||||||
|
lang: (data.lang || previous.lang),
|
||||||
|
user_type: (data.user_type || previous.user_type || 'both')
|
||||||
|
}, uow.connection);
|
||||||
|
}
|
||||||
|
|
||||||
// Deactivate previous (requirement: deactive the previous one)
|
// Deactivate previous (requirement: deactive the previous one)
|
||||||
await DocumentTemplateRepository.updateState(previousId, 'inactive', uow.connection);
|
await DocumentTemplateRepository.updateState(previousId, 'inactive', uow.connection);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user