Calculating Time Difference in InfoPath Form

This article describe a custom method for time difference based on a custom combo box used for hours and minutes input.

You can download the XSN file from https://1drv.ms/u/s!ApGubfWFh8Nuq5o3q77TRbTkVX634w

timesheet

This form is designated to calculate the time difference between end hour and minute and start hour and start minute.

The form contains a XML resource file used to populate the combo box for hours and minutes:

Structure:

<Hour>
    <HValue>9</HValue>
    <HDisplay>09</HDisplay>
    <HType>R</HType>
    <MValue>0</MValue>
    <MDisplay>00</MDisplay>
</Hour>

 

  • HValue – Integer from 0 to 23
  • HDisplay – Text, 00, 01 to 23
  • HType – R – Regular, ABH – After Business Hours, N – Night hours
  • MValue – Integer from 0 to 59
  • MDisplay – Text, 00, 01 to 59

Worked hours and minutes are calculated using xPath 1.0 (details in article IF-THEN-ELSE in xPath)

For H Diff (worked hours) formula is:

concat(substring(concat(substring(../my:EndH – ../my:StartH, 1, (../my:EndM – ../my:StartM >= 0) * string-length(../my:EndH – ../my:StartH)), substring(../my:EndH – ../my:StartH – 1, 1, (not(../my:EndM – ../my:StartM >= 0)) * string-length(../my:EndH – ../my:StartH – 1))), 1, (../my:EndH – ../my:StartH >= 0) * string-length(concat(substring(../my:EndH – ../my:StartH, 1, (../my:EndM – ../my:StartM >= 0) * string-length(../my:EndH – ../my:StartH)), substring(../my:EndH – ../my:StartH – 1, 1, (not(../my:EndM – ../my:StartM >= 0)) * string-length(../my:EndH – ../my:StartH – 1))))), substring(concat(substring(../my:EndH – ../my:StartH + 24, 1, (../my:EndM – ../my:StartM >= 0) * string-length(../my:EndH – ../my:StartH + 24)), substring(../my:EndH – ../my:StartH + 23, 1, (not(../my:EndM – ../my:StartM >= 0)) * string-length(../my:EndH – ../my:StartH + 23))), 1, (not(../my:EndH – ../my:StartH >= 0)) * string-length(concat(substring(../my:EndH – ../my:StartH + 24, 1, (../my:EndM – ../my:StartM >= 0) * string-length(../my:EndH – ../my:StartH + 24)), substring(../my:EndH – ../my:StartH + 23, 1, (not(../my:EndM – ../my:StartM >= 0)) * string-length(../my:EndH – ../my:StartH + 23)))))) – ../my:LunchBreak

  • LunchBreak – is 1 when is checked and 0 when is unchecked.

For number of minutes (M Diff) the formula is:

concat(substring(../my:EndM – ../my:StartM, 1, (../my:EndM – ../my:StartM >= 0) * string-length(../my:EndM – ../my:StartM)), substring(../my:EndM – ../my:StartM + 60, 1, (not(../my:EndM – ../my:StartM >= 0)) * string-length(../my:EndM – ../my:StartM + 60)))

Also, in the form is implemented a validation rules for cases when the hour is the same and start minutes are greater than end minutes.

ValidationRule

This rule is used also to deactivate submit button.

Future improvements: Add AM/PM format for time differences.

Hope it helps!

IF-THEN-ELSE in xPath

Există multă documentație și exemple pe Internet legate de utilizarea condițiilor IF-THEN-ELSE în formularele personalizate în InfoPath din SharePoint. De ce încă despre InfoPath? Pentru că este încă un produs suportat în versiunea de SharePoint 2016 și a fost prelungit suportul pentru InfoPath 2013. De ce xPath? Pentru că va mai rezista ceva timp.

În modimage clasic în xPath 1.0, cel care poate fi folosit pe formularele InfoPath 2013, nu există o sintaxă de tip IF-THEN-ELSE dar există o adaptare a acesteia, oarecum mai complicată dar care funcționează inclusiv pentru IF-uri imbricate.

În mod simplificat sintaxa este:

concat(
substring(TrueResult, 1, (BoolCondition) * string-length(TrueResult)),
substring(ElseResult, 1, (not(BoolCondition)) * string-length(ElseResult))
)

Formula poate fi scrisă pe mai multe rânduri sau pe un singur rând caracterele Enter nefiind interpretate în execuție. O altă formă a IF-THEN-ELSE-ului este prezentată în forma:

concat(
    substring(TrueResult, 1 div BoolCondition),
    substring(ElseResult, 1 div not(BoolCondition))
    )

Din punctul meu de vedere formula în schimb nu este la fel de exactă, rezultatul pentru True fiind de multe ori altertat, mai ales când se operează cu numere.

 

Introducerea unei noi condiții pe ramura IF-THEN transformă formula în:

concat(
    substring(
            concat(
                substring(
                    TrueResult1
                    , 1, (BoolCondition) * string-length(
                    TrueResult1
                    )),
                substring(
                    ElseResult1
                    , 1, (not(BoolCondition)) * string-length(
                    ElseResult1
                    ))
                )
            , 1, (BoolCondition) * string-length(
            concat(
                substring(
                    TrueResult1
                    , 1, (BoolCondition) * string-length(
                    TrueResult1
                    )),
                substring(
                    ElseResult1
                    , 1, (not(BoolCondition)) * string-length(
                    ElseResult1
                    ))
                )
            )),
    substring(
            ElseResult2
            , 1, (not(BoolCondition)) * string-length(
            ElseResult2
            ))
    )

Condițiile folosesc operatorii clasici: =, >, etc. În cazul în care valoarea comparată în condiție este un număr trebuie să folosim funcția number(câmp).

Prezentul articol este mai mult un exemplu de calculare a unui câmp dintr-un formular pe baza alegerilor sau datelor introduse de un utilizator. Practic există și alte metode de lucru, xPath fiind una din metodele foarte dificile. Astfel, dacă avem un formular prin intermediul căruia dorim să comparăm două valori, Value1 și Value2 atunci pentru a rezolva problema trebuie să folosim următoarea formulă:

image

Formularul conține două câmpuri cu numele Value1 și Value2 și un buton care determină valoarea textului afișat prin utilizarea formulei respective:

image

Pas cu pas implementarea:

1. Am creat un formular simplu în SharePoint cu cele două coloane;

2. Am personalizat formularul în InfoPath și am adăugat butonul Compare…

3. La acțiunea butonului am pus setare valoare câmp Title la formula de mai sus.

Simplu, nu? Smile

Condițiile cumulative

În IF-uri mai avem de multe ori și condiții cumulative. Pentru a specifica acest cumul de condiții trebuie să folosim operatorul AND sau OR după caz.

Formula devine în acest caz:

concat(
    substring(TrueResult, 1, (BoolCondition1 and BoolCondition2) * string-length(TrueResult)),
    substring(ElseResult, 1, (not(BoolCondition1 and BoolCondition2)) * string-length(ElseResult))
    )

 

Exemplu de formulă pentru compararea sumară a trei valori:

concat(
    substring(„Ambele valori sunt mai mari”, 1,
        ((number(dfs:dataFields/my:SharePointListItem_RW/my:Value1)>number(dfs:dataFields/my:SharePointListItem_RW/my:Value3))
        and (number(dfs:dataFields/my:SharePointListItem_RW/my:Value2)>number(dfs:dataFields/my:SharePointListItem_RW/my:Value3))) *
        string-length(„Ambele valori sunt mai mari”)),
    substring(„Una din valori este mai mica”, 1,
        (not((number(dfs:dataFields/my:SharePointListItem_RW/my:Value1)>number(dfs:dataFields/my:SharePointListItem_RW/my:Value3))
        and (number(dfs:dataFields/my:SharePointListItem_RW/my:Value2)>number(dfs:dataFields/my:SharePointListItem_RW/my:Value3)))) *
        string-length(„Una din valori este mai mica”))
    )

Sper să vă fie util!

Validare CNP în #Excel și #SharePoint

După un concediu înfiorător de relaxant și o săptămână groaznică de acomodare, astăzi nu mi-am propus dar am reușit să încep cu ”motoarele în flăcări”. :)

Se face că un bun prieten are o mică problemă de rezolvat în SharePoint și anume aceea de validare a unor CNP-uri introduse într-o listă. Ce ar fi putut fi mai simplu, nu? :)

Ca să ajungi la o coloană calculată în SharePoint trebuie să ai ceva cunoștințe de Excel… așa că am pornit în realizarea formulei întâi în Excel.

Am aflat de un miraculos număr: 279146358279 pe care mulți colegi tehnici nu îl înțeleg dar pentru care am găsit o explicație pe comunitatea utilizatorilor de FoxPro:

”Scopul cifrelor de control este să semnaleze dacă vreuna din cifrele utile a fost schimbată. Există o întreagă terorie a cifrelor de control și o gramada de algoritmi cu diferite toleranțe. In cazul codurilor până la 10 cifre algoritmul respectiv folosește numerele prime de o cifră in ordine descrescăstoare: 7, 5, 3, 2, 1, după care se repetă până se acoperă lungimea codului. Este un algoritm cu o toleranță foarte mică la erori.

In cazul codurilor mai mari de 10 cifre se folosește un algoritm mai complicat ținându-se cont și de alte elemente, cum ar fi în cazul CNP faptul că cifrele de pe anumite poziții nu pot avea orice valoare. Totuși algoritmul nu este perfect, se poate vedea clar că cifra de control este 1 în cazul în care  restul împărțirii la 11 este 1 sau 10. Cu alte cuvinte CNP-urile 1640228240011 și 2640228240011 sunt amandouă valide deși diferă doar o cifră, deci un baiat ar putea fi confundat cu o fată fără ca eroarea să fie sesizată de un computer.”

Pentru cei care nu cunosc structura CNP se pot informa la adresa: http://www.hangman.ro/cnp/. Tot la această adresă mai explicată odată constanta de verificare.

Varianta de lucru în Excel

Să revenim la modelul de validare în Excel. Descărcați fișierul Validare CNP. În prima foaie de calcul există un model descompus al metodei de calcul:

ValidareCNP Descompus

În foaia a doua, este un tabel care face o îmbinare a validărilor din celulele S, T, U si V.

ValidareCNP Compus

Pentru cei care nu pot descărca fișierul formula de calcul este:

=IF(AND(INT(MID([@CNP];13;1))=IF(MOD(2*INT(MID([@CNP];1;1))+7*INT(MID([@CNP];2;1))+9*INT(MID([@CNP];3;1))+1*INT(MID([@CNP];4;1))+4*INT(MID([@CNP];5;1))+6*INT(MID([@CNP];6;1))+3*INT(MID([@CNP];7;1))+5*INT(MID([@CNP];8;1))+8*INT(MID([@CNP];9;1))+2*INT(MID([@CNP];10;1))+7*INT(MID([@CNP];11;1))+9*INT(MID([@CNP];12;1));11)=10;1;MOD(2*INT(MID([@CNP];1;1))+7*INT(MID([@CNP];2;1))+9*INT(MID([@CNP];3;1))+1*INT(MID([@CNP];4;1))+4*INT(MID([@CNP];5;1))+6*INT(MID([@CNP];6;1))+3*INT(MID([@CNP];7;1))+5*INT(MID([@CNP];8;1))+8*INT(MID([@CNP];9;1))+2*INT(MID([@CNP];10;1))+7*INT(MID([@CNP];11;1))+9*INT(MID([@CNP];12;1));11));INT([@CNP])>0);”CNP CORECT”;”CNP GRESIT”)

Adaptare în SharePoint

După ce am obținut formula concatenată în Excel, implementarea în SharePoint este doar o adaptare a acesteia.

Se crează așadar o listă simplă personalizată în care am schimbat coloana Title cu CNP și am mai adăugat o coloană nouă Stare de tip calculated column. Valoare coloanei calculate este:

=IF(AND(INT(MID(CNP,13,1))=IF(MOD(2*INT(MID(CNP,1,1))+7*INT(MID(CNP,2,1))+9*INT(MID(CNP,3,1))+1*INT(MID(CNP,4,1))+4*INT(MID(CNP,5,1))+6*INT(MID(CNP,6,1))+3*INT(MID(CNP,7,1))+5*INT(MID(CNP,8,1))+8*INT(MID(CNP,9,1))+2*INT(MID(CNP,10,1))+7*INT(MID(CNP,11,1))+9*INT(MID(CNP,12,1)),11)=10,1,MOD(2*INT(MID(CNP,1,1))+7*INT(MID(CNP,2,1))+9*INT(MID(CNP,3,1))+1*INT(MID(CNP,4,1))+4*INT(MID(CNP,5,1))+6*INT(MID(CNP,6,1))+3*INT(MID(CNP,7,1))+5*INT(MID(CNP,8,1))+8*INT(MID(CNP,9,1))+2*INT(MID(CNP,10,1))+7*INT(MID(CNP,11,1))+9*INT(MID(CNP,12,1)),11)),INT(CNP)>0),”CNP CORECT”,”CNP GRESIT”)

Izbitor de asemănător, nu? :)

Rezultatul:

Validare CNP Lista SharePoint 2013

În cazul în care vă propuneți să realizați formula în InfoPath pentru validarea funcției trebuie să știm că există o serie de diferențe printre care: funcția MID() este înlocuită cu funcția substring(), funcția MOD() nu există ci este înlocuită de operatorul mod care nu funcționează la fel ca cel din Excel, și altele printre care inexistența atât de utilului IF-THEN-ELSE.

Evaluarea CNP-ului în timp real

Să trecem la puțină treabă de MVP :)

Așadar, ”clientul” dorește ca după ce a completat câmpul de CNP să-i apară în mod automat, fără să dea submit, ca în exemplul anterior, dacă CNP-ul a fost completat corect sau nu:

image

În ecranul curent apar pe lângă starea de validare încă două variabile de lucru.

Starea este calculată în InfoPath prin compararea stării unor câmpuri calculate cu xPath. După cum spuneam în paragraful anterior în xPath nu avem IF-THEN-ELSE. În schimb în articolul Conditional Default Values putem vedea mai multe metode de înlocuire a IF-ului.

Personal am testat și utilizat metoda:

concat(
substring(TrueResult, 1, (BoolCondition) * string-length(TrueResult)),
substring(ElseResult, 1, (not(BoolCondition)) * string-length(ElseResult)))

Ca să ajung în schimb la rezultatul final, trebuie menționat că xPath nu suportă funcții logice imbircate (IF-uri în IF-uri) de aceea a trebuit să separ într-o variabilă de lucru denumită nan2 rezultatul unei funcții logice intermediare:

NAN2:

substring(CNP; 13; 1) = concat(substring(1; 1; (nan = 10) * string-length(1)); substring(nan; 1; (not(nan = 10)) * string-length(nan)))

nan-ul în formulă este de faptu modul calculat de interpretare a formulei de validare a CNP-ului.

NAN:

(2 * substring(CNP; 1; 1) + 7 * substring(CNP; 2; 1) + 9 * substring(CNP; 3; 1) + 1 * substring(CNP; 4; 1) + 4 * substring(CNP; 5; 1) + 6 * substring(CNP; 6; 1) + 3 * substring(CNP; 7; 1) + 5 * substring(CNP; 8; 1) + 8 * substring(CNP; 9; 1) + 2 * substring(CNP; 10; 1) + 7 * substring(CNP; 11; 1) + 9 * substring(CNP; 12; 1)) – 11 * (ceiling((2 * substring(CNP; 1; 1) + 7 * substring(CNP; 2; 1) + 9 * substring(CNP; 3; 1) + 1 * substring(CNP; 4; 1) + 4 * substring(CNP; 5; 1) + 6 * substring(CNP; 6; 1) + 3 * substring(CNP; 7; 1) + 5 * substring(CNP; 8; 1) + 8 * substring(CNP; 9; 1) + 2 * substring(CNP; 10; 1) + 7 * substring(CNP; 11; 1) + 9 * substring(CNP; 12; 1)) / 11) – 1)

Starea finală este determinată printr-un alt șir de evaluare care are legătură cu NAN2.

Starea:

concat(substring(„CNP CORECT”; 1; (nan2 = „true”) * string-length(„CNP CORECT”)); substring(„CNP GRESIT”; 1; (not(nan2 = „true”)) * string-length(„CNP GRESIT”)))

Concluzii

Câteva ore bune de muncă dar o statisfacție pozitivă că dacă îți pui ceva în cap doar lenea te poate ține departe de rezultate.

Sper să fie util cuiva!

Blog la WordPress.com.

SUS ↑