Skip to content

Commit 0f988cf

Browse files
committed
autogenerate bank_reference from data_raw by default
1 parent c22fa1d commit 0f988cf

3 files changed

Lines changed: 53 additions & 27 deletions

File tree

Civi/Api4/Action/BankTransactionBatch/ImportBase.php

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ abstract class ImportBase extends \Civi\Api4\Generic\AbstractAction {
4545
/**
4646
* @var string
4747
*
48-
* The column header corresponding to the transaction reference - leave blank to auto-detect
48+
* The column header corresponding to the transaction reference - leave blank to hash raw data
4949
*/
5050
protected string $referenceColumn = '';
5151
/**
@@ -131,20 +131,11 @@ public function _run(Result $result) {
131131
$this->autodetectDateFormat($transactions);
132132
}
133133

134-
// parse data/amount/reference columns
134+
// extract data/amount/reference columns
135135
$transactions = array_map(function ($tx) {
136-
// parse reference
137-
$rawReference = $tx['data_parsed'][$this->referenceColumn];
138-
$tx['bank_reference'] = ($rawReference !== '') ? $rawReference : NULL;
139-
140-
// parse amount to float
141-
// @todo could we autodetect thousands separator?
142-
$rawAmount = $tx['data_parsed'][$this->amountColumn];
143-
$tx['amount'] = ($rawAmount !== '') ? (float) \str_replace($this->thousandsSeparator, '', $rawAmount) : NULL;
144-
145-
// parse date
146-
$tx['booking_date'] = $this->parseDate($this->dateFormat, $tx['data_parsed'][$this->dateColumn]);
147-
136+
$tx['bank_reference'] = $this->getReference($tx);
137+
$tx['amount'] = $this->getAmount($tx);
138+
$tx['booking_date'] = $this->getDate($tx);
148139
return $tx;
149140
}, $transactions);
150141

@@ -216,7 +207,8 @@ abstract protected function process(array $statement, array $transactions, Resul
216207
protected function validHeader(array $header): bool {
217208
$dateColumn = $this->dateColumn ? in_array($this->dateColumn, $header) : array_find($header, fn ($col) => \str_contains($col, E::ts('Date')));
218209
$amountColumn = $this->amountColumn ? in_array($this->amountColumn, $header) : array_find($header, fn ($col) => \str_contains($col, E::ts('Amount')));
219-
$referenceColumn = $this->referenceColumn ? in_array($this->referenceColumn, $header) : array_find($header, fn ($col) => \str_contains($col, E::ts('Reference')));
210+
// note: we only need the reference column if set - if not autogenerated
211+
$referenceColumn = $this->referenceColumn ? in_array($this->referenceColumn, $header) : TRUE;
220212

221213
return $dateColumn && $amountColumn && $referenceColumn;
222214
}
@@ -235,14 +227,13 @@ protected function autodetectHeader(array $sheet): void {
235227
// get the header row
236228
$header = $sheet[$this->headerIndex];
237229

238-
// use autodetect key columns values if needed
230+
// use autodetected key columns values if needed
239231
$this->dateColumn = $this->dateColumn ?: array_find($header, fn ($col) => \str_contains($col, E::ts('Date')));
240232
$this->amountColumn = $this->amountColumn ?: array_find($header, fn ($col) => \str_contains($col, E::ts('Amount')));
241-
$this->referenceColumn = $this->referenceColumn ?: array_find($header, fn ($col) => \str_contains($col, E::ts('Reference')));
242233

243234
// sense check: header should contain all key columns
244235
// this could fail if header and xColumns are specified that are inconsistent
245-
$missing = array_diff([$this->dateColumn, $this->amountColumn, $this->referenceColumn], $header);
236+
$missing = array_diff(array_filter([$this->dateColumn, $this->amountColumn, $this->referenceColumn]), $header);
246237
if ($missing) {
247238
$message = E::ts('Unable to find %1 in header row %2', [1 => implode(', ', $missing), 2 => $this->headerIndex]);
248239
throw new \CRM_Core_Exception($message);
@@ -292,4 +283,23 @@ protected function parseDate($format, $value): ?string {
292283
return NULL;
293284
}
294285

286+
protected function getDate(array $tx): ?string {
287+
return $this->parseDate($this->dateFormat, $tx['data_parsed'][$this->dateColumn]);
288+
}
289+
290+
protected function getReference(array $tx): ?string {
291+
if ($this->referenceColumn) {
292+
$rawReference = $tx['data_parsed'][$this->referenceColumn];
293+
return ($rawReference !== '') ? $rawReference : NULL;
294+
}
295+
return \md5($tx['data_raw']);
296+
}
297+
298+
protected function getAmount(array $tx): ?float {
299+
// parse amount to float
300+
// @todo could we autodetect thousands separator?
301+
$rawAmount = $tx['data_parsed'][$this->amountColumn];
302+
return ($rawAmount !== '') ? (float) \str_replace($this->thousandsSeparator, '', $rawAmount) : NULL;
303+
}
304+
295305
}

Civi/Api4/Action/BankTransactionBatch/Preview.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ protected function process(array $statement, array $transactions, Result $result
2222
'amountColumn' => $this->amountColumn,
2323
];
2424

25-
$result['columnOptions'] = array_keys($transactions[0]['data_parsed']);
25+
$result['headerColumns'] = array_keys($transactions[0]['data_parsed']);
2626
}
2727

2828
}

elements/civi-banking-import.js

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@
105105
buildOptionSelector(param, labelText, options) {
106106
const select = document.createElement('select');
107107

108-
select.append(...options.map((value) => {
108+
select.append(...options.map((o) => {
109109
const option = document.createElement('option');
110-
option.value = value;
111-
option.innerText = value;
112-
option.selected = (value === this.apiParams[param])
110+
option.value = o.value;
111+
option.innerText = o.label;
112+
option.selected = (o.value === this.apiParams[param])
113113
return option;
114114
}));
115115

@@ -135,11 +135,27 @@
135135
}
136136

137137
renderParamInputs() {
138+
const columnOptions = [
139+
{
140+
label: '- auto -',
141+
value: '',
142+
},
143+
...this.previewResult.headerColumns.map((col) => ({
144+
label: col,
145+
value: col
146+
}))
147+
];
148+
149+
const dateFormatOptions = ['Y-m-d', 'm/d/Y', 'd/m/Y'].map((format) => ({
150+
label: format,
151+
value: format
152+
}));
153+
138154
this.querySelector('fieldset').replaceChildren(
139-
this.buildOptionSelector('dateColumn', ts('Date Column'), this.previewResult.columnOptions),
140-
this.buildOptionSelector('dateFormat', ts('Date Format'), ['Y-m-d', 'm/d/Y', 'd/m/Y']),
141-
this.buildOptionSelector('amountColumn', ts('Amount Column'), this.previewResult.columnOptions),
142-
this.buildOptionSelector('referenceColumn', ts('Reference Column'), this.previewResult.columnOptions),
155+
this.buildOptionSelector('dateColumn', ts('Date Column'), columnOptions),
156+
this.buildOptionSelector('dateFormat', ts('Date Format'), dateFormatOptions),
157+
this.buildOptionSelector('amountColumn', ts('Amount Column'), columnOptions),
158+
this.buildOptionSelector('referenceColumn', ts('Reference Column'), columnOptions),
143159
// this.buildTextInput('statementTitle', ts('Statement Title'))
144160
);
145161
}

0 commit comments

Comments
 (0)