Related entries out of date (Android)

279 views
Skip to first unread message

Diego Tames

unread,
Dec 4, 2024, 6:04:42 PM12/4/24
to mementodatabase
Hello everyone,

I'm building a project management and financial tracking system in Memento Database, and I'm encountering an issue related to the way payments are recorded and how they reflect the amounts owed to team members.
System Overview:

The goal is to track the amount of revenue a project generates, record client payments, and track payments to team members based on their contributions. The system should calculate how much each team member is owed according to their percentage of contribution to the project.
Database Structure:


Projects:
- Title: text
- Amount Paid: lookup (sum of "Amount" for library "Payments" where "Type" is "Project")

Staff:
- Name: text
- Amount Paid: lookup (sum of "Amount" for library "Payments" where "Type" is "Staff")
- Gross Profit: lookup (sum of "Gross Profit" for library "Contributions")
- Amount Owed: javascript ("Gross Profit" - "Amount Paid")

Payments:
- Type: single-choise ("Project", "Staff")
- Amount: real number
- Project: One-to-Many (library "Projects") [if "Type" is "Project"]
- Staff: One-to-Many (library "Staff") [if "Type" is "Staff"]

Contributions:
- Project: One-to-Many (library "Projects")
- Staff: One-to-Many (library "Staff")
- Percentage: integer (1-100)
- Gross Profit: javascript ( field("Project")[0].field("Amount Paid") * ( field("Percentage") / 100 ) )



The Problem:

When I register a payment for a project, the amounts aren't automatically updated for the team members’ pending payments (Amount Owed). The changes only reflect if I manually enter each record ("Projects", "Contributions", "Staff") and save them, even without modifying anything.

I created a script that performs the update automatically when a payment is created or deleted, but I'm wondering if there’s a better way to automate this process.

Here is the code of my trigger:


if ( entry().field( "Type" ) === "Project" ){
  let project = entry().field( "Project" )[ 0 ];
  project.recalc();
  for ( let cont of libByName( "Contributions" ).linksTo( project ) ){
    cont.recalc();
    for ( let staff of libByName( "Staff" ).linksTo( cont ) ){
      staff.recalc();
    }
  }
}



I’m a developer, but I’m still learning how to use Memento Database, so any help or guidance would be greatly appreciated!

Thank you in advance!

Mmm

unread,
Dec 5, 2024, 11:17:31 AM12/5/24
to mementodatabase
Попробуйте каждую запись обновлять в отдельной функции с возвратом true.
Это касается и триггеров, которые используются.

const e_recalc = ( obj ) => {
    obj.recalc();
    return true;

}

if ( entry().field( "Type" ) === "Project" ){
    let project = entry().field( "Project" )[ 0 ];
    e_recalc( project );

    for ( let cont of libByName( "Contributions" ).linksTo( project ) ){
        e_recalc( cont );

        for ( let staff of libByName( "Staff" ).linksTo( cont ) ){
            e_recalc( staff );
        }
    }
}

Не панацея, но иногда помогает.

Желательно избавиться от полей с типом "поиск" ("подстановка"). По мере накопления большого массива записей возможно возникновение "тормозов".

Кроме того, sql() опережает linksTo() по производительности.

Добавить в записи скрытое поле JS "entry_id" с кодом:
entry().id;

Получить массив записей, по аналогии с linksTo():

let project_id = entry().field( "Project" )[ 0 ].id;
let linkToID = sql( 'SELECT * "Contributions" WHERE "entry_id" = project_id' ).asEntries();

Для перерасчета записей достаточно получать из sql() массив не всех полей библиотеки, а только полей "entry_id".

Находить записи по ID и обновлять их:

let ee = libByName( "Contributions" ).findById( id[ i ] );
e_recalc( ee );

В общем - большой простор для оптимизации...

Еще посмотрите:
https://groups.google.com/u/1/g/mementodatabase/c/oSkTC-5XImk/m/v-kRAnGIAQAJ

Как дополнительный вариант...
Не разработчик...

четверг, 5 декабря 2024 г. в 02:04:42 UTC+3, thame...@gmail.com:

Er Mo

unread,
Dec 5, 2024, 12:04:58 PM12/5/24
to mementodatabase
Hallo
Memento ist eine Datenbank . In erster linie dazu da Daten zu Speichern und nicht für Rechenaufgaben . Du muss deine Struktur so umstellen das du NUR Daten speichert und nicht das Ergebniss . Wenn ich ein Haushalsbuch mache , mit allen Ausgaben und Einnahmen speicher ich auch nicht den Geldbertrag in meinen Geldtasche , sonder gehe vom Anfangsstand aus und rechne die Ausgaben weck und die Einnahmen dazu . Stand - Ausgaben + Einnahmen = Betrag . So ist die Rechnung immer gleich

Hello
Memento is a database. It is primarily there to store data and not for calculations. You have to change your structure so that you ONLY store data and not the result. When I make a household book with all expenses and income, I don't store the amount of money in my wallet, but rather start from the initial balance and calculate the expenses and add the income. Balance - expenses + income = amount. This way the calculation is always the same.

Ernst

Diego Tames

unread,
Dec 5, 2024, 1:04:46 PM12/5/24
to mementodatabase
Thank you both!

From your answers I see no way to avoid the script, I was hoping that memento could keep those relations up to date without me having to create a trigger for that, I wanted to replace a spreadsheet...

To "Mmm": those are some interesting suggestions, I tought that linksTo had a better performance than sql since the latter, in my understanding, works on a second stage, not fetching data directly from the memento database. Also, how would you avoid a lookup to perform the aggregation?

To "Ernst": I'm not sure about your point, I mean, I'm not storing the result, I am, in fact, sotoring expenses (Payments of type Staff) and incomes (Paymnets of type Project), but I want to perform an aggregation and use that on a different library (Staff) to know how much is owed to a person, but I can't see a workaround for the lookup on the "Projects" library (sure I could be able to do it using sql, but then how would I be able to show the results without a table view?)

Please, if you can show me how to do that avoiding those lookups I would really appreaciate the help. As I see, those calculations are stored, which is something I am not sure could fit my needs

Er Mo

unread,
Dec 5, 2024, 2:00:44 PM12/5/24
to mementodatabase
Hallo
In Memento kann man Filter einrichten . Aus den Filtern Registerkarten machen . So kann man auf 2 Ebenen Filtern . Weiß nicht ob das Helfen kann Pojekte auf Registerkarten und nach Namen Filtern . Die Ein- , Ausgaben Summieren lassen , so habe ich den Aktuellen Stand . Allerdings ist dieser nicht gespeichert .

Hello
In Memento you can set up filters. Make tabs out of the filters. This way you can filter on 2 levels. I don't know if that can help. Filter projects on tabs and by name. Add up the inputs and outputs, so I have the current status. However, this is not saved.

Ernst

Mmm

unread,
Dec 6, 2024, 9:52:43 AM12/6/24
to mementodatabase
Простой пример отказа от полей с типом "поиск" ("подстановка") в пользу скриптов и числовых полей.
Так как в скриптах библиотек используется sql(), то необходимо разрешить доступ в каждой библиотеке к двум другим.

Это не готовое решение. 
Три библиотеки. 
Триггеры отслеживают самые распространенные события: 
- в библиотеке Payments => создание записи, изменение записи и поля, 
- в библиотеках Projects и Staff => создание и удаление ссылки. 
В окончательном решении необходимо предусмотреть другие события. 

Обновление полей  Amount Paid  в библиотеках Projects и Staff выполняются автоматически триггерами.

При необходимости эти поля можно обновить вручную из  библиотек Projects и Staff с помощью скрипта массового действия.

Для использования sql() в библиотеку Payments добавлены скрытые поля JS: 
- id_pr для отслеживания ID записи в поле Projects,
- id_st для отслеживания ID записи в поле  Staff,
- am_pr и am_st для отслеживания значения поля Amount в зависимости от значения поля Type. Кроме того эти поля используются для агрегации в библиотеке. 

Результат выполнения запроса sql() сумма полей am_pr или am_st при условии совпадения ID. 

Скрипт обновления на примере библиотеки Projects:

let entries = selectedEntries();
for (let ob of entries) {
    changeLink( ob );
}

function changeLink( ee ) {
    let id_ee = ee.id;
    let lbn = libByName('Payments');
    let sum = sql('SELECT SUM ("am_pr") FROM "Payments" WHERE "id_pr" = "' + id_ee + '"').asInt();    //Целое число
    if (ee.field('Amount Paid') != sum) {
        ee.set('Amount Paid', sum);
    }
    return true;
}

Шаблоны библиотек примера:

Будет интересно узнать мнение о производительности библиотек из примера по сравнению с исходными библиотеками автора.

четверг, 5 декабря 2024 г. в 22:00:44 UTC+3, ernst...@gmail.com:
Reply all
Reply to author
Forward
0 new messages