('aggregate', {
- subscription: { value: true },
- format: (value) => value ?? false,
- }).input.value;
+ const aggregateOptions = useMemo(
+ () =>
+ [
+ {
+ label: intl.formatMessage({
+ id: 'ui-plugin-bursar-export.bursarExports.token.userData',
+ }),
+ value: DataTokenType.USER_DATA,
+ },
+ {
+ label: intl.formatMessage({
+ id: 'ui-plugin-bursar-export.bursarExports.token.totalAmount',
+ }),
+ value: DataTokenType.AGGREGATE_TOTAL,
+ },
+ {
+ label: intl.formatMessage({
+ id: 'ui-plugin-bursar-export.bursarExports.token.numAccounts',
+ }),
+ value: DataTokenType.AGGREGATE_COUNT,
+ },
+ ].sort((a, b) => a.label.localeCompare(b.label)),
+ [intl]
+ );
return (
-
+
{(fieldProps) => (
@@ -65,11 +68,13 @@ export default function TransferAccountFields({ prefix }: { prefix: string }) {
fullWidth
marginBottom0
required
- label="Transfer account"
+ label={
+
+ }
dataOptions={[
{ label: '', value: undefined },
...accountSelectOptions,
- ]}
+ ].sort((a, b) => a.label.localeCompare(b.label))}
/>
)}
diff --git a/src/form/ConfigurationForm.test.tsx b/src/form/ConfigurationForm.test.tsx
new file mode 100644
index 00000000..3cf9483f
--- /dev/null
+++ b/src/form/ConfigurationForm.test.tsx
@@ -0,0 +1,60 @@
+import { render, screen } from '@testing-library/react';
+import arrayMutators from 'final-form-arrays';
+import React, { ComponentType } from 'react';
+import { Form, FormProps } from 'react-final-form';
+import withIntlConfiguration from '../test/util/withIntlConfiguration';
+import ConfigurationForm from './ConfigurationForm';
+
+jest.mock('@folio/stripes/final-form', () => ({
+ __esModule: true,
+ default: () => (Component: ComponentType) => (props: FormProps) =>
+ (
+
+ ),
+}));
+
+jest.mock('../api/queries/useFeeFineOwners', () => ({
+ __esModule: true,
+ default: () => ({ data: [], isSuccess: true }),
+}));
+
+jest.mock('../api/queries/useTransferAccounts', () => ({
+ __esModule: true,
+ default: () => ({ data: [], isSuccess: true }),
+}));
+
+describe('Configuration form', () => {
+ it('renders the configuration form', () => {
+ render(
+ withIntlConfiguration(
+
+ )
+ );
+
+ screen.debug();
+
+ expect(screen.getByText('Account data format')).toBeVisible();
+ });
+
+ it('renders the configuration form with aggregate initial true', () => {
+ render(
+ withIntlConfiguration(
+
+ )
+ );
+
+ screen.debug();
+
+ expect(screen.getByText('Patron data format')).toBeVisible();
+ });
+});
diff --git a/src/form/ConfigurationForm.tsx b/src/form/ConfigurationForm.tsx
index f874cfa0..1d55f0a3 100644
--- a/src/form/ConfigurationForm.tsx
+++ b/src/form/ConfigurationForm.tsx
@@ -8,19 +8,8 @@ import {
import stripesFinalForm from '@folio/stripes/final-form';
import { FormApi } from 'final-form';
import React, { FormEvent, MutableRefObject, useCallback } from 'react';
-import { FormRenderProps, FormSpy, useField } from 'react-final-form';
-import formValuesToDto from '../api/dto/to/formValuesToDto';
-import useCampuses from '../api/queries/useCampuses';
-import useCurrentConfig from '../api/queries/useCurrentConfig';
-import useFeeFineOwners from '../api/queries/useFeeFineOwners';
-import useFeeFineTypes from '../api/queries/useFeeFineTypes';
-import useInstitutions from '../api/queries/useInstitutions';
-import useLibraries from '../api/queries/useLibraries';
-import useLocations from '../api/queries/useLocations';
-import usePatronGroups from '../api/queries/usePatronGroups';
-import useServicePoints from '../api/queries/useServicePoints';
-import useTransferAccounts from '../api/queries/useTransferAccounts';
-import useInitialValues from '../hooks/useInitialValues';
+import { FormRenderProps, useField } from 'react-final-form';
+import { FormattedMessage } from 'react-intl';
import FormValues from '../types/FormValues';
import AggregateMenu from './sections/AggregateMenu';
import CriteriaMenu from './sections/CriteriaMenu';
@@ -65,82 +54,68 @@ function ConfigurationForm({
-
+
+ }
+ >
-
+
+ }
+ >
-
+
+ }
+ >
-
+
+ }
+ >
+ ) : (
+
+ )
}
>
-
+
+ }
+ >
-
+
+ }
+ >
-
+
+ }
+ >
-
-
-
- {({ values }) => {JSON.stringify(values, undefined, 2)}
}
-
-
-
- subscription={{ values: true }}>
- {({ values }) => (
- {JSON.stringify(formValuesToDto(values), undefined, 2)}
- )}
-
-
-
- {JSON.stringify(useCurrentConfig().data, undefined, 2)}
-
-
- {JSON.stringify(useInitialValues(), undefined, 2)}
-
-
- {JSON.stringify(usePatronGroups().data, undefined, 2)}
-
-
- {JSON.stringify(useServicePoints().data, undefined, 2)}
-
-
- {JSON.stringify(useFeeFineOwners().data, undefined, 2)}
-
-
- {JSON.stringify(useFeeFineTypes().data, undefined, 2)}
-
-
- {JSON.stringify(useTransferAccounts().data, undefined, 2)}
-
-
- {JSON.stringify(useInstitutions().data, undefined, 2)}
-
-
- {JSON.stringify(useCampuses().data, undefined, 2)}
-
-
- {JSON.stringify(useLibraries().data, undefined, 2)}
-
-
- {JSON.stringify(useLocations().data, undefined, 2)}
-
);
@@ -148,5 +123,4 @@ function ConfigurationForm({
export default stripesFinalForm({
validateOnBlur: true,
- // TODO: add validate,
})(ConfigurationForm);
diff --git a/src/form/sections/AggregateMenu.tsx b/src/form/sections/AggregateMenu.tsx
index d26b57ee..23fb6413 100644
--- a/src/form/sections/AggregateMenu.tsx
+++ b/src/form/sections/AggregateMenu.tsx
@@ -2,6 +2,7 @@ import { Checkbox } from '@folio/stripes/components';
import React from 'react';
import { Field, useField } from 'react-final-form';
import AggregateCriteriaCard from '../../components/AggregateCriteria/AggregateCriteriaCard';
+import { FormattedMessage, useIntl } from 'react-intl';
export default function AggregateMenu() {
const isAggregateEnabled = useField('aggregate', {
@@ -9,17 +10,24 @@ export default function AggregateMenu() {
format: (value) => value ?? false,
}).input.value;
+ const intl = useIntl();
+
return (
{(fieldProps) => (
-
+
)}
- If enabled, each output row will correspond to a single patron with
- all of their accounts, rather than just a single account.
+
diff --git a/src/form/sections/DataTokenMenu.tsx b/src/form/sections/DataTokenMenu.tsx
index 9bf47077..559c6ec1 100644
--- a/src/form/sections/DataTokenMenu.tsx
+++ b/src/form/sections/DataTokenMenu.tsx
@@ -4,6 +4,7 @@ import { FieldArray } from 'react-final-form-arrays';
import DataTokenCard from '../../components/Token/Data/DataTokenCard';
import { DataTokenType } from '../../types/TokenTypes';
import { useField } from 'react-final-form';
+import { FormattedMessage } from 'react-intl';
export default function DataTokenMenu() {
const aggregate = useField
('aggregate', {
@@ -32,7 +33,7 @@ export default function DataTokenMenu() {
/>
))}
>
)}
diff --git a/src/form/sections/ExportPreview.test.tsx b/src/form/sections/ExportPreview.test.tsx
index bba99337..de0a0268 100644
--- a/src/form/sections/ExportPreview.test.tsx
+++ b/src/form/sections/ExportPreview.test.tsx
@@ -5,6 +5,7 @@ import { Form } from 'react-final-form';
import ExportPreview from './ExportPreview';
import { DataTokenType, HeaderFooterTokenType } from '../../types/TokenTypes';
import userEvent from '@testing-library/user-event';
+import withIntlConfiguration from '../../test/util/withIntlConfiguration';
jest.mock('@ngneat/falso', () => ({
randFloat: jest.fn(() => 12.34),
@@ -14,11 +15,12 @@ jest.mock('@ngneat/falso', () => ({
describe('Export preview component', () => {
it('wraps lines when selected', async () => {
const { container } = render(
-
+ withIntlConfiguration(
+
+ )
);
-
// when undefined
expect(container.querySelector('.wrap')).toBeVisible();
diff --git a/src/form/sections/ExportPreview.tsx b/src/form/sections/ExportPreview.tsx
index 8a0369e3..175d6820 100644
--- a/src/form/sections/ExportPreview.tsx
+++ b/src/form/sections/ExportPreview.tsx
@@ -4,6 +4,7 @@ import { Card, Checkbox } from '@folio/stripes/components';
import classNames from 'classnames';
import { Field, useField } from 'react-final-form';
import ExportPreviewData from '../../components/ExportPreview/ExportPreviewData';
+import { FormattedMessage, useIntl } from 'react-intl';
export default function ExportPreview() {
const wrap = useField('preview.wrap', {
@@ -11,23 +12,32 @@ export default function ExportPreview() {
format: (value) => value ?? true,
}).input.value;
+ const intl = useIntl();
+
return (
<>
+ }
bodyClass={classNames(css.preview, { [css.wrap]: wrap })}
>
- This preview is only a sample and does not represent real data, nor
- does it consider any specified criteria.
+
{(fieldProps) => (
-
+
)}
@@ -35,7 +45,9 @@ export default function ExportPreview() {
)}
diff --git a/src/form/sections/HeaderFooterMenu.tsx b/src/form/sections/HeaderFooterMenu.tsx
index de99d6c7..122a85da 100644
--- a/src/form/sections/HeaderFooterMenu.tsx
+++ b/src/form/sections/HeaderFooterMenu.tsx
@@ -3,6 +3,7 @@ import React from 'react';
import { FieldArray } from 'react-final-form-arrays';
import { HeaderFooterTokenType } from '../../types/TokenTypes';
import HeaderFooterCard from '../../components/Token/HeaderFooter/HeaderFooterCard';
+import { FormattedMessage } from 'react-intl';
export default function HeaderFooterMenu({ name }: { name: string }) {
return (
@@ -21,7 +22,7 @@ export default function HeaderFooterMenu({ name }: { name: string }) {
>
)}
diff --git a/src/form/sections/SchedulingMenu.test.tsx b/src/form/sections/SchedulingMenu.test.tsx
index 875fa68d..eb8e74ca 100644
--- a/src/form/sections/SchedulingMenu.test.tsx
+++ b/src/form/sections/SchedulingMenu.test.tsx
@@ -69,7 +69,7 @@ describe('Scheduling menu', () => {
).toBeVisible();
expect(
screen.getByRole('textbox', {
- name: (name) => name.startsWith('Run at'),
+ name: (name) => name.startsWith('Start time'),
})
).toBeVisible();
});
@@ -94,7 +94,7 @@ describe('Scheduling menu', () => {
).toBeVisible();
expect(
screen.getByRole('textbox', {
- name: (name) => name.startsWith('Run at'),
+ name: (name) => name.startsWith('Start time'),
})
).toBeVisible();
expect(
diff --git a/src/form/sections/SchedulingMenu.tsx b/src/form/sections/SchedulingMenu.tsx
index b110df59..2ed01002 100644
--- a/src/form/sections/SchedulingMenu.tsx
+++ b/src/form/sections/SchedulingMenu.tsx
@@ -9,18 +9,24 @@ import {
} from '@folio/stripes/components';
import React from 'react';
import { Field, useField } from 'react-final-form';
-import { useIntl } from 'react-intl';
+import { FormattedMessage, useIntl } from 'react-intl';
import { Weekday, useLocaleWeekdays } from '../../utils/WeekdayUtils';
import SchedulingFrequency from '../../types/SchedulingFrequency';
export function getIntervalLabel(frequency: SchedulingFrequency) {
switch (frequency) {
case SchedulingFrequency.Hours:
- return 'Hours between runs';
+ return (
+
+ );
case SchedulingFrequency.Days:
- return 'Days between runs';
+ return (
+
+ );
case SchedulingFrequency.Weeks:
- return 'Weeks between runs';
+ return (
+
+ );
default:
return '';
}
@@ -31,7 +37,8 @@ export default function SchedulingMenu() {
subscription: { value: true },
}).input.value;
- const localeWeekdays = useLocaleWeekdays(useIntl());
+ const intl = useIntl();
+ const localeWeekdays = useLocaleWeekdays(intl);
return (
@@ -45,22 +52,32 @@ export default function SchedulingMenu() {
{...fieldProps}
fullWidth
required
- label="Frequency"
+ label={
+
+ }
dataOptions={[
{
- label: 'Never (run manually)',
+ label: intl.formatMessage({
+ id: 'ui-plugin-bursar-export.bursarExports.scheduling.frequency.manual',
+ }),
value: SchedulingFrequency.Manual,
},
{
- label: 'Hours',
+ label: intl.formatMessage({
+ id: 'ui-plugin-bursar-export.bursarExports.scheduling.frequency.hours',
+ }),
value: SchedulingFrequency.Hours,
},
{
- label: 'Days',
+ label: intl.formatMessage({
+ id: 'ui-plugin-bursar-export.bursarExports.scheduling.frequency.days',
+ }),
value: SchedulingFrequency.Days,
},
{
- label: 'Weeks',
+ label: intl.formatMessage({
+ id: 'ui-plugin-bursar-export.bursarExports.scheduling.frequency.weeks',
+ }),
value: SchedulingFrequency.Weeks,
},
]}
@@ -93,19 +110,27 @@ export default function SchedulingMenu() {
{(fieldProps) => (
-
+
)}
)}
{frequencyValue === SchedulingFrequency.Weeks && (
-
+
{(fieldProps) => (
>
{...fieldProps}
required
- label="Run on weekdays"
+ label={
+
+ }
dataOptions={localeWeekdays.map((weekday) => ({
label: weekday.long,
value: weekday.weekday,
diff --git a/src/form/sections/TransferInfoMenu.tsx b/src/form/sections/TransferInfoMenu.tsx
index e383cea5..4f40acaf 100644
--- a/src/form/sections/TransferInfoMenu.tsx
+++ b/src/form/sections/TransferInfoMenu.tsx
@@ -4,6 +4,7 @@ import { FieldArray } from 'react-final-form-arrays';
import ConditionalCard from '../../components/ConditionalCard';
import TransferAccountFields from '../../components/TransferAccountFields';
import { CriteriaTerminalType } from '../../types/CriteriaTypes';
+import { FormattedMessage } from 'react-intl';
export default function TransferInfoMenu() {
return (
@@ -21,7 +22,15 @@ export default function TransferInfoMenu() {
))}
-
+
+ ) : (
+
+ )
+ }
+ >
@@ -32,14 +41,12 @@ export default function TransferInfoMenu() {
})
}
>
- Add condition
+
- Conditions will be evaluated in order, with the first matched
- transfer account being used. If no conditions are matched, the
- account listed under “otherwise” will be used.
+
>
diff --git a/translations/ui-plugin-bursar-export/en.json b/translations/ui-plugin-bursar-export/en.json
index 5d26a3f6..62e812b8 100644
--- a/translations/ui-plugin-bursar-export/en.json
+++ b/translations/ui-plugin-bursar-export/en.json
@@ -1,40 +1,154 @@
{
- "meta.title": "Transfer criteria",
-
- "bursarExports": "Bursar exports",
- "bursarExports.schedulePeriod": "Schedule period",
- "bursarExports.schedulePeriod.none": "None",
- "bursarExports.schedulePeriod.hours": "Hours",
- "bursarExports.schedulePeriod.days": "Days",
- "bursarExports.schedulePeriod.weeks": "Weeks",
- "bursarExports.scheduleFrequency": "Schedule frequency",
- "bursarExports.scheduleWeekdays": "Weekdays",
- "bursarExports.scheduleWeekdays.friday": "F",
- "bursarExports.scheduleWeekdays.monday": "M",
- "bursarExports.scheduleWeekdays.saturday": "S",
- "bursarExports.scheduleWeekdays.sunday": "S",
- "bursarExports.scheduleWeekdays.thursday": "T",
- "bursarExports.scheduleWeekdays.tuesday": "T",
- "bursarExports.scheduleWeekdays.wednesday": "W",
- "bursarExports.scheduleTime": "Schedule time",
- "bursarExports.daysOutstanding": "Fees/Fines older than (days)",
- "bursarExports.patronGroups": "Patron groups",
- "bursarExports.transferOwner": "Transfer owner",
- "bursarExports.transferAccount": "Transfer account",
- "bursarExports.owner": "Fee/fine owner",
- "bursarExports.itemTypes": "Transfer types",
- "bursarExports.feeFineType": "Fee/fine type",
- "bursarExports.itemType": "Transfer type",
- "bursarExports.itemDescription": "Transfer description",
- "bursarExports.itemCode": "Transfer code",
- "bursarExports.itemCode.CHARGE": "Charge",
- "bursarExports.itemCode.PAYMENT": "Payment",
- "bursarExports.save": "Save",
- "bursarExports.save.success": "Bursar exports configuration has been successfully saved",
- "bursarExports.save.error": "Bursar exports configuration was not saved",
- "bursarExports.runManually": "Run manually",
- "bursarExports.runManually.success": "Bursar exports has been successfully scheduled",
- "bursarExports.runManually.error": "Bursar exports was not scheduled",
+ "meta.title": "Transfer configuration",
+
+ "bursarExports.paneTitle": "Transfer configuration",
+
+ "bursarExports.button.addCondition": "Add condition",
+ "bursarExports.button.add": "Add",
+ "bursarExports.button.save": "Save",
+ "bursarExports.button.runManually": "Run manually",
+ "bursarExports.otherwise": "Otherwise:",
+
+ "bursarExports.scheduling.accordion": "Scheduling",
+ "bursarExports.scheduling.frequency": "Frequency",
+ "bursarExports.scheduling.frequency.manual": "Never (run manually)",
+ "bursarExports.scheduling.frequency.hours": "Hours",
+ "bursarExports.scheduling.frequency.days": "Days",
+ "bursarExports.scheduling.frequency.weeks": "Weeks",
+ "bursarExports.scheduling.interval.hours": "Hours between runs",
+ "bursarExports.scheduling.interval.days": "Days between runs",
+ "bursarExports.scheduling.interval.weeks": "Weeks between runs",
+ "bursarExports.scheduling.time": "Start time",
+ "bursarExports.scheduling.weekdays": "Run on weekdays",
+
+ "bursarExports.criteria.accordion": "Criteria",
+ "bursarExports.criteria.select.allOf": "All of:",
+ "bursarExports.criteria.select.anyOf": "Any of:",
+ "bursarExports.criteria.select.noneOf": "None of:",
+ "bursarExports.criteria.select.age": "Age",
+ "bursarExports.criteria.select.amount": "Amount",
+ "bursarExports.criteria.select.owner": "Fee/fine owner",
+ "bursarExports.criteria.select.type": "Fee/fine type",
+ "bursarExports.criteria.select.location": "Item location",
+ "bursarExports.criteria.select.servicePoint": "Item service point",
+ "bursarExports.criteria.select.patronGroup": "Patron group",
+ "bursarExports.criteria.select.none": "No criteria (always run)",
+ "bursarExports.criteria.select.label": "Criteria",
+
+ "bursarExports.criteria.age.value": "Older than (days)",
+ "bursarExports.criteria.type.automatic": "Automatic",
+ "bursarExports.criteria.location.inst": "Institution",
+ "bursarExports.criteria.location.camp": "Campus",
+ "bursarExports.criteria.location.lib": "Library",
+ "bursarExports.criteria.location.loc": "Location",
+ "bursarExports.criteria.servicePoint.value": "Service point",
+
+ "bursarExports.aggregate.accordion": "Aggregate by patron",
+ "bursarExports.aggregate.enabler": "Group data by patron",
+ "bursarExports.aggregate.description": "If enabled, each output row will correspond to a single patron with all of their accounts, rather than just a single account.",
+ "bursarExports.aggregate.filter": "Filter type",
+ "bursarExports.aggregate.filter.header": "Only include patrons with:",
+ "bursarExports.aggregate.filter.none": "None (include all patrons)",
+ "bursarExports.aggregate.filter.numAccounts": "Number of accounts",
+ "bursarExports.aggregate.filter.numAccounts.amount": "Number of accounts",
+ "bursarExports.aggregate.filter.totalAmount": "Total amount",
+ "bursarExports.aggregate.filter.totalAmount.amount": "Amount",
+ "bursarExports.aggregate.filter.operator": "Comparison operator",
+ "bursarExports.aggregate.filter.operator.less": "Less than but not equal to",
+ "bursarExports.aggregate.filter.operator.lessEqual": "Less than or equal to",
+ "bursarExports.aggregate.filter.operator.greater": "Greater than but not equal to",
+ "bursarExports.aggregate.filter.operator.greaterEqual": "Greater than or equal to",
+ "bursarExports.aggregate.filter.description": "This will be applied after accounts are evaluated per the \u201cCriteria\u201d specified above.",
+
+ "bursarExports.header.accordion": "Header format",
+ "bursarExports.footer.accordion": "Footer format",
+
+ "bursarExports.token.newline": "Newline (LF)",
+ "bursarExports.token.newlineMicrosoft": "Newline (Microsoft, CRLF)",
+ "bursarExports.token.tab": "Tab",
+ "bursarExports.token.comma": "Comma",
+ "bursarExports.token.whitespace": "Whitespace",
+ "bursarExports.token.arbitraryText": "Arbitrary text",
+ "bursarExports.token.currentDate": "Current date",
+ "bursarExports.token.constantConditional": "Conditional text",
+ "bursarExports.token.numAccounts": "Number of accounts",
+ "bursarExports.token.totalAmount": "Total amount",
+ "bursarExports.token.userData": "User info",
+ "bursarExports.token.accountAmount": "Account amount",
+ "bursarExports.token.accountDate": "Account date",
+ "bursarExports.token.feeFineType": "Fee/fine type",
+ "bursarExports.token.itemInfo": "Item info",
+ "bursarExports.token.fallback": "Fallback value",
+ "bursarExports.token.fallback.description": "If the chosen date is not available/applicable, the fallback value will be used instead.",
+ "bursarExports.token.value": "Value",
+
+ "bursarExports.token.headerFooter.typeSelect": "Header/footer type select",
+
+ "bursarExports.token.dataType.typeSelect": "Data type select",
+
+ "bursarExports.token.whitespace.numSpaces": "Number of spaces",
+
+ "bursarExports.token.currentDate.format": "Format",
+ "bursarExports.token.currentDate.format.yearLong": "Year (4-digit)",
+ "bursarExports.token.currentDate.format.yearShort": "Year (2-digit)",
+ "bursarExports.token.currentDate.format.month": "Month",
+ "bursarExports.token.currentDate.format.date": "Day of month",
+ "bursarExports.token.currentDate.format.hour": "Hour",
+ "bursarExports.token.currentDate.format.minute": "Minute",
+ "bursarExports.token.currentDate.format.second": "Second",
+ "bursarExports.token.currentDate.format.quarter": "Quarter",
+ "bursarExports.token.currentDate.format.isoWeekNum": "ISO week number",
+ "bursarExports.token.currentDate.format.isoWeekYear": "ISO week year",
+ "bursarExports.token.currentDate.timezone": "Timezone",
+
+ "bursarExports.token.accountAmount.enableDecimal": "Include the decimal point",
+ "bursarExports.token.accountAmount.enableDecimal.description": "If selected, amounts will be exported like \u201c12.50\u201d if left unselected, they will be exported like \u201c1250\u201d.",
+
+ "bursarExports.token.accountDate.dateType": "Date",
+ "bursarExports.token.accountDate.dateType.created": "Creation date",
+ "bursarExports.token.accountDate.dateType.updated": "Last updated date",
+ "bursarExports.token.accountDate.dateType.dueItem": "Item due date",
+ "bursarExports.token.accountDate.dateType.dueLoan": "Loan end date",
+ "bursarExports.token.accountDate.fallback.description": "If the chosen date is not available/applicable, the fallback value will be used instead.",
+
+ "bursarExports.token.feeFineType.attribute": "Attribute",
+ "bursarExports.token.feeFineType.name": "Type name",
+ "bursarExports.token.feeFineType.id": "Type ID",
+
+ "bursarExports.token.itemInfo.name": "Name",
+ "bursarExports.token.itemInfo.barcode": "Barcode",
+ "bursarExports.token.itemInfo.material": "Material type",
+ "bursarExports.token.itemInfo.instId": "Institution ID",
+ "bursarExports.token.itemInfo.campId": "Campus ID",
+ "bursarExports.token.itemInfo.libId": "Library ID",
+ "bursarExports.token.itemInfo.locId": "Location ID",
+
+ "bursarExports.token.userInfo.folioId": "Folio ID",
+ "bursarExports.token.userInfo.extId": "External ID",
+ "bursarExports.token.userInfo.groupId": "Patron group ID",
+ "bursarExports.token.userInfo.barcode": "Barcode",
+ "bursarExports.token.userInfo.username": "Username",
+ "bursarExports.token.userInfo.firstname": "First name",
+ "bursarExports.token.userInfo.middlename": "Middle name",
+ "bursarExports.token.userInfo.lastname": "Last name",
+
+ "bursarExports.token.constantConditional.value": "Then use:",
+ "bursarExports.token.constantConditional.description": "Conditions will be evaluated in order, with the first matched value being used. If no conditions are matched, the fallback value will be used.",
+
+ "bursarExports.data.accordion.patron": "Patron data format",
+ "bursarExports.data.accordion.account": "Account data format",
+
+ "bursarExports.preview.accordion": "Preview",
+ "bursarExports.preview.header": "Export preview",
+ "bursarExports.preview.wrap": "Wrap long lines",
+ "bursarExports.preview.description": "This preview is only a sample and does not represent real data, nor does it consider any specified criteria.",
+ "bursarExports.preview.enableInvisibleChar": "Display invisible characters (newlines, tabs, and spaces)",
+
+ "bursarExports.transfer.accordion": "Transfer accounts to",
+ "bursarExports.transfer.description": "Conditions will be evaluated in order, with the first matched transfer account being used. If no conditions are matched, the account listed under \u201cotherwise\u201d will be used.",
+ "bursarExports.transfer.owner": "Fee/fine owner",
+ "bursarExports.transfer.account": "Transfer account",
+ "bursarExports.transfer.transferTo": "Transfer to:",
"permission.bursar-exports.all": "Transfer exports: Modify configuration and start jobs",
"permission.bursar-exports.manual": "Transfer exports: Start manual jobs",