From 4bc82443fe22bf48bfad6aa106c90864f6d66793 Mon Sep 17 00:00:00 2001
From: Jed Fox <git@jedfox.com>
Date: Wed, 1 Feb 2023 14:15:36 -0500
Subject: [PATCH] Improve handling of schedules that are missing a date (#601)

* Fix adding date back to a schedule that lost its date

* Propagate errors when searching matching transactions

* Make the error more visible

* Block removing the date field on schedule-linked rules
---
 .../accounts/SimpleTransactionsTable.js       |  2 ++
 .../src/components/modals/EditRule.js         | 11 ++++++++-
 .../src/components/schedules/EditSchedule.js  | 23 ++++++++++++++++++-
 .../loot-core/src/server/schedules/app.js     |  4 ++--
 4 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/packages/desktop-client/src/components/accounts/SimpleTransactionsTable.js b/packages/desktop-client/src/components/accounts/SimpleTransactionsTable.js
index ca04a19ff..189289b4c 100644
--- a/packages/desktop-client/src/components/accounts/SimpleTransactionsTable.js
+++ b/packages/desktop-client/src/components/accounts/SimpleTransactionsTable.js
@@ -146,6 +146,7 @@ const TransactionRow = React.memo(function TransactionRow({
 export default function SimpleTransactionsTable({
   transactions,
   schedules,
+  renderEmpty,
   fields = ['date', 'payee', 'amount'],
   style
 }) {
@@ -185,6 +186,7 @@ export default function SimpleTransactionsTable({
     <Table
       style={style}
       items={serializedTransactions}
+      renderEmpty={renderEmpty}
       headers={
         <>
           <SelectCell
diff --git a/packages/desktop-client/src/components/modals/EditRule.js b/packages/desktop-client/src/components/modals/EditRule.js
index e31d429d0..25b86c621 100644
--- a/packages/desktop-client/src/components/modals/EditRule.js
+++ b/packages/desktop-client/src/components/modals/EditRule.js
@@ -172,6 +172,7 @@ export function ConditionEditor({
   ops,
   condition,
   editorStyle,
+  isSchedule,
   onChange,
   onDelete,
   onAdd
@@ -214,7 +215,10 @@ export function ConditionEditor({
       <View style={{ flex: 1 }}>{valueEditor}</View>
 
       <Stack direction="row">
-        <EditorButtons onAdd={onAdd} onDelete={onDelete} />
+        <EditorButtons
+          onAdd={onAdd}
+          onDelete={isSchedule && field === 'date' ? null : onDelete}
+        />
       </Stack>
     </Editor>
   );
@@ -395,6 +399,7 @@ export function ConditionsList({
   conditions,
   conditionFields,
   editorStyle,
+  isSchedule,
   onChangeConditions
 }) {
   function addCondition(index) {
@@ -523,6 +528,7 @@ export function ConditionsList({
               editorStyle={editorStyle}
               ops={ops}
               condition={cond}
+              isSchedule={isSchedule}
               onChange={(name, value) => {
                 updateCondition(cond, name, value);
               }}
@@ -567,6 +573,8 @@ export default function EditRule({
   let dispatch = useDispatch();
   let scrollableEl = useRef();
 
+  let isSchedule = actions.some(action => action.op === 'link-schedule');
+
   useEffect(() => {
     dispatch(initiallyLoadPayees());
 
@@ -769,6 +777,7 @@ export default function EditRule({
                   conditions={conditions}
                   conditionFields={conditionFields}
                   editorStyle={editorStyle}
+                  isSchedule={isSchedule}
                   onChangeConditions={conds => setConditions(conds)}
                 />
               </View>
diff --git a/packages/desktop-client/src/components/schedules/EditSchedule.js b/packages/desktop-client/src/components/schedules/EditSchedule.js
index 79cd94673..064e56ea8 100644
--- a/packages/desktop-client/src/components/schedules/EditSchedule.js
+++ b/packages/desktop-client/src/components/schedules/EditSchedule.js
@@ -290,13 +290,18 @@ export default function ScheduleDetails() {
     let unsubscribe;
 
     if (state.schedule && state.transactionsMode === 'matched') {
-      let { conditions } = updateScheduleConditions(
+      let { error, conditions } = updateScheduleConditions(
         state.schedule,
         state.fields
       );
 
       dispatch({ type: 'set-transactions', transactions: [] });
 
+      if (error) {
+        dispatch({ type: 'form-error', error });
+        return;
+      }
+
       // *Extremely* gross hack because the rules are not mapped to
       // public names automatically. We really should be doing that
       // at the database layer
@@ -695,6 +700,22 @@ export default function ScheduleDetails() {
           )}
 
           <SimpleTransactionsTable
+            renderEmpty={
+              state.transactionsMode === 'matched' &&
+              (() => (
+                <View
+                  style={{ padding: 20, color: colors.n4, textAlign: 'center' }}
+                >
+                  {state.error ? (
+                    <Text style={{ color: colors.r4 }}>
+                      Could not search: {state.error}
+                    </Text>
+                  ) : (
+                    'No transactions found'
+                  )}
+                </View>
+              ))
+            }
             transactions={state.transactions}
             fields={['date', 'payee', 'amount']}
             style={{
diff --git a/packages/loot-core/src/server/schedules/app.js b/packages/loot-core/src/server/schedules/app.js
index 513f49ef9..a75edbe9b 100644
--- a/packages/loot-core/src/server/schedules/app.js
+++ b/packages/loot-core/src/server/schedules/app.js
@@ -268,8 +268,8 @@ export async function updateSchedule({ schedule, conditions, resetNextDate }) {
           oldConditions.find(c => c.field === 'account')
         ) ||
         !deepEqual(
-          stripType(oldConditions.find(c => c.field === 'date')),
-          stripType(newConditions.find(c => c.field === 'date'))
+          stripType(oldConditions.find(c => c.field === 'date') || {}),
+          stripType(newConditions.find(c => c.field === 'date') || {})
         )
       ) {
         await setNextDate({
-- 
GitLab