empuno 1.2.0 → 1.3.0
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/empuno.mjs +150 -4
- package/package.json +1 -1
package/empuno.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import os from 'node:os';
|
|
|
21
21
|
import path from 'node:path';
|
|
22
22
|
import crypto from 'node:crypto';
|
|
23
23
|
|
|
24
|
-
const VERSION = '1.
|
|
24
|
+
const VERSION = '1.3.0'; // EMPUNO_CLI_VERSION — config/agent_api.php cli_version + package.json ile aynı tutulur
|
|
25
25
|
const NPM_PACKAGE = 'empuno';
|
|
26
26
|
const DEFAULT_URL = 'https://empuno.com';
|
|
27
27
|
const CATALOG_TTL_MS = 10 * 60 * 1000;
|
|
@@ -840,6 +840,140 @@ async function cmdTickets(args) {
|
|
|
840
840
|
}
|
|
841
841
|
}
|
|
842
842
|
|
|
843
|
+
// ─── geçiş (başka sistemden geçmiş verinin aktarımı) ─────────────────────────
|
|
844
|
+
|
|
845
|
+
const MIGRATE_KINDS = {
|
|
846
|
+
employees: { path: '/manager/data-migration/employees/commit', chunk: 200 },
|
|
847
|
+
leaves: { path: '/manager/data-migration/leaves/commit', chunk: 500 },
|
|
848
|
+
advances: { path: '/manager/data-migration/advances/commit', chunk: 200 },
|
|
849
|
+
expenses: { path: '/manager/data-migration/expenses/commit', chunk: 200 },
|
|
850
|
+
documents: { path: '/manager/data-migration/documents/commit', chunk: 10 },
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
/** Basit CSV ayrıştırıcı: tırnak, "" kaçışı, ; ya da , ayırıcı (başlıktan seçilir), BOM. */
|
|
854
|
+
function parseCsv(text) {
|
|
855
|
+
const src = text.replace(/^/, '');
|
|
856
|
+
const firstLine = src.split(/\r?\n/, 1)[0] || '';
|
|
857
|
+
const delim = (firstLine.match(/;/g) || []).length > (firstLine.match(/,/g) || []).length ? ';' : ',';
|
|
858
|
+
const rows = [];
|
|
859
|
+
let row = [];
|
|
860
|
+
let field = '';
|
|
861
|
+
let quoted = false;
|
|
862
|
+
for (let i = 0; i < src.length; i++) {
|
|
863
|
+
const c = src[i];
|
|
864
|
+
if (quoted) {
|
|
865
|
+
if (c === '"' && src[i + 1] === '"') { field += '"'; i++; }
|
|
866
|
+
else if (c === '"') quoted = false;
|
|
867
|
+
else field += c;
|
|
868
|
+
continue;
|
|
869
|
+
}
|
|
870
|
+
if (c === '"') quoted = true;
|
|
871
|
+
else if (c === delim) { row.push(field); field = ''; }
|
|
872
|
+
else if (c === '\n' || c === '\r') {
|
|
873
|
+
if (c === '\r' && src[i + 1] === '\n') i++;
|
|
874
|
+
row.push(field); field = '';
|
|
875
|
+
if (row.some((v) => v !== '')) rows.push(row);
|
|
876
|
+
row = [];
|
|
877
|
+
} else field += c;
|
|
878
|
+
}
|
|
879
|
+
row.push(field);
|
|
880
|
+
if (row.some((v) => v !== '')) rows.push(row);
|
|
881
|
+
const [header, ...data] = rows;
|
|
882
|
+
if (!header) return [];
|
|
883
|
+
const keys = header.map((h) => h.trim());
|
|
884
|
+
return data.map((cells) => {
|
|
885
|
+
const obj = {};
|
|
886
|
+
keys.forEach((k, idx) => {
|
|
887
|
+
const v = (cells[idx] ?? '').trim();
|
|
888
|
+
if (v === '' || !k) return;
|
|
889
|
+
// CSV değerleri metin kalır (TC, sicil no, telefon); yalnız mantıksal değerler çevrilir.
|
|
890
|
+
obj[k] = ['true', 'false'].includes(v.toLowerCase()) ? v.toLowerCase() === 'true' : v;
|
|
891
|
+
});
|
|
892
|
+
return obj;
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
function readRecordsFile(file) {
|
|
897
|
+
if (!fs.existsSync(file)) throw new UsageError(`dosya bulunamadı: ${file}`);
|
|
898
|
+
const text = fs.readFileSync(file, 'utf8');
|
|
899
|
+
if (/\.csv$/i.test(file)) return parseCsv(text);
|
|
900
|
+
let data;
|
|
901
|
+
try { data = JSON.parse(text); } catch (e) { throw new UsageError(`geçersiz JSON (${file}): ${e.message}`); }
|
|
902
|
+
const records = Array.isArray(data) ? data : data?.records;
|
|
903
|
+
if (!Array.isArray(records)) throw new UsageError('JSON bir dizi ya da {"records": [...]} olmalı');
|
|
904
|
+
return records;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
async function cmdMigrate(args) {
|
|
908
|
+
const { flags, fields, positional } = args;
|
|
909
|
+
const [kind, file] = positional;
|
|
910
|
+
if (!kind || !MIGRATE_KINDS[kind] || !file) {
|
|
911
|
+
throw new UsageError(`kullanım: empuno migrate <${Object.keys(MIGRATE_KINDS).join('|')}> <kayitlar.json|.csv> [--validate] [--on_existing update] [--employee_id N] [--chunk N]
|
|
912
|
+
alanlar: empuno describe data-migration.${kind && MIGRATE_KINDS[kind] ? kind : 'employees'}.commit · rehber: https://empuno.com/api-docs/agent#gecis`);
|
|
913
|
+
}
|
|
914
|
+
const auth = resolveAuth(flags);
|
|
915
|
+
requireKey(auth);
|
|
916
|
+
const opts = fieldMap(fields);
|
|
917
|
+
const spec = MIGRATE_KINDS[kind];
|
|
918
|
+
const records = readRecordsFile(file);
|
|
919
|
+
const chunkSize = Math.max(1, Math.min(Number(opts.chunk) || spec.chunk, spec.chunk));
|
|
920
|
+
const validate = Boolean(flags.validate);
|
|
921
|
+
const baseDir = path.dirname(path.resolve(file));
|
|
922
|
+
|
|
923
|
+
const summary = { kind, file, dry_run: validate, records: records.length, created: 0, updated: 0, skipped: 0, failed: 0, results: [] };
|
|
924
|
+
|
|
925
|
+
for (let offset = 0; offset < records.length; offset += chunkSize) {
|
|
926
|
+
const chunk = records.slice(offset, offset + chunkSize);
|
|
927
|
+
let response;
|
|
928
|
+
|
|
929
|
+
if (kind === 'documents') {
|
|
930
|
+
const files = [];
|
|
931
|
+
const manifest = chunk.map((entry, i) => {
|
|
932
|
+
const filePath = path.resolve(baseDir, String(entry.file ?? ''));
|
|
933
|
+
files.push({ field: 'files[]', file: filePath });
|
|
934
|
+
return { ...entry, file: i };
|
|
935
|
+
});
|
|
936
|
+
for (const f of files) if (!fs.existsSync(f.file)) throw new UsageError(`dosya bulunamadı: ${f.file}`);
|
|
937
|
+
response = await request(auth, 'POST', spec.path, {
|
|
938
|
+
body: { manifest: JSON.stringify(manifest), ...(validate ? { dry_run: 1 } : {}) },
|
|
939
|
+
files,
|
|
940
|
+
dryRun: flags['dry-run'],
|
|
941
|
+
});
|
|
942
|
+
} else {
|
|
943
|
+
const body = { records: chunk, ...(validate ? { dry_run: true } : {}) };
|
|
944
|
+
if (opts.on_existing) body.on_existing = String(opts.on_existing);
|
|
945
|
+
if (opts.employee_id) body.employee_id = Number(opts.employee_id);
|
|
946
|
+
response = await request(auth, 'POST', spec.path, { body, dryRun: flags['dry-run'] });
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (response.dryRun) {
|
|
950
|
+
print(response.result);
|
|
951
|
+
return 0;
|
|
952
|
+
}
|
|
953
|
+
const r = response.result || {};
|
|
954
|
+
if (!response.ok) {
|
|
955
|
+
// Doğrulama hatası (422) tüm parçayı etkiler: sunucunun errors'ını kayıt indeksine çevir.
|
|
956
|
+
summary.failed += chunk.length;
|
|
957
|
+
summary.results.push({ chunk_offset: offset, action: 'failed', status: response.status, message: r.message, errors: r.errors });
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
for (const key of ['created', 'updated', 'skipped', 'failed']) summary[key] += Number(r[key] || 0);
|
|
961
|
+
for (const row of r.results || []) summary.results.push({ ...row, index: offset + Number(row.index ?? 0) });
|
|
962
|
+
if (isTTY) warn(`${Math.min(offset + chunkSize, records.length)}/${records.length} işlendi`);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
summary.message = `${summary.created} ${validate ? 'eklenecek' : 'eklendi'}, ${summary.updated} güncellen${validate ? 'ecek' : 'di'}, ${summary.skipped} atlandı, ${summary.failed} hatalı${validate ? ' (doğrulama — hiçbir şey yazılmadı)' : ''}`;
|
|
966
|
+
if (wantsText()) {
|
|
967
|
+
process.stdout.write(`${summary.message}\n`);
|
|
968
|
+
for (const row of summary.results.filter((x) => x.action === 'failed')) {
|
|
969
|
+
process.stdout.write(` ✗ #${row.index ?? row.chunk_offset}: ${row.reason || row.message || ''}${row.errors ? ' ' + JSON.stringify(row.errors) : ''}\n`);
|
|
970
|
+
}
|
|
971
|
+
} else {
|
|
972
|
+
print(summary);
|
|
973
|
+
}
|
|
974
|
+
return summary.failed > 0 ? 1 : 0;
|
|
975
|
+
}
|
|
976
|
+
|
|
843
977
|
// ─── destek (yalnız empuno ekibinin ajanları — emp_staff_ jetonu) ────────────
|
|
844
978
|
|
|
845
979
|
function requireStaff(auth) {
|
|
@@ -968,6 +1102,15 @@ ALANLAR
|
|
|
968
1102
|
--alan @./dosya.pdf | --file alan=./dosya.pdf # multipart yükleme
|
|
969
1103
|
--query anahtar=değer # yazma isteğinde ek sorgu parametresi
|
|
970
1104
|
|
|
1105
|
+
GEÇİŞ (başka sistemden geçmiş veri — JSON ya da CSV; davet/e-posta/bildirim gitmez; tekrar çalıştırmak güvenli)
|
|
1106
|
+
empuno migrate employees calisanlar.csv --validate # önce yazmadan doğrula
|
|
1107
|
+
empuno migrate employees calisanlar.csv [--on_existing update] # ayrılmışlar dahil; TC ile eşler
|
|
1108
|
+
empuno migrate leaves izinler.json # her satırda identity_number|employee_number + leave_type
|
|
1109
|
+
empuno migrate advances avanslar.csv # geçmiş avans + devreden bakiye (deducted_amount)
|
|
1110
|
+
empuno migrate expenses masraflar.csv # ödenmiş geçmiş masraf (kategori adla)
|
|
1111
|
+
empuno migrate documents manifest.csv # file sütunu: dosya yolu; ≤10'arlı gönderilir
|
|
1112
|
+
alanlar: empuno describe data-migration.<tür>.commit · sıra: çalışanlar → izin → avans → masraf → evrak
|
|
1113
|
+
|
|
971
1114
|
TALEPLER (geliştirme isteği, hata, eleştiri, soru → empuno ekibi)
|
|
972
1115
|
empuno tickets [--status active|open|answered|closed|all] [--unread] [--mine]
|
|
973
1116
|
empuno tickets open "Toplu izin onayı" --kind feature --body "…"|@not.md [--priority high] [--command leave-requests.approve]
|
|
@@ -994,8 +1137,9 @@ BELGE: https://empuno.com/api-docs/agent
|
|
|
994
1137
|
|
|
995
1138
|
async function main() {
|
|
996
1139
|
const argv = process.argv.slice(2);
|
|
997
|
-
const
|
|
998
|
-
const
|
|
1140
|
+
const sub0 = argv.find((a) => !a.startsWith('--')) ?? '';
|
|
1141
|
+
const ticketish = ['tickets', 'ticket', 'support'].includes(sub0);
|
|
1142
|
+
const args = parseArgs(argv, ticketish ? ['mine', 'unread', 'close', 'token-stdin'] : sub0 === 'migrate' ? ['validate'] : []);
|
|
999
1143
|
const [sub, ...rest] = args.positional;
|
|
1000
1144
|
args.positional = rest;
|
|
1001
1145
|
|
|
@@ -1004,7 +1148,7 @@ async function main() {
|
|
|
1004
1148
|
return 0;
|
|
1005
1149
|
}
|
|
1006
1150
|
if (args.flags.help) {
|
|
1007
|
-
if (['tickets', 'ticket', 'support', 'login', 'commands', 'describe', 'call', 'api'].includes(sub)) {
|
|
1151
|
+
if (['tickets', 'ticket', 'support', 'login', 'commands', 'describe', 'call', 'api', 'migrate', 'reference'].includes(sub)) {
|
|
1008
1152
|
process.stdout.write(HELP);
|
|
1009
1153
|
return 0;
|
|
1010
1154
|
}
|
|
@@ -1041,6 +1185,8 @@ async function main() {
|
|
|
1041
1185
|
case 'reference':
|
|
1042
1186
|
case 'ref':
|
|
1043
1187
|
return cmdReference(args);
|
|
1188
|
+
case 'migrate':
|
|
1189
|
+
return cmdMigrate(args);
|
|
1044
1190
|
case 'support':
|
|
1045
1191
|
return cmdSupport(args);
|
|
1046
1192
|
case 'run':
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "empuno",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "empuno İK platformunun komut satırı: yönetim panelindeki tüm işlemleri (izin, bordro, vardiya, PDKS, zimmet, masraf, işe alım) yapay zekâ ajanlarına ve otomasyonlara açar.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|