@waron97/prbot 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agrippa_typings/__builtins__.pyi +2 -0
- package/agrippa_typings/odoo_environment.pyi +3 -7
- package/agrippa_typings/odoo_records.pyi +22 -34
- package/package.json +1 -1
- package/src/commands/exportEmailTemplates.js +94 -12
- package/src/commands/exportImperex.js +5 -4
- package/src/commands/exportLrp.js +26 -57
- package/src/commands/exportPb.js +7 -6
- package/src/commands/exportWorkflow.js +101 -10
- package/src/commands/pr.js +7 -16
- package/src/commands/routine.js +99 -0
- package/src/commands/ver.js +6 -15
- package/src/index.js +27 -5
- package/src/lib/git.js +6 -2
- package/src/lib/logger.js +15 -0
- package/src/lib/premigrate.js +223 -0
|
@@ -134,9 +134,9 @@ from odoo_records import _UomCategory, _UomUom, _UpdateCadastreData, _UtmCampaig
|
|
|
134
134
|
from odoo_records import _UtmSource, _UtmStage, _UtmTag, _ValidateAccountMove, _VulnerabilityDashboard, _VulnerabilityDashboardLine
|
|
135
135
|
from odoo_records import _VulnerabilityHistory, _VulnerabilityNotifyAdmissibility, _VulnerabilityNotifyAdmissibilityLine, _VulnerabilityNotifyOutgoing, _WalletImportWizard, _WalletManagementLine
|
|
136
136
|
from odoo_records import _WatchlistAddWizard, _WatchlistDownloadWizard, _WatchlistRemoveWizard, _WebEditorAssets, _WebEditorConverterTest, _WebEditorConverterTestSub
|
|
137
|
-
from odoo_records import _WebTourTour, _WelcomeAttachmentTable,
|
|
138
|
-
from odoo_records import
|
|
139
|
-
from odoo_records import
|
|
137
|
+
from odoo_records import _WebTourTour, _WelcomeAttachmentTable, _WizardIrModelMenuCreate, _WizardUploadVulnerability, _X, _XCcq
|
|
138
|
+
from odoo_records import _XMktCom, _XPod, _XPrecheck, _XTopny, _XmlgeneratorDataAggregator, _XmlgeneratorDataField
|
|
139
|
+
from odoo_records import _XmlgeneratorDataModel
|
|
140
140
|
|
|
141
141
|
|
|
142
142
|
class OdooEnvironment:
|
|
@@ -1914,10 +1914,6 @@ class OdooEnvironment:
|
|
|
1914
1914
|
@overload
|
|
1915
1915
|
def __getitem__(self, model_name: Literal["welcome.attachment.table"]) -> _WelcomeAttachmentTable: ...
|
|
1916
1916
|
@overload
|
|
1917
|
-
def __getitem__(
|
|
1918
|
-
self, model_name: Literal["welcome.letter.attachment.mixin"]
|
|
1919
|
-
) -> _WelcomeLetterAttachmentMixin: ...
|
|
1920
|
-
@overload
|
|
1921
1917
|
def __getitem__(self, model_name: Literal["wizard.ir.model.menu.create"]) -> _WizardIrModelMenuCreate: ...
|
|
1922
1918
|
@overload
|
|
1923
1919
|
def __getitem__(self, model_name: Literal["wizard.upload.vulnerability"]) -> _WizardUploadVulnerability: ...
|
|
@@ -7253,6 +7253,7 @@ class _CorrectionInvoice(Recordset):
|
|
|
7253
7253
|
def _compute_code_supply_point(self): ...
|
|
7254
7254
|
def _compute_currency(self): ...
|
|
7255
7255
|
def _get_symphonie_url(self): ...
|
|
7256
|
+
def download_invoice_data(self): ...
|
|
7256
7257
|
def download_invoice_pdf(self): ...
|
|
7257
7258
|
|
|
7258
7259
|
# --- create.case ---
|
|
@@ -9613,7 +9614,7 @@ class _HelpdeskTicket(Recordset):
|
|
|
9613
9614
|
disconnect_date: Optional[_dt.date]
|
|
9614
9615
|
distributor_charge: float
|
|
9615
9616
|
distributor_code: str
|
|
9616
|
-
distributor_id:
|
|
9617
|
+
distributor_id: "_ResPartner"
|
|
9617
9618
|
distributor_name: str
|
|
9618
9619
|
distributor_notes: str
|
|
9619
9620
|
distributor_outcome_reception_date: Optional[_dt.date]
|
|
@@ -9790,6 +9791,7 @@ class _HelpdeskTicket(Recordset):
|
|
|
9790
9791
|
meter_reading_message: str
|
|
9791
9792
|
meter_reading_result: str
|
|
9792
9793
|
meter_readings: str
|
|
9794
|
+
meter_relocation_note: str
|
|
9793
9795
|
migrated: bool
|
|
9794
9796
|
mkt_comm_atti_autorizzativi: str
|
|
9795
9797
|
mkt_comm_causal_code: str
|
|
@@ -10262,6 +10264,7 @@ class _HelpdeskTicket(Recordset):
|
|
|
10262
10264
|
def ir_cron_registry_change_customer_privacy(self, triplet_code, days_delta, privacy_flag): ...
|
|
10263
10265
|
def ir_cron_vulnerable_clients_missing_documentation(self): ...
|
|
10264
10266
|
def kv_store(self) -> "_SymplePbProcessData": ...
|
|
10267
|
+
def last_staging(self, process_name) -> "_SymplePbProcessData": ...
|
|
10265
10268
|
def launch_cancel_case_wizard(self): ...
|
|
10266
10269
|
def launch_connect_case_wizard(self): ...
|
|
10267
10270
|
def launch_connect_parent_wizard(self): ...
|
|
@@ -12161,9 +12164,9 @@ class _IrConfigParameter(Recordset):
|
|
|
12161
12164
|
def group_by_contract(self, service_point_array): ...
|
|
12162
12165
|
def imperex_get_values(self): ...
|
|
12163
12166
|
def patch_asset(self, asset_id, payload, additional_headers=False): ...
|
|
12164
|
-
def prepare_m2c_passthrough_payload(self, case_id, triplet, is_client=True, is_get_client_extended=False, is_validity_date=False, is_split_payment=False, is_contratti_mandato=False, service_points=False, service_points_billing_profile_array=False, is_associazioneContrattoFornitura=False, is_domiciliazione=False, is_ccr=False, is_dpay=False, contatti_array=False, extra_contract_info=False, extra_forniture_info=False, causale_attivazione=False, contrattiDettaglio_array=False, forn_contatti_array=False, contrattiMandatoDettaglio_array=False, is_cCodiceDestinatario=False, is_cCodiceUfficioPA=False, is_cigCup=False, is_contratti=False, is_prodotti=False, causale_cessazione=False, fine_contratto=False, fine_contratto_fornitura=False, is_oneri=False, charge=False, charge_type=False, charge_description=False, cma_array=False, is_codice_offerta=False, garanzia=False, is_contratti_mandato_dInizioValidita=False, protocollo=False, is_rcu=False, oneri_case_type=False, mask=None, is_storno=False, data_storno=False, storno_case_type=False): ...
|
|
12165
|
-
def prepare_m2c_passthrough_payload__nightly(self, case_id, triplet, is_client=True, is_get_client_extended=False, is_validity_date=False, is_split_payment=False, is_contratti_mandato=False, service_points=False, service_points_billing_profile_array=False, is_associazioneContrattoFornitura=False, is_domiciliazione=False, is_ccr=False, is_dpay=False, contatti_array=False, extra_contract_info=False, extra_forniture_info=False, causale_attivazione=False, contrattiDettaglio_array=False, forn_contatti_array=False, contrattiMandatoDettaglio_array=False, is_cCodiceDestinatario=False, is_cCodiceUfficioPA=False, is_cigCup=False, is_contratti=False, is_prodotti=False, causale_cessazione=False, fine_contratto=False, fine_contratto_fornitura=False, is_oneri=False, charge=False, charge_type=False, charge_description=False, cma_array=False, is_codice_offerta=False, garanzia=False, is_contratti_mandato_dInizioValidita=False, protocollo=False, is_rcu=False, oneri_case_type=False, mask=None, is_storno=False, data_storno=False, storno_case_type=False): ...
|
|
12166
|
-
def prepare_m2c_payload(self, case_id, triplet, is_client=True, is_get_client_extended=False, is_validity_date=False, is_split_payment=False, is_contratti_mandato=False, service_points=False, service_points_billing_profile_array=False, is_associazioneContrattoFornitura=False, is_domiciliazione=False, is_ccr=False, is_dpay=False, contatti_array=False, extra_contract_info=False, extra_forniture_info=False, causale_attivazione=False, contrattiDettaglio_array=False, forn_contatti_array=False, contrattiMandatoDettaglio_array=False, is_cCodiceDestinatario=False, is_cCodiceUfficioPA=False, is_cigCup=False, is_contratti=False, is_prodotti=False, causale_cessazione=False, is_oneri=False, charge=False, charge_type=False, charge_description=False, fine_contratto=False, fine_contratto_fornitura=False, cma_array=False, is_codice_offerta=False, garanzia=False, is_contratti_mandato_dInizioValidita=False, protocollo=False, is_rcu=False, oneri_case_type=False, isContrattiOLDAsset=False, is_storno=False, storno_case_type=False, data_storno=False, mask=None): ...
|
|
12167
|
+
def prepare_m2c_passthrough_payload(self, case_id, triplet, is_client=True, is_get_client_extended=False, is_validity_date=False, is_split_payment=False, is_contratti_mandato=False, service_points=False, service_points_billing_profile_array=False, is_associazioneContrattoFornitura=False, is_domiciliazione=False, is_ccr=False, is_dpay=False, contatti_array=False, extra_contract_info=False, extra_forniture_info=False, causale_attivazione=False, contrattiDettaglio_array=False, forn_contatti_array=False, contrattiMandatoDettaglio_array=False, is_cCodiceDestinatario=False, is_cCodiceUfficioPA=False, is_cigCup=False, is_contratti=False, is_prodotti=False, causale_cessazione=False, fine_contratto=False, fine_contratto_fornitura=False, is_oneri=False, charge=False, charge_type=False, charge_description=False, cma_array=False, is_codice_offerta=False, garanzia=False, is_contratti_mandato_dInizioValidita=False, protocollo=False, is_rcu=False, oneri_case_type=False, mask=None, is_storno=False, data_storno=False, storno_case_type=False, service_cell_index=False): ...
|
|
12168
|
+
def prepare_m2c_passthrough_payload__nightly(self, case_id, triplet, is_client=True, is_get_client_extended=False, is_validity_date=False, is_split_payment=False, is_contratti_mandato=False, service_points=False, service_points_billing_profile_array=False, is_associazioneContrattoFornitura=False, is_domiciliazione=False, is_ccr=False, is_dpay=False, contatti_array=False, extra_contract_info=False, extra_forniture_info=False, causale_attivazione=False, contrattiDettaglio_array=False, forn_contatti_array=False, contrattiMandatoDettaglio_array=False, is_cCodiceDestinatario=False, is_cCodiceUfficioPA=False, is_cigCup=False, is_contratti=False, is_prodotti=False, causale_cessazione=False, fine_contratto=False, fine_contratto_fornitura=False, is_oneri=False, charge=False, charge_type=False, charge_description=False, cma_array=False, is_codice_offerta=False, garanzia=False, is_contratti_mandato_dInizioValidita=False, protocollo=False, is_rcu=False, oneri_case_type=False, mask=None, is_storno=False, data_storno=False, storno_case_type=False, service_cell_index=False): ...
|
|
12169
|
+
def prepare_m2c_payload(self, case_id, triplet, is_client=True, is_get_client_extended=False, is_validity_date=False, is_split_payment=False, is_contratti_mandato=False, service_points=False, service_points_billing_profile_array=False, is_associazioneContrattoFornitura=False, is_domiciliazione=False, is_ccr=False, is_dpay=False, contatti_array=False, extra_contract_info=False, extra_forniture_info=False, causale_attivazione=False, contrattiDettaglio_array=False, forn_contatti_array=False, contrattiMandatoDettaglio_array=False, is_cCodiceDestinatario=False, is_cCodiceUfficioPA=False, is_cigCup=False, is_contratti=False, is_prodotti=False, causale_cessazione=False, is_oneri=False, charge=False, charge_type=False, charge_description=False, fine_contratto=False, fine_contratto_fornitura=False, cma_array=False, is_codice_offerta=False, garanzia=False, is_contratti_mandato_dInizioValidita=False, protocollo=False, is_rcu=False, oneri_case_type=False, isContrattiOLDAsset=False, is_storno=False, storno_case_type=False, data_storno=False, mask=None, cella=False): ...
|
|
12167
12170
|
def process_attributes(self, family, supply_start_date, fonteprezzo): ...
|
|
12168
12171
|
def process_child_products(self, components, supply_start_date): ...
|
|
12169
12172
|
def process_family_attributes(self, families, supply_start_date, fonteprezzo): ...
|
|
@@ -14565,7 +14568,7 @@ class _M2cActivationDeactivationPayload(Recordset):
|
|
|
14565
14568
|
def _M2CActivationDeactivationPayload__smart_patch(self, payload, key, value, on_missing='error'): ...
|
|
14566
14569
|
def _check_valid_json(self): ...
|
|
14567
14570
|
def get_payload_with_patch(self, patches=None, on_missing='error'): ...
|
|
14568
|
-
def last(self, cod_pod, payload_type=None): ...
|
|
14571
|
+
def last(self, cod_pod, payload_type=None) -> "_M2cActivationDeactivationPayload": ...
|
|
14569
14572
|
|
|
14570
14573
|
# --- mail.activity ---
|
|
14571
14574
|
|
|
@@ -16924,7 +16927,7 @@ class _MarketCommEventLog(Recordset):
|
|
|
16924
16927
|
def exists(self) -> "_MarketCommEventLog": ...
|
|
16925
16928
|
def sudo(self) -> "_MarketCommEventLog": ...
|
|
16926
16929
|
def with_context(self, ctx: Dict[str, Any] = ..., **kwargs: Any) -> "_MarketCommEventLog": ...
|
|
16927
|
-
def last(self, flow_code=None, service_code=None): ...
|
|
16930
|
+
def last(self, flow_code=None, service_code=None) -> "_MarketCommEventLog": ...
|
|
16928
16931
|
|
|
16929
16932
|
# --- market.comm.event.log.cp.utente.filter ---
|
|
16930
16933
|
|
|
@@ -22540,7 +22543,7 @@ class _ResPartner(Recordset):
|
|
|
22540
22543
|
def _avatar_get_placeholder_path(self): ...
|
|
22541
22544
|
def _build_vat_error_message(self, country_code, wrong_vat, record_label): ...
|
|
22542
22545
|
def _build_vcard(self): ...
|
|
22543
|
-
def _calculate_service_cell(self): ...
|
|
22546
|
+
def _calculate_service_cell(self, trigger_type='all'): ...
|
|
22544
22547
|
def _cf_check_birth_data(self, fiscal_code, gender, birth_date): ...
|
|
22545
22548
|
def _cf_check_city(self, fiscal_code, birth_city): ...
|
|
22546
22549
|
def _cf_check_name(self, fiscal_code, surname, firstname): ...
|
|
@@ -22652,7 +22655,10 @@ class _ResPartner(Recordset):
|
|
|
22652
22655
|
def _display_address(self, without_company=False): ...
|
|
22653
22656
|
def _display_address_depends(self): ...
|
|
22654
22657
|
def _email_send(self, email_from, subject, body, on_error=None): ...
|
|
22655
|
-
def
|
|
22658
|
+
def _eval_cell_monthly_cron(self, target_cell, old_cell, thirteen_months): ...
|
|
22659
|
+
def _eval_cell_realtime(self, target_cell, old_cell): ...
|
|
22660
|
+
def _eval_cell_weekly_cron(self, target_cell, old_cell, sixty_days): ...
|
|
22661
|
+
def _evaluate_service_cell_rules(self, target_cell, old_cell, sixty_days, thirteen_months, trigger_type='all'): ...
|
|
22656
22662
|
def _fields_sync(self, values): ...
|
|
22657
22663
|
def _fields_view_get_address(self, arch): ...
|
|
22658
22664
|
def _find_accounting_partner(self, partner): ...
|
|
@@ -22719,6 +22725,7 @@ class _ResPartner(Recordset):
|
|
|
22719
22725
|
def _run_vat_test(self, vat_number, default_country, partner_is_company=True): ...
|
|
22720
22726
|
def _send_to_m2c_passthrough(self): ...
|
|
22721
22727
|
def _set_calendar_last_notif_ack(self): ...
|
|
22728
|
+
def _should_clear_mm(self, old_cell): ...
|
|
22722
22729
|
def _signup_retrieve_partner(self, token, check_validity=False, raise_exception=False): ...
|
|
22723
22730
|
def _sms_get_default_partners(self): ...
|
|
22724
22731
|
def _split_vat(self, vat): ...
|
|
@@ -22804,6 +22811,7 @@ class _ResPartner(Recordset):
|
|
|
22804
22811
|
def get_iva_rates(self): ...
|
|
22805
22812
|
def get_mention_suggestions(self, search, limit=8, channel_id=None): ...
|
|
22806
22813
|
def get_most_recent_termination_date(self): ...
|
|
22814
|
+
def get_order_sending_date(self, apply_filter=True): ...
|
|
22807
22815
|
def get_pod_ids_from_service_point_ids(self): ...
|
|
22808
22816
|
def get_selection_name_value(self, _lang_='it_IT'): ...
|
|
22809
22817
|
def get_update_client_payload(self, new_status): ...
|
|
@@ -30217,6 +30225,7 @@ class _SympleInteraction(Recordset):
|
|
|
30217
30225
|
def exists(self) -> "_SympleInteraction": ...
|
|
30218
30226
|
def sudo(self) -> "_SympleInteraction": ...
|
|
30219
30227
|
def with_context(self, ctx: Dict[str, Any] = ..., **kwargs: Any) -> "_SympleInteraction": ...
|
|
30228
|
+
def _check_complaint_start_date(self): ...
|
|
30220
30229
|
def _check_multiple_contacts(self): ...
|
|
30221
30230
|
def _check_tickets_before_close_interaction(self): ...
|
|
30222
30231
|
def _checks_before_close_interaction(self): ...
|
|
@@ -30605,6 +30614,7 @@ class _SymplePbLauncher(Recordset):
|
|
|
30605
30614
|
def _get_elements_eval_context(self, data, model, process, record, user): ...
|
|
30606
30615
|
def get_event_html(self, res_model_name, res_id, process_key, instance_key): ...
|
|
30607
30616
|
def get_html(self, model_name, record_id, data, user_id): ...
|
|
30617
|
+
def get_process_elements(self, process_name, model_name, record_id, data, user_id): ...
|
|
30608
30618
|
def initialize_process(self, process_key, payload): ...
|
|
30609
30619
|
def which(self, model_name): ...
|
|
30610
30620
|
|
|
@@ -31311,7 +31321,8 @@ class _SympleRcuLine(Recordset):
|
|
|
31311
31321
|
def _inverse_cod_pdr(self): ...
|
|
31312
31322
|
def _inverse_cod_pod(self): ...
|
|
31313
31323
|
def action_change_state(self): ...
|
|
31314
|
-
def
|
|
31324
|
+
def action_check_activation_cases(self): ...
|
|
31325
|
+
def action_check_deactivation_cases(self): ...
|
|
31315
31326
|
def action_create_case_when_no_deactivation_cases(self): ...
|
|
31316
31327
|
def action_refresh_activation(self): ...
|
|
31317
31328
|
def action_refresh_deactivation(self): ...
|
|
@@ -33806,12 +33817,14 @@ class _WelcomeAttachmentTable(Recordset):
|
|
|
33806
33817
|
client_type: str
|
|
33807
33818
|
commodity: str
|
|
33808
33819
|
dms_attachment_code: str
|
|
33820
|
+
end_date: Optional[_dt.date]
|
|
33809
33821
|
has_bit2publish_template: bool
|
|
33810
33822
|
is_placet: bool
|
|
33811
33823
|
is_unreachable: bool
|
|
33812
33824
|
request_type: str
|
|
33813
33825
|
sending_via: str
|
|
33814
33826
|
show_bit2publish_button: bool
|
|
33827
|
+
start_date: Optional[_dt.date]
|
|
33815
33828
|
table_type: str
|
|
33816
33829
|
tension: str
|
|
33817
33830
|
def browse(self, ids: Union[int, List[int]]) -> "_WelcomeAttachmentTable": ...
|
|
@@ -33829,31 +33842,6 @@ class _WelcomeAttachmentTable(Recordset):
|
|
|
33829
33842
|
def sudo(self) -> "_WelcomeAttachmentTable": ...
|
|
33830
33843
|
def with_context(self, ctx: Dict[str, Any] = ..., **kwargs: Any) -> "_WelcomeAttachmentTable": ...
|
|
33831
33844
|
|
|
33832
|
-
# --- welcome.letter.attachment.mixin ---
|
|
33833
|
-
|
|
33834
|
-
class _WelcomeLetterAttachmentMixin(Recordset):
|
|
33835
|
-
bit2publish_template_ids: "_Bit2publishTemplate"
|
|
33836
|
-
has_bit2publish_template: bool
|
|
33837
|
-
show_bit2publish_button: bool
|
|
33838
|
-
def browse(self, ids: Union[int, List[int]]) -> "_WelcomeLetterAttachmentMixin": ...
|
|
33839
|
-
def search(
|
|
33840
|
-
self,
|
|
33841
|
-
domain: List[Any],
|
|
33842
|
-
limit: Optional[int] = None,
|
|
33843
|
-
order: Optional[str] = None,
|
|
33844
|
-
offset: int = 0,
|
|
33845
|
-
) -> "_WelcomeLetterAttachmentMixin": ...
|
|
33846
|
-
def create(self, vals: Dict[str, Any]) -> "_WelcomeLetterAttachmentMixin": ...
|
|
33847
|
-
def filtered(self, func: Any) -> "_WelcomeLetterAttachmentMixin": ...
|
|
33848
|
-
def sorted(self, key: Any = None, reverse: bool = False) -> "_WelcomeLetterAttachmentMixin": ...
|
|
33849
|
-
def exists(self) -> "_WelcomeLetterAttachmentMixin": ...
|
|
33850
|
-
def sudo(self) -> "_WelcomeLetterAttachmentMixin": ...
|
|
33851
|
-
def with_context(self, ctx: Dict[str, Any] = ..., **kwargs: Any) -> "_WelcomeLetterAttachmentMixin": ...
|
|
33852
|
-
def add_welcome_letter_attachment(self, attachment_codes): ...
|
|
33853
|
-
def get_attachment_id_from_dms(self, path, attachment_name, b2w_url, b2w_token): ...
|
|
33854
|
-
def get_welcome_letter_attachment_codes(self, event_id): ...
|
|
33855
|
-
def welcome_letter_attachments(self, event_id): ...
|
|
33856
|
-
|
|
33857
33845
|
# --- wizard.ir.model.menu.create ---
|
|
33858
33846
|
|
|
33859
33847
|
class _WizardIrModelMenuCreate(Recordset):
|
package/package.json
CHANGED
|
@@ -2,10 +2,19 @@ import fs from 'fs/promises';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fetch from 'node-fetch';
|
|
4
4
|
import search from '@inquirer/search';
|
|
5
|
+
import { select, confirm } from '@inquirer/prompts';
|
|
5
6
|
import { getToken } from '../lib/auth.js';
|
|
6
7
|
import { execGit } from '../lib/git.js';
|
|
7
8
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
8
9
|
import { fuzzyMatch } from '../lib/fuzzy.js';
|
|
10
|
+
import { log, isSilent } from '../lib/logger.js';
|
|
11
|
+
import { verbot } from './ver.js';
|
|
12
|
+
import {
|
|
13
|
+
readEmailTemplateMappings,
|
|
14
|
+
detectEmailRenames,
|
|
15
|
+
computeMigrationVersion,
|
|
16
|
+
generateEmailPreMigrateScript,
|
|
17
|
+
} from '../lib/premigrate.js';
|
|
9
18
|
|
|
10
19
|
async function getWorkflows(token) {
|
|
11
20
|
const url = `${process.env.RIP_URL}/symple.workflow/*`;
|
|
@@ -89,6 +98,21 @@ async function getModuleChoices() {
|
|
|
89
98
|
.map((e) => ({ name: e.name, value: e.name }));
|
|
90
99
|
}
|
|
91
100
|
|
|
101
|
+
async function resolveManifestPath(module, ADDONS_PATH) {
|
|
102
|
+
for (const candidate of [
|
|
103
|
+
path.join(ADDONS_PATH, module, '__manifest__.py'),
|
|
104
|
+
path.join(ADDONS_PATH, 'config', module, '__manifest__.py'),
|
|
105
|
+
]) {
|
|
106
|
+
try {
|
|
107
|
+
await fs.access(candidate);
|
|
108
|
+
return candidate;
|
|
109
|
+
} catch {
|
|
110
|
+
// try next
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
|
|
92
116
|
async function exportEmailTemplates(opts) {
|
|
93
117
|
const token = await getToken();
|
|
94
118
|
|
|
@@ -104,7 +128,7 @@ async function exportEmailTemplates(opts) {
|
|
|
104
128
|
},
|
|
105
129
|
});
|
|
106
130
|
|
|
107
|
-
|
|
131
|
+
log('Fetching workflows...');
|
|
108
132
|
const workflows = await getWorkflows(token);
|
|
109
133
|
const choices = workflows.map((w) => ({ name: w.name, value: w.id }));
|
|
110
134
|
|
|
@@ -121,7 +145,7 @@ async function exportEmailTemplates(opts) {
|
|
|
121
145
|
},
|
|
122
146
|
});
|
|
123
147
|
|
|
124
|
-
|
|
148
|
+
log(`Fetching email templates for workflow ${workflowId}...`);
|
|
125
149
|
const excludes = opts.exclude ?? [];
|
|
126
150
|
const templates = (await getEmailTemplates(workflowId, token))
|
|
127
151
|
.filter((t) => t.template_code)
|
|
@@ -130,7 +154,7 @@ async function exportEmailTemplates(opts) {
|
|
|
130
154
|
});
|
|
131
155
|
|
|
132
156
|
if (!templates.length) {
|
|
133
|
-
|
|
157
|
+
log('No email templates found for this workflow.');
|
|
134
158
|
return;
|
|
135
159
|
}
|
|
136
160
|
|
|
@@ -138,17 +162,75 @@ async function exportEmailTemplates(opts) {
|
|
|
138
162
|
const dataDir = path.join(ADDONS_PATH, 'config', module, 'data');
|
|
139
163
|
await fs.mkdir(dataDir, { recursive: true });
|
|
140
164
|
|
|
165
|
+
const oldMappings = await readEmailTemplateMappings(dataDir);
|
|
166
|
+
|
|
141
167
|
const outPath = path.join(dataDir, 'mail_template.xml');
|
|
142
168
|
await fs.writeFile(outPath, generateXml(templates), 'utf-8');
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
169
|
+
log(`Written: ${outPath}`);
|
|
170
|
+
|
|
171
|
+
const newMappings = await readEmailTemplateMappings(dataDir);
|
|
172
|
+
const renamedCodes = detectEmailRenames(oldMappings, newMappings);
|
|
173
|
+
|
|
174
|
+
let bumpLevel = opts.bump;
|
|
175
|
+
if (!bumpLevel) {
|
|
176
|
+
bumpLevel = await select({
|
|
177
|
+
message: 'Bump version?',
|
|
178
|
+
choices: [
|
|
179
|
+
{ name: 'No bump', value: 'none' },
|
|
180
|
+
{ name: 'Patch', value: 'patch' },
|
|
181
|
+
{ name: 'Minor', value: 'minor' },
|
|
182
|
+
{ name: 'Major', value: 'major' },
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let preMigratePath = null;
|
|
188
|
+
|
|
189
|
+
if (renamedCodes.length > 0) {
|
|
190
|
+
log(`Renamed template_codes (${renamedCodes.length}): ${renamedCodes.join(', ')}`);
|
|
191
|
+
|
|
192
|
+
let shouldGenerate = opts.autoPremigrate;
|
|
193
|
+
if (!shouldGenerate && !isSilent()) {
|
|
194
|
+
shouldGenerate = await confirm({
|
|
195
|
+
message: `Detected ${renamedCodes.length} renamed template_code(s). Generate pre-migrate script?`,
|
|
196
|
+
default: true,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (shouldGenerate) {
|
|
201
|
+
const manifestPath = await resolveManifestPath(module, ADDONS_PATH);
|
|
202
|
+
if (!manifestPath) {
|
|
203
|
+
log(`Warning: __manifest__.py not found for ${module}, skipping pre-migrate generation`);
|
|
204
|
+
} else {
|
|
205
|
+
const version = await computeMigrationVersion(manifestPath, bumpLevel);
|
|
206
|
+
const migrationDir = path.join(ADDONS_PATH, 'config', module, 'migrations', version);
|
|
207
|
+
preMigratePath = path.join(migrationDir, 'pre-migrate.py');
|
|
208
|
+
await fs.mkdir(migrationDir, { recursive: true });
|
|
209
|
+
await fs.writeFile(preMigratePath, generateEmailPreMigrateScript(renamedCodes));
|
|
210
|
+
log(`Wrote pre-migrate: ${preMigratePath}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (opts.commit === false) {
|
|
216
|
+
if (bumpLevel && bumpLevel !== 'none') {
|
|
217
|
+
await verbot(module, bumpLevel, { ...opts, commit: false });
|
|
218
|
+
}
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const filesToAdd = [outPath];
|
|
223
|
+
if (preMigratePath) filesToAdd.push(preMigratePath);
|
|
224
|
+
|
|
225
|
+
for (const filePath of filesToAdd) {
|
|
226
|
+
await execGit(['add', filePath], ADDONS_PATH);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
await execGit(['commit', '-m', `[IMP][${module}] Export email templates`], ADDONS_PATH);
|
|
230
|
+
log('Committed.');
|
|
231
|
+
|
|
232
|
+
if (bumpLevel && bumpLevel !== 'none') {
|
|
233
|
+
await verbot(module, bumpLevel, opts);
|
|
152
234
|
}
|
|
153
235
|
}
|
|
154
236
|
|
|
@@ -6,6 +6,7 @@ import { getToken } from '../lib/auth.js';
|
|
|
6
6
|
import { execGit } from '../lib/git.js';
|
|
7
7
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
8
8
|
import { fuzzyMatch } from '../lib/fuzzy.js';
|
|
9
|
+
import { log } from '../lib/logger.js';
|
|
9
10
|
|
|
10
11
|
const IMPEREX_REL = 'sorgenia_imperex_metadata/migrations/0.0.0/imperex';
|
|
11
12
|
|
|
@@ -56,7 +57,7 @@ async function exportImperex(opts) {
|
|
|
56
57
|
},
|
|
57
58
|
});
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
log(`Fetching records for ${model}...`);
|
|
60
61
|
const records = await listRecords(model, token);
|
|
61
62
|
const recChoices = records.map((r) => ({ name: String(r.name ?? r.id), value: r.id }));
|
|
62
63
|
const recordId = await search({
|
|
@@ -67,7 +68,7 @@ async function exportImperex(opts) {
|
|
|
67
68
|
},
|
|
68
69
|
});
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
log(`Exporting record ${recordId}...`);
|
|
71
72
|
const { attachments } = await exportRecord(model, recordId, token);
|
|
72
73
|
|
|
73
74
|
const modelDir = path.join(ADDONS_PATH, IMPEREX_REL, model);
|
|
@@ -78,7 +79,7 @@ async function exportImperex(opts) {
|
|
|
78
79
|
if (att.name === '__manifest__.yaml') continue;
|
|
79
80
|
const destPath = path.join(modelDir, path.basename(att.name));
|
|
80
81
|
await fs.writeFile(destPath, att.content, 'utf-8');
|
|
81
|
-
|
|
82
|
+
log(`Written: ${destPath}`);
|
|
82
83
|
saved.push(destPath);
|
|
83
84
|
}
|
|
84
85
|
|
|
@@ -90,7 +91,7 @@ async function exportImperex(opts) {
|
|
|
90
91
|
['commit', '-m', `[IMP][sorgenia_imperex_metadata] update ${model} record`],
|
|
91
92
|
ADDONS_PATH,
|
|
92
93
|
);
|
|
93
|
-
|
|
94
|
+
log('Committed.');
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
97
|
|
|
@@ -5,10 +5,7 @@ import search from '@inquirer/search';
|
|
|
5
5
|
import { getToken } from '../lib/auth.js';
|
|
6
6
|
import { execGit } from '../lib/git.js';
|
|
7
7
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
8
|
-
import {
|
|
9
|
-
import { CONFIG_DIR } from '../config.js';
|
|
10
|
-
|
|
11
|
-
const CACHE_FILE = path.join(CONFIG_DIR, 'lrp_processes_cache.json');
|
|
8
|
+
import { log } from '../lib/logger.js';
|
|
12
9
|
|
|
13
10
|
function getSymphonyBase() {
|
|
14
11
|
const url = process.env.IMPORTEXPORT_URL;
|
|
@@ -17,13 +14,16 @@ function getSymphonyBase() {
|
|
|
17
14
|
return `${parsed.protocol}//${parsed.host}`;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
|
-
async function
|
|
17
|
+
async function fetchProcesses(token, nameFilter, signal) {
|
|
21
18
|
const base = getSymphonyBase();
|
|
22
|
-
const
|
|
19
|
+
const size = nameFilter ? 20 : 12;
|
|
20
|
+
const params = encodeURIComponent(
|
|
21
|
+
JSON.stringify({ page: 1, size, sorters: [], filters: [] })
|
|
22
|
+
);
|
|
23
23
|
const otherfilters = encodeURIComponent(
|
|
24
24
|
JSON.stringify([
|
|
25
25
|
{ field: 'id', type: '=', value: null },
|
|
26
|
-
{ field: 'name', type: 'like', value: null },
|
|
26
|
+
{ field: 'name', type: 'like', value: nameFilter ?? null },
|
|
27
27
|
{ field: 'tenantId', type: '=', value: null },
|
|
28
28
|
{ field: 'latestVersion', type: '=', value: true },
|
|
29
29
|
])
|
|
@@ -53,24 +53,6 @@ async function fetchProcessList(token, signal) {
|
|
|
53
53
|
return items;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
async function loadCache() {
|
|
57
|
-
try {
|
|
58
|
-
const data = await fs.readFile(CACHE_FILE, 'utf-8');
|
|
59
|
-
return JSON.parse(data);
|
|
60
|
-
} catch {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async function saveCache(items) {
|
|
66
|
-
try {
|
|
67
|
-
await fs.mkdir(path.dirname(CACHE_FILE), { recursive: true });
|
|
68
|
-
await fs.writeFile(CACHE_FILE, JSON.stringify(items));
|
|
69
|
-
} catch {
|
|
70
|
-
// non-fatal
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
56
|
async function fetchProcessDetail(id, token) {
|
|
75
57
|
const base = getSymphonyBase();
|
|
76
58
|
const url = `${base}/symphony/restInfo/ajax/tabulator/id/${id}?connector=SymphBpmnFileTabCon&modelroot=/management/development/edit`;
|
|
@@ -118,41 +100,30 @@ async function findExistingFile(baseDir, filename) {
|
|
|
118
100
|
async function exportLrp(opts) {
|
|
119
101
|
const token = await getToken();
|
|
120
102
|
|
|
121
|
-
|
|
122
|
-
|
|
103
|
+
log('Fetching processes...');
|
|
104
|
+
const initialItems = await fetchProcesses(token, null);
|
|
105
|
+
const initialChoices = initialItems.map((p) => ({ name: p.name, value: p.id }));
|
|
123
106
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const bgController = new AbortController();
|
|
127
|
-
|
|
128
|
-
if (cached) {
|
|
129
|
-
choices = cached.map((p) => ({ name: p.name, value: p.id }));
|
|
130
|
-
console.log(`Loaded ${choices.length} processes from cache. Fetching fresh list in background (~1 min) — results update as you type.`);
|
|
131
|
-
fetchProcessList(token, bgController.signal)
|
|
132
|
-
.then(async (fresh) => {
|
|
133
|
-
await saveCache(fresh);
|
|
134
|
-
const updated = fresh.map((p) => ({ name: p.name, value: p.id }));
|
|
135
|
-
choices.length = 0;
|
|
136
|
-
choices.push(...updated);
|
|
137
|
-
})
|
|
138
|
-
.catch(() => {});
|
|
139
|
-
} else {
|
|
140
|
-
console.log('No cache found. Fetching process list (~1 min)...');
|
|
141
|
-
const items = await fetchProcessList(token);
|
|
142
|
-
await saveCache(items);
|
|
143
|
-
choices = items.map((p) => ({ name: p.name, value: p.id }));
|
|
144
|
-
console.log(`Loaded ${choices.length} processes.`);
|
|
145
|
-
}
|
|
107
|
+
let searchController = null;
|
|
146
108
|
|
|
147
109
|
const selectedId = await search({
|
|
148
110
|
message: 'Select LRP process to export:',
|
|
149
111
|
source: async (input) => {
|
|
150
|
-
if (!input) return
|
|
151
|
-
|
|
112
|
+
if (!input) return initialChoices;
|
|
113
|
+
|
|
114
|
+
if (searchController) searchController.abort();
|
|
115
|
+
searchController = new AbortController();
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const items = await fetchProcesses(token, input, searchController.signal);
|
|
119
|
+
return items.map((p) => ({ name: p.name, value: p.id }));
|
|
120
|
+
} catch {
|
|
121
|
+
return initialChoices;
|
|
122
|
+
}
|
|
152
123
|
},
|
|
153
124
|
});
|
|
154
125
|
|
|
155
|
-
|
|
126
|
+
log('Fetching process detail...');
|
|
156
127
|
const jsText = await fetchProcessDetail(selectedId, token);
|
|
157
128
|
const { xml, filename } = extractBpmnData(jsText);
|
|
158
129
|
const bpmnFilename = `${filename.replace(/^B2WA_/, '')}.bpmn20.xml`;
|
|
@@ -165,21 +136,19 @@ async function exportLrp(opts) {
|
|
|
165
136
|
if (existing) {
|
|
166
137
|
savePath = existing;
|
|
167
138
|
await fs.writeFile(savePath, xml, 'utf-8');
|
|
168
|
-
|
|
139
|
+
log(`Updated: ${savePath}`);
|
|
169
140
|
} else {
|
|
170
141
|
savePath = path.join(processesDir, 'all', bpmnFilename);
|
|
171
142
|
await fs.mkdir(path.dirname(savePath), { recursive: true });
|
|
172
143
|
await fs.writeFile(savePath, xml, 'utf-8');
|
|
173
|
-
|
|
144
|
+
log(`Created: ${savePath}`);
|
|
174
145
|
}
|
|
175
146
|
|
|
176
147
|
if (opts.commit !== false) {
|
|
177
148
|
await execGit(['add', savePath], ADDONS_PATH);
|
|
178
149
|
await execGit(['commit', '-m', '[IMP][.cloudbuild] Update long running process'], ADDONS_PATH);
|
|
179
|
-
|
|
150
|
+
log('Committed.');
|
|
180
151
|
}
|
|
181
|
-
|
|
182
|
-
bgController.abort();
|
|
183
152
|
}
|
|
184
153
|
|
|
185
154
|
export { exportLrp };
|
package/src/commands/exportPb.js
CHANGED
|
@@ -6,6 +6,7 @@ import { getToken } from '../lib/auth.js';
|
|
|
6
6
|
import { execGit } from '../lib/git.js';
|
|
7
7
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
8
8
|
import { fuzzyMatch } from '../lib/fuzzy.js';
|
|
9
|
+
import { log } from '../lib/logger.js';
|
|
9
10
|
|
|
10
11
|
async function getProcessList(token) {
|
|
11
12
|
const url = `${process.env.IMPORTEXPORT_URL}/object/process_builder?addLanguageParam=true`;
|
|
@@ -120,14 +121,14 @@ async function exportPb(opts) {
|
|
|
120
121
|
const { guid, document_id } = selected;
|
|
121
122
|
const filename = `${document_id}.zip`;
|
|
122
123
|
|
|
123
|
-
|
|
124
|
+
log(`Initiating export for ${document_id}...`);
|
|
124
125
|
const requestTime = Date.now();
|
|
125
126
|
await initiateExport(guid, token);
|
|
126
127
|
|
|
127
|
-
|
|
128
|
+
log('Waiting for export to complete...');
|
|
128
129
|
const requestId = await pollExportResult(guid, requestTime, token);
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
log(`Downloading ${filename}...`);
|
|
131
132
|
const zipBuffer = await downloadZip(requestId, token);
|
|
132
133
|
|
|
133
134
|
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
@@ -138,18 +139,18 @@ async function exportPb(opts) {
|
|
|
138
139
|
if (existing) {
|
|
139
140
|
savePath = existing;
|
|
140
141
|
await fs.writeFile(savePath, zipBuffer);
|
|
141
|
-
|
|
142
|
+
log(`Updated existing file at ${savePath}`);
|
|
142
143
|
} else {
|
|
143
144
|
savePath = path.join(processesDir, 'all', filename);
|
|
144
145
|
await fs.mkdir(path.dirname(savePath), { recursive: true });
|
|
145
146
|
await fs.writeFile(savePath, zipBuffer);
|
|
146
|
-
|
|
147
|
+
log(`Created new file at ${savePath}`);
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
if (opts.commit !== false) {
|
|
150
151
|
await execGit(['add', savePath], ADDONS_PATH);
|
|
151
152
|
await execGit(['commit', '-m', '[IMP][.cloudbuild] Update wizard'], ADDONS_PATH);
|
|
152
|
-
|
|
153
|
+
log('Committed.');
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import search from '@inquirer/search';
|
|
4
|
-
import { select } from '@inquirer/prompts';
|
|
4
|
+
import { select, confirm } from '@inquirer/prompts';
|
|
5
5
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
6
6
|
import { fuzzyMatch } from '../lib/fuzzy.js';
|
|
7
|
+
import { execGit } from '../lib/git.js';
|
|
8
|
+
import { log, isSilent } from '../lib/logger.js';
|
|
7
9
|
import { runPr } from './pr.js';
|
|
8
10
|
import { verbot } from './ver.js';
|
|
11
|
+
import {
|
|
12
|
+
readWorkflowMappings,
|
|
13
|
+
detectRenames,
|
|
14
|
+
computeMigrationVersion,
|
|
15
|
+
generatePreMigrateScript,
|
|
16
|
+
} from '../lib/premigrate.js';
|
|
9
17
|
|
|
10
18
|
async function getModuleChoices() {
|
|
11
19
|
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
@@ -16,17 +24,46 @@ async function getModuleChoices() {
|
|
|
16
24
|
.map((e) => ({ name: e.name, value: e.name }));
|
|
17
25
|
}
|
|
18
26
|
|
|
27
|
+
async function resolveManifestPath(module, ADDONS_PATH) {
|
|
28
|
+
for (const candidate of [
|
|
29
|
+
path.join(ADDONS_PATH, module, '__manifest__.py'),
|
|
30
|
+
path.join(ADDONS_PATH, 'config', module, '__manifest__.py'),
|
|
31
|
+
]) {
|
|
32
|
+
try {
|
|
33
|
+
await fs.access(candidate);
|
|
34
|
+
return candidate;
|
|
35
|
+
} catch {
|
|
36
|
+
// try next
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
19
42
|
async function exportWorkflow(opts) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
43
|
+
let module;
|
|
44
|
+
if (opts.module) {
|
|
45
|
+
module = opts.module;
|
|
46
|
+
} else {
|
|
47
|
+
const moduleChoices = await getModuleChoices();
|
|
48
|
+
module = await search({
|
|
49
|
+
message: 'Select module:',
|
|
50
|
+
source: async (input) => {
|
|
51
|
+
if (!input) return moduleChoices;
|
|
52
|
+
return moduleChoices.filter((c) => fuzzyMatch(c.name, input));
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
}
|
|
28
56
|
|
|
29
|
-
|
|
57
|
+
const ADDONS_PATH = resolveAddonsPath(process.env.ADDONS_PATH);
|
|
58
|
+
const dataDir = path.join(ADDONS_PATH, 'config', module, 'data');
|
|
59
|
+
|
|
60
|
+
const oldMappings = await readWorkflowMappings(dataDir);
|
|
61
|
+
|
|
62
|
+
await runPr(module, { ...opts, commit: false });
|
|
63
|
+
|
|
64
|
+
const newMappings = await readWorkflowMappings(dataDir);
|
|
65
|
+
const renames = detectRenames(oldMappings, newMappings);
|
|
66
|
+
const hasRenames = renames.stateCodes.length > 0 || renames.phaseCodes.length > 0;
|
|
30
67
|
|
|
31
68
|
let bumpLevel = opts.bump;
|
|
32
69
|
if (!bumpLevel) {
|
|
@@ -41,6 +78,60 @@ async function exportWorkflow(opts) {
|
|
|
41
78
|
});
|
|
42
79
|
}
|
|
43
80
|
|
|
81
|
+
let preMigratePath = null;
|
|
82
|
+
|
|
83
|
+
if (hasRenames) {
|
|
84
|
+
if (renames.stateCodes.length > 0) {
|
|
85
|
+
log(`Renamed state_codes (${renames.stateCodes.length}): ${renames.stateCodes.join(', ')}`);
|
|
86
|
+
}
|
|
87
|
+
if (renames.phaseCodes.length > 0) {
|
|
88
|
+
log(`Renamed phase_codes (${renames.phaseCodes.length}): ${renames.phaseCodes.join(', ')}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let shouldGenerate = opts.autoPremigrate;
|
|
92
|
+
if (!shouldGenerate && !isSilent()) {
|
|
93
|
+
shouldGenerate = await confirm({
|
|
94
|
+
message: `Detected ${renames.stateCodes.length} renamed state_code(s) and ${renames.phaseCodes.length} renamed phase_code(s). Generate pre-migrate script?`,
|
|
95
|
+
default: true,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (shouldGenerate) {
|
|
100
|
+
const manifestPath = await resolveManifestPath(module, ADDONS_PATH);
|
|
101
|
+
if (!manifestPath) {
|
|
102
|
+
log(`Warning: __manifest__.py not found for ${module}, skipping pre-migrate generation`);
|
|
103
|
+
} else {
|
|
104
|
+
const version = await computeMigrationVersion(manifestPath, bumpLevel);
|
|
105
|
+
const migrationDir = path.join(ADDONS_PATH, 'config', module, 'migrations', version);
|
|
106
|
+
preMigratePath = path.join(migrationDir, 'pre-migrate.py');
|
|
107
|
+
await fs.mkdir(migrationDir, { recursive: true });
|
|
108
|
+
await fs.writeFile(preMigratePath, generatePreMigrateScript(renames.stateCodes, renames.phaseCodes));
|
|
109
|
+
log(`Wrote pre-migrate: ${preMigratePath}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (opts.commit === false) {
|
|
115
|
+
if (bumpLevel && bumpLevel !== 'none') {
|
|
116
|
+
await verbot(module, bumpLevel, { ...opts, commit: false });
|
|
117
|
+
}
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const filesToAdd = [
|
|
122
|
+
path.join(dataDir, 'workflow_missing_relations.xml'),
|
|
123
|
+
path.join(dataDir, 'workflow_configuration.xml'),
|
|
124
|
+
];
|
|
125
|
+
if (preMigratePath) filesToAdd.push(preMigratePath);
|
|
126
|
+
|
|
127
|
+
for (const filePath of filesToAdd) {
|
|
128
|
+
await execGit(['add', filePath], ADDONS_PATH);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const commitMessage = `[IMP][${module}] Update workflow`;
|
|
132
|
+
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
133
|
+
log(`Committed with message: ${commitMessage}`);
|
|
134
|
+
|
|
44
135
|
if (bumpLevel && bumpLevel !== 'none') {
|
|
45
136
|
await verbot(module, bumpLevel, opts);
|
|
46
137
|
}
|
package/src/commands/pr.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { execFile } from 'child_process';
|
|
2
1
|
import fs from 'fs/promises';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import fetch from 'node-fetch';
|
|
5
4
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
6
5
|
import { getToken } from '../lib/auth.js';
|
|
6
|
+
import { execGit } from '../lib/git.js';
|
|
7
|
+
import { log } from '../lib/logger.js';
|
|
7
8
|
|
|
8
9
|
async function getFiles(module_name, token) {
|
|
9
10
|
const url = `${process.env.RIP_URL}/ir.model/xml_prbot`;
|
|
@@ -66,7 +67,7 @@ async function runPr(module_name, opts = {}) {
|
|
|
66
67
|
|
|
67
68
|
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
|
68
69
|
await fs.writeFile(destPath, content);
|
|
69
|
-
|
|
70
|
+
log(`Processed: ${file.name} -> ${destPath}`);
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
if (opts.commit === false) return;
|
|
@@ -78,23 +79,13 @@ async function runPr(module_name, opts = {}) {
|
|
|
78
79
|
];
|
|
79
80
|
|
|
80
81
|
for (const filePath of filesToAdd) {
|
|
81
|
-
await
|
|
82
|
-
execFile('git', ['add', filePath], { cwd: ADDONS_PATH }, (error) => {
|
|
83
|
-
if (error) reject(error);
|
|
84
|
-
else resolve();
|
|
85
|
-
});
|
|
86
|
-
});
|
|
82
|
+
await execGit(['add', filePath], ADDONS_PATH);
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
const commitMessage = `[IMP][${module_name}] Update workflow`;
|
|
90
|
-
await
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
else resolve();
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
console.log(`Committed with message: ${commitMessage}`);
|
|
86
|
+
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
87
|
+
|
|
88
|
+
log(`Committed with message: ${commitMessage}`);
|
|
98
89
|
}
|
|
99
90
|
|
|
100
91
|
async function main(module_name) {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { parse } from 'yaml';
|
|
4
|
+
import { select } from '@inquirer/prompts';
|
|
5
|
+
import { CONFIG_DIR } from '../config.js';
|
|
6
|
+
import { setSilent } from '../lib/logger.js';
|
|
7
|
+
import { exportWorkflow } from './exportWorkflow.js';
|
|
8
|
+
import { exportPb } from './exportPb.js';
|
|
9
|
+
import { exportImperex } from './exportImperex.js';
|
|
10
|
+
import { exportEmailTemplates } from './exportEmailTemplates.js';
|
|
11
|
+
import { exportLrp } from './exportLrp.js';
|
|
12
|
+
|
|
13
|
+
const ROUTINES_FILE = path.join(CONFIG_DIR, 'routines.yaml');
|
|
14
|
+
const WORKSPACE_FILE = 'agrippa.yaml';
|
|
15
|
+
|
|
16
|
+
const COMMAND_MAP = {
|
|
17
|
+
'export workflow': exportWorkflow,
|
|
18
|
+
'export pb': exportPb,
|
|
19
|
+
'export imperex': exportImperex,
|
|
20
|
+
'export lrp': exportLrp,
|
|
21
|
+
'export email-templates': exportEmailTemplates,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function loadRoutines() {
|
|
25
|
+
const routines = [];
|
|
26
|
+
|
|
27
|
+
if (existsSync(ROUTINES_FILE)) {
|
|
28
|
+
const config = parse(readFileSync(ROUTINES_FILE, 'utf-8'));
|
|
29
|
+
if (config?.routines) routines.push(...config.routines);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (existsSync(WORKSPACE_FILE)) {
|
|
33
|
+
const config = parse(readFileSync(WORKSPACE_FILE, 'utf-8'));
|
|
34
|
+
if (config?.routines) routines.push(...config.routines);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return routines;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function stepOpts(step) {
|
|
41
|
+
const opts = { bump: step.bump ?? 'none' };
|
|
42
|
+
if (step.module) opts.module = step.module;
|
|
43
|
+
if (step.workflow) opts.workflow = step.workflow;
|
|
44
|
+
if (step.exclude) opts.exclude = step.exclude;
|
|
45
|
+
if (step.no_commit) opts.commit = false;
|
|
46
|
+
if (step.auto_premigrate) opts.autoPremigrate = true;
|
|
47
|
+
return opts;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isNothingToCommit(err) {
|
|
51
|
+
return err?.gitOutput?.includes('nothing to commit');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function runRoutine(routine) {
|
|
55
|
+
console.log(`Running Routine: ${routine.name}`);
|
|
56
|
+
|
|
57
|
+
for (const step of routine.steps) {
|
|
58
|
+
const label = `[${step.name}]`;
|
|
59
|
+
const fn = COMMAND_MAP[step.command];
|
|
60
|
+
if (!fn) {
|
|
61
|
+
console.log(`${label} Unknown command: ${step.command}`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(`${label} Job started`);
|
|
66
|
+
setSilent(true);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
await fn(stepOpts(step));
|
|
70
|
+
console.log(`${label} Done (committed)`);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
if (isNothingToCommit(err)) {
|
|
73
|
+
console.log(`${label} Done (nothing to commit)`);
|
|
74
|
+
} else {
|
|
75
|
+
console.log(`${label} Failed: ${err.message}`);
|
|
76
|
+
}
|
|
77
|
+
} finally {
|
|
78
|
+
setSilent(false);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function routine() {
|
|
84
|
+
const routines = loadRoutines();
|
|
85
|
+
|
|
86
|
+
if (!routines.length) {
|
|
87
|
+
console.log('No routines defined. Create ~/.config/prbot/routines.yaml or add routines: to agrippa.yaml.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const selected = await select({
|
|
92
|
+
message: 'Select routine:',
|
|
93
|
+
choices: routines.map((r) => ({ name: r.name, value: r })),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
await runRoutine(selected);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { routine };
|
package/src/commands/ver.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { execFile } from 'child_process';
|
|
2
1
|
import fs from 'fs/promises';
|
|
3
2
|
import path from 'path';
|
|
4
3
|
import { resolveAddonsPath } from '../lib/addons.js';
|
|
4
|
+
import { execGit } from '../lib/git.js';
|
|
5
|
+
import { log } from '../lib/logger.js';
|
|
5
6
|
|
|
6
7
|
async function verbot(module_name, level, opts = {}) {
|
|
7
8
|
if (!['major', 'minor', 'patch'].includes(level)) {
|
|
@@ -50,26 +51,16 @@ async function verbot(module_name, level, opts = {}) {
|
|
|
50
51
|
);
|
|
51
52
|
|
|
52
53
|
await fs.writeFile(manifestPath, newContent);
|
|
53
|
-
|
|
54
|
+
log(`Updated version: ${currentVersion} -> ${newVersion}`);
|
|
54
55
|
|
|
55
56
|
if (opts.commit === false) return;
|
|
56
57
|
|
|
57
|
-
await
|
|
58
|
-
execFile('git', ['add', manifestPath], { cwd: ADDONS_PATH }, (error) => {
|
|
59
|
-
if (error) reject(error);
|
|
60
|
-
else resolve();
|
|
61
|
-
});
|
|
62
|
-
});
|
|
58
|
+
await execGit(['add', manifestPath], ADDONS_PATH);
|
|
63
59
|
|
|
64
60
|
const commitMessage = `[VER][${module_name}] Bump`;
|
|
65
|
-
await
|
|
66
|
-
execFile('git', ['commit', '-m', commitMessage], { cwd: ADDONS_PATH }, (error) => {
|
|
67
|
-
if (error) reject(error);
|
|
68
|
-
else resolve();
|
|
69
|
-
});
|
|
70
|
-
});
|
|
61
|
+
await execGit(['commit', '-m', commitMessage], ADDONS_PATH);
|
|
71
62
|
|
|
72
|
-
|
|
63
|
+
log(`Committed with message: ${commitMessage}`);
|
|
73
64
|
}
|
|
74
65
|
|
|
75
66
|
export { verbot };
|
package/src/index.js
CHANGED
|
@@ -6,11 +6,13 @@ import { autopr } from './commands/autopr.js';
|
|
|
6
6
|
import { changelog } from './commands/changelog.js';
|
|
7
7
|
import { commit } from './commands/commit.js';
|
|
8
8
|
import { exportPb, exportRip, exportImperex, exportEmailTemplates, exportWorkflow, exportLrp } from './commands/export.js';
|
|
9
|
+
import { routine } from './commands/routine.js';
|
|
9
10
|
import { init } from './commands/init.js';
|
|
10
11
|
import { main as prMain } from './commands/pr.js';
|
|
11
12
|
import { verbot } from './commands/ver.js';
|
|
12
13
|
import { CONFIG_FILE } from './config.js';
|
|
13
14
|
import { checkForUpdate, currentVersion } from './lib/updateCheck.js';
|
|
15
|
+
import { setSilent } from './lib/logger.js';
|
|
14
16
|
|
|
15
17
|
configDotenv({ path: CONFIG_FILE, quiet: true });
|
|
16
18
|
|
|
@@ -93,9 +95,13 @@ exportCmd
|
|
|
93
95
|
.command('workflow')
|
|
94
96
|
.option('--no-commit')
|
|
95
97
|
.option('-b, --bump <level>', 'Version bump level (patch, minor, major)')
|
|
98
|
+
.option('-m, --module <id>', 'Module/workflow ID to export (skips interactive selection)')
|
|
99
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
100
|
+
.option('--auto-premigrate', 'Auto-generate pre-migrate script when XML ID renames are detected (no prompt)')
|
|
96
101
|
.action((opts) => {
|
|
102
|
+
if (opts.silent) setSilent(true);
|
|
97
103
|
exportWorkflow(opts).catch((err) => {
|
|
98
|
-
throw err;
|
|
104
|
+
if (!opts.silent) throw err;
|
|
99
105
|
});
|
|
100
106
|
});
|
|
101
107
|
|
|
@@ -104,42 +110,58 @@ exportCmd.command('rip').action(() => exportRip());
|
|
|
104
110
|
exportCmd
|
|
105
111
|
.command('pb')
|
|
106
112
|
.option('--no-commit')
|
|
113
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
107
114
|
.action((opts) => {
|
|
115
|
+
if (opts.silent) setSilent(true);
|
|
108
116
|
exportPb(opts).catch((err) => {
|
|
109
|
-
throw err;
|
|
117
|
+
if (!opts.silent) throw err;
|
|
110
118
|
});
|
|
111
119
|
});
|
|
112
120
|
|
|
113
121
|
exportCmd
|
|
114
122
|
.command('imperex')
|
|
115
123
|
.option('--no-commit')
|
|
124
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
116
125
|
.action((opts) => {
|
|
126
|
+
if (opts.silent) setSilent(true);
|
|
117
127
|
exportImperex(opts).catch((err) => {
|
|
118
|
-
throw err;
|
|
128
|
+
if (!opts.silent) throw err;
|
|
119
129
|
});
|
|
120
130
|
});
|
|
121
131
|
|
|
122
132
|
exportCmd
|
|
123
133
|
.command('lrp')
|
|
124
134
|
.option('--no-commit')
|
|
135
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
125
136
|
.action((opts) => {
|
|
137
|
+
if (opts.silent) setSilent(true);
|
|
126
138
|
exportLrp(opts).catch((err) => {
|
|
127
|
-
throw err;
|
|
139
|
+
if (!opts.silent) throw err;
|
|
128
140
|
});
|
|
129
141
|
});
|
|
130
142
|
|
|
131
143
|
exportCmd
|
|
132
144
|
.command('email-templates')
|
|
133
145
|
.option('--no-commit')
|
|
146
|
+
.option('-b, --bump <level>', 'Version bump level (patch, minor, major)')
|
|
134
147
|
.option('-e, --exclude <value...>', 'exclude templates matching id, name, or template_code')
|
|
135
148
|
.option('-m, --module <name>', 'module directory name (skip prompt)')
|
|
136
149
|
.option('-w, --workflow <value>', 'workflow name or id (skip prompt)')
|
|
150
|
+
.option('-s, --silent', 'Suppress all output and swallow errors')
|
|
151
|
+
.option('--auto-premigrate', 'Auto-generate pre-migrate script when XML ID renames are detected (no prompt)')
|
|
137
152
|
.action((opts) => {
|
|
153
|
+
if (opts.silent) setSilent(true);
|
|
138
154
|
exportEmailTemplates(opts).catch((err) => {
|
|
139
|
-
throw err;
|
|
155
|
+
if (!opts.silent) throw err;
|
|
140
156
|
});
|
|
141
157
|
});
|
|
142
158
|
|
|
159
|
+
program.command('routine').action(() => {
|
|
160
|
+
routine().catch((err) => {
|
|
161
|
+
throw err;
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
143
165
|
program.command('update').action(() => {
|
|
144
166
|
console.log('Updating prbot...');
|
|
145
167
|
execFile('npm', ['i', '-g', '@waron97/prbot'], (error, stdout, stderr) => {
|
package/src/lib/git.js
CHANGED
|
@@ -3,8 +3,12 @@ import { execFile } from 'child_process';
|
|
|
3
3
|
function execGit(args, cwd) {
|
|
4
4
|
return new Promise((resolve, reject) => {
|
|
5
5
|
execFile('git', args, { cwd }, (error, stdout) => {
|
|
6
|
-
if (error)
|
|
7
|
-
|
|
6
|
+
if (error) {
|
|
7
|
+
error.gitOutput = stdout;
|
|
8
|
+
reject(error);
|
|
9
|
+
} else {
|
|
10
|
+
resolve(stdout);
|
|
11
|
+
}
|
|
8
12
|
});
|
|
9
13
|
});
|
|
10
14
|
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
function parseXmlMappings(content) {
|
|
5
|
+
const phases = new Map();
|
|
6
|
+
const results = new Map();
|
|
7
|
+
|
|
8
|
+
const chunks = content.split('<record ');
|
|
9
|
+
for (const chunk of chunks) {
|
|
10
|
+
const idMatch = chunk.match(/^id="([^"]+)"/);
|
|
11
|
+
if (!idMatch) continue;
|
|
12
|
+
const xmlId = idMatch[1];
|
|
13
|
+
|
|
14
|
+
const modelMatch = chunk.match(/model="(symple\.triplet\.phase(?:\.result)?)"/);
|
|
15
|
+
if (!modelMatch) continue;
|
|
16
|
+
const model = modelMatch[1];
|
|
17
|
+
|
|
18
|
+
if (model === 'symple.triplet.phase') {
|
|
19
|
+
const codeMatch = chunk.match(/<field name="phase_code">([^<]+)<\/field>/);
|
|
20
|
+
if (codeMatch) phases.set(codeMatch[1].trim(), xmlId);
|
|
21
|
+
} else if (model === 'symple.triplet.phase.result') {
|
|
22
|
+
const codeMatch = chunk.match(/<field name="state_code">([^<]+)<\/field>/);
|
|
23
|
+
if (codeMatch) results.set(codeMatch[1].trim(), xmlId);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { phases, results };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function readWorkflowMappings(dataDir) {
|
|
31
|
+
const phases = new Map();
|
|
32
|
+
const results = new Map();
|
|
33
|
+
|
|
34
|
+
const files = ['workflow_configuration.xml', 'workflow_missing_relations.xml'];
|
|
35
|
+
for (const filename of files) {
|
|
36
|
+
try {
|
|
37
|
+
const content = await fs.readFile(path.join(dataDir, filename), 'utf-8');
|
|
38
|
+
const { phases: p, results: r } = parseXmlMappings(content);
|
|
39
|
+
for (const [k, v] of p) phases.set(k, v);
|
|
40
|
+
for (const [k, v] of r) results.set(k, v);
|
|
41
|
+
} catch {
|
|
42
|
+
// file doesn't exist yet; skip
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { phases, results };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function detectRenames(oldMaps, newMaps) {
|
|
50
|
+
const stateCodes = [];
|
|
51
|
+
const phaseCodes = [];
|
|
52
|
+
|
|
53
|
+
for (const [code, oldXmlId] of oldMaps.results) {
|
|
54
|
+
const newXmlId = newMaps.results.get(code);
|
|
55
|
+
if (newXmlId && newXmlId !== oldXmlId) stateCodes.push(code);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
for (const [code, oldXmlId] of oldMaps.phases) {
|
|
59
|
+
const newXmlId = newMaps.phases.get(code);
|
|
60
|
+
if (newXmlId && newXmlId !== oldXmlId) phaseCodes.push(code);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return { stateCodes, phaseCodes };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function computeMigrationVersion(manifestPath, bumpLevel) {
|
|
67
|
+
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
68
|
+
const versionMatch = content.match(/"version":\s*"(15\.0\.\d+\.\d+\.\d+)"/);
|
|
69
|
+
if (!versionMatch) throw new Error('Version not found in manifest');
|
|
70
|
+
|
|
71
|
+
const parts = versionMatch[1].split('.');
|
|
72
|
+
const base = `${parts[0]}.${parts[1]}`;
|
|
73
|
+
const major = parseInt(parts[2]);
|
|
74
|
+
const minor = parseInt(parts[3]);
|
|
75
|
+
const patch = parseInt(parts[4]);
|
|
76
|
+
|
|
77
|
+
if (bumpLevel === 'major') return `${base}.${major + 1}.0.0`;
|
|
78
|
+
if (bumpLevel === 'minor') return `${base}.${major}.${minor + 1}.0`;
|
|
79
|
+
if (bumpLevel === 'patch') return `${base}.${major}.${minor}.${patch + 1}`;
|
|
80
|
+
return `${base}.${major}.${minor + 1}.0`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function generatePreMigrateScript(stateCodes, phaseCodes) {
|
|
84
|
+
const lines = [
|
|
85
|
+
'# Copyright 2025-TODAY Symphonie Prime S.r.l. (www.symphonieprime.com)',
|
|
86
|
+
'# All rights reserved.',
|
|
87
|
+
'',
|
|
88
|
+
'from openupgradelib import openupgrade',
|
|
89
|
+
'',
|
|
90
|
+
'',
|
|
91
|
+
'@openupgrade.migrate()',
|
|
92
|
+
'def migrate(env, version):',
|
|
93
|
+
' env.cr.execute(',
|
|
94
|
+
' "SELECT 1 FROM information_schema.tables WHERE table_name = %s",',
|
|
95
|
+
' ("symple_triplet_phase_result",),',
|
|
96
|
+
' )',
|
|
97
|
+
' if not env.cr.fetchone():',
|
|
98
|
+
' return',
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
if (stateCodes.length > 0) {
|
|
102
|
+
lines.push('');
|
|
103
|
+
if (stateCodes.length === 1) {
|
|
104
|
+
lines.push(` state_codes = ("${stateCodes[0]}",)`);
|
|
105
|
+
} else {
|
|
106
|
+
lines.push(' state_codes = (');
|
|
107
|
+
for (const code of stateCodes) lines.push(` "${code}",`);
|
|
108
|
+
lines.push(' )');
|
|
109
|
+
}
|
|
110
|
+
lines.push(
|
|
111
|
+
' env.cr.execute(',
|
|
112
|
+
' "DELETE FROM result_code_configurator WHERE triplet_phase_result_id IN (SELECT id FROM symple_triplet_phase_result WHERE state_code IN %s)",',
|
|
113
|
+
' (state_codes,),',
|
|
114
|
+
' )',
|
|
115
|
+
' env.cr.execute(',
|
|
116
|
+
' "DELETE FROM symple_triplet_phase_result WHERE state_code IN %s",',
|
|
117
|
+
' (state_codes,),',
|
|
118
|
+
' )',
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (phaseCodes.length > 0) {
|
|
123
|
+
lines.push('');
|
|
124
|
+
if (phaseCodes.length === 1) {
|
|
125
|
+
lines.push(` phase_codes = ("${phaseCodes[0]}",)`);
|
|
126
|
+
} else {
|
|
127
|
+
lines.push(' phase_codes = (');
|
|
128
|
+
for (const code of phaseCodes) lines.push(` "${code}",`);
|
|
129
|
+
lines.push(' )');
|
|
130
|
+
}
|
|
131
|
+
lines.push(
|
|
132
|
+
' env.cr.execute(',
|
|
133
|
+
' "DELETE FROM symple_triplet_phase WHERE phase_code IN %s",',
|
|
134
|
+
' (phase_codes,),',
|
|
135
|
+
' )',
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return lines.join('\n') + '\n';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function parseEmailTemplateMappings(content) {
|
|
143
|
+
const templates = new Map();
|
|
144
|
+
|
|
145
|
+
const chunks = content.split('<record ');
|
|
146
|
+
for (const chunk of chunks) {
|
|
147
|
+
const idMatch = chunk.match(/^id="([^"]+)"/);
|
|
148
|
+
if (!idMatch) continue;
|
|
149
|
+
const xmlId = idMatch[1];
|
|
150
|
+
|
|
151
|
+
if (!chunk.match(/model="mail\.template"/)) continue;
|
|
152
|
+
|
|
153
|
+
const codeMatch = chunk.match(/<field name="template_code">([^<]+)<\/field>/);
|
|
154
|
+
if (codeMatch) templates.set(codeMatch[1].trim(), xmlId);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return templates;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function readEmailTemplateMappings(dataDir) {
|
|
161
|
+
try {
|
|
162
|
+
const content = await fs.readFile(path.join(dataDir, 'mail_template.xml'), 'utf-8');
|
|
163
|
+
return parseEmailTemplateMappings(content);
|
|
164
|
+
} catch {
|
|
165
|
+
return new Map();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function detectEmailRenames(oldMap, newMap) {
|
|
170
|
+
const templateCodes = [];
|
|
171
|
+
for (const [code, oldXmlId] of oldMap) {
|
|
172
|
+
const newXmlId = newMap.get(code);
|
|
173
|
+
if (newXmlId && newXmlId !== oldXmlId) templateCodes.push(code);
|
|
174
|
+
}
|
|
175
|
+
return templateCodes;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function generateEmailPreMigrateScript(templateCodes) {
|
|
179
|
+
const lines = [
|
|
180
|
+
'# Copyright 2025-TODAY Symphonie Prime S.r.l. (www.symphonieprime.com)',
|
|
181
|
+
'# All rights reserved.',
|
|
182
|
+
'',
|
|
183
|
+
'from openupgradelib import openupgrade',
|
|
184
|
+
'',
|
|
185
|
+
'',
|
|
186
|
+
'@openupgrade.migrate()',
|
|
187
|
+
'def migrate(env, version):',
|
|
188
|
+
' env.cr.execute(',
|
|
189
|
+
' "SELECT 1 FROM information_schema.tables WHERE table_name = %s",',
|
|
190
|
+
' ("mail_template",),',
|
|
191
|
+
' )',
|
|
192
|
+
' if not env.cr.fetchone():',
|
|
193
|
+
' return',
|
|
194
|
+
'',
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
if (templateCodes.length === 1) {
|
|
198
|
+
lines.push(` template_codes = ("${templateCodes[0]}",)`);
|
|
199
|
+
} else {
|
|
200
|
+
lines.push(' template_codes = (');
|
|
201
|
+
for (const code of templateCodes) lines.push(` "${code}",`);
|
|
202
|
+
lines.push(' )');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
lines.push(
|
|
206
|
+
' env.cr.execute(',
|
|
207
|
+
' "DELETE FROM mail_template WHERE template_code IN %s",',
|
|
208
|
+
' (template_codes,),',
|
|
209
|
+
' )',
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
return lines.join('\n') + '\n';
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export {
|
|
216
|
+
readWorkflowMappings,
|
|
217
|
+
detectRenames,
|
|
218
|
+
computeMigrationVersion,
|
|
219
|
+
generatePreMigrateScript,
|
|
220
|
+
readEmailTemplateMappings,
|
|
221
|
+
detectEmailRenames,
|
|
222
|
+
generateEmailPreMigrateScript,
|
|
223
|
+
};
|