@waron97/prbot 3.1.0 → 3.1.2
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/autopr.js +77 -0
- package/src/commands/changelog.js +50 -0
- 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 +28 -5
- package/src/lib/git.js +6 -2
- package/src/lib/logger.js +15 -0
- package/src/lib/premigrate.js +215 -0
|
@@ -0,0 +1,215 @@
|
|
|
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 renames = [];
|
|
171
|
+
for (const [code, oldXmlId] of oldMap) {
|
|
172
|
+
const newXmlId = newMap.get(code);
|
|
173
|
+
if (newXmlId && newXmlId !== oldXmlId) renames.push({ oldXmlId, newXmlId });
|
|
174
|
+
}
|
|
175
|
+
return renames;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function generateEmailPreMigrateScript(renames) {
|
|
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
|
+
' renames = [',
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
for (const { oldXmlId, newXmlId } of renames) {
|
|
192
|
+
lines.push(` ("${oldXmlId}", "${newXmlId}"),`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
lines.push(
|
|
196
|
+
' ]',
|
|
197
|
+
' for old_name, new_name in renames:',
|
|
198
|
+
' env.cr.execute(',
|
|
199
|
+
' "UPDATE ir_model_data SET name = %s WHERE name = %s AND model = %s",',
|
|
200
|
+
' (new_name, old_name, "mail.template"),',
|
|
201
|
+
' )',
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
return lines.join('\n') + '\n';
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export {
|
|
208
|
+
readWorkflowMappings,
|
|
209
|
+
detectRenames,
|
|
210
|
+
computeMigrationVersion,
|
|
211
|
+
generatePreMigrateScript,
|
|
212
|
+
readEmailTemplateMappings,
|
|
213
|
+
detectEmailRenames,
|
|
214
|
+
generateEmailPreMigrateScript,
|
|
215
|
+
};
|