Jaatuli

From ICO wiki

Meeskond

  • Tarmo Luugus
  • Leho Kivistik
  • Hannes Mäeorg
  • Marten Tammeleht

Analüüs

Kirjeldus

Luua klientrakendus ja veebiteenus, millega saab restoran registreerida müüki ja sellega seotud elementaarseid asju hallata ehk kassa programm.

Eesmärk - kiirendada müüki ning tekitada mobiilne kassa. Saab lauast otse tellimusi teha. Tekib vähem vigu tellimuse vastuvõtmisel. Tulevased kliendid saavad ise ehitada endale sobiva liidese meie veebiteenusele. Personaalsed lahendused ning kliendi enda kujundus.

Funktsionaalsus

Must have

Veebiteenuse kaudu saab:

  • sisestada müüdavaid tooteid - omanik
  • tooterühmu teha - omanik
  • tooted ära jagada tooterühmadesse - omanik
  • erinevaid makseviise luua- omanik
  • arveid luua - teenindaja,omanik
  • arveid sulgeda/maksta - teenindaja,omanik
  • müügiaruandeid vaadata - TOP tooted mida on müüdud etteantud perioodi lõikes - omanik
  • luua teenindajatele kasutajaid - omanik

Nice to have

  • soodustustuste loomine - omanik
  • soodustuse määramine arvele - teenindaja
  • kliendikaardid/klientide haldus - omanik
  • laoseisu vaadata toodetel - omanik
  • laoseisu sisestada/tooteid sisse võtta lattu - omanik

Tegevuste logi

22.03.2018 Meeskonna moodustamine.


XML

Andmefail

<?xml version="1.0" encoding="utf-8"?>
<Bills>
  <BillDTO BillID="7010" State="Open">
    <BillRows>
      <BillRowDTO BillRowID="7021" FinalPrice="1" Quantity="3" >
        <Price PriceID="7010" PriceValue="2"/>
        <Product ProductID="7011">
          <ProductName><![CDATA[Fanta]]></ProductName>
          <ProductType ProductTypeID="3">
            <ProductTypeName><![CDATA[karastusjook]]></ProductTypeName>
          </ProductType>
        </Product>
      </BillRowDTO>
      <BillRowDTO BillRowID="8019" FinalPrice="5" Quantity="1">
        <Price PriceID="8009" PriceValue="5"/>
        <Product ProductID="8010">
          <ProductName><![CDATA[lõheleib]]></ProductName>
          <ProductType ProductTypeID="1003">
            <ProductTypeName><![CDATA[võileib]]></ProductTypeName>
          </ProductType>
        </Product>
      </BillRowDTO>
    </BillRows>
    <Waiter ApplicationUserID="385e090c-bb13-4f89-be77-3596657311c1" Role="Manager">
      <Email><![CDATA[tiina@kohvik.ee]]></Email>
    </Waiter>
  </BillDTO>
  <BillDTO BillID="7011" State="Open">
    <BillRows>
      <BillRowDTO BillRowID="7022" FinalPrice="1" Quantity="4" >
        <Price PriceID="7010" PriceValue="2"/>
        <Product ProductID="7011">
          <ProductName><![CDATA[Fanta]]></ProductName>
          <ProductType ProductTypeID="3">
            <ProductTypeName><![CDATA[karastusjook]]></ProductTypeName>
          </ProductType>
        </Product>
      </BillRowDTO>
      <BillRowDTO BillRowID="8020" FinalPrice="5" Quantity="1">
        <Price PriceID="8091" PriceValue="5"/>
        <Product ProductID="8011">
          <ProductName><![CDATA[kokteilsalat]]></ProductName>
          <ProductType ProductTypeID="1004">
            <ProductTypeName><![CDATA[salat]]></ProductTypeName>
          </ProductType>
        </Product>
      </BillRowDTO>
      <BillRowDTO BillRowID="8021" FinalPrice="5" Quantity="1">
        <Price PriceID="8092" PriceValue="5"/>
        <Product ProductID="8012">
          <ProductName><![CDATA[Seafilee ahjukartulitega]]></ProductName>
          <ProductType ProductTypeID="1004">
            <ProductTypeName><![CDATA[praad]]></ProductTypeName>
          </ProductType>
        </Product>
      </BillRowDTO>
    </BillRows>
    <Waiter ApplicationUserID="385e090c-bb13-4f89-be77-3596657311c1" Role="Manager">
      <Email><![CDATA[tiina@kohvik.ee]]></Email>
    </Waiter>
  </BillDTO>
</Bills>

Skeemifail

<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Bills">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="BillDTO">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="BillRows">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element maxOccurs="unbounded" name="BillRowDTO">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element name="Price">
                            <xs:complexType>
                              <xs:attribute name="PriceID" type="xs:unsignedInt" use="required" />
                              <xs:attribute name="PriceValue" type="xs:decimal" use="required" />
                            </xs:complexType>
                          </xs:element>
                          <xs:element name="Product">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="ProductName" type="xs:string" />
                                <xs:element name="ProductType">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element name="ProductTypeName" type="xs:string" />
                                    </xs:sequence>
                                    <xs:attribute name="ProductTypeID" type="xs:unsignedInt" use="required" />
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                              <xs:attribute name="ProductID" type="xs:unsignedInt" use="required" />
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                        <xs:attribute name="BillRowID" type="xs:unsignedInt" use="required" />
                        <xs:attribute name="FinalPrice" type="xs:decimal" use="required" />
                        <xs:attribute name="Quantity" type="xs:unsignedInt" use="required" />
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
              <xs:element name="Waiter">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="Email" type="xs:string" />
                  </xs:sequence>
                  <xs:attribute name="ApplicationUserID" type="xs:string" use="required" />
                  <xs:attribute name="Role" type="xs:string" use="required" />
                </xs:complexType>
              </xs:element>
            </xs:sequence>
            <xs:attribute name="BillID" type="xs:unsignedInt" use="required" />
            <xs:attribute name="State" type="xs:string" use="required" />
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

XML HTMLi (XSLT)

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="html" indent="yes"/>
  <xsl:template match="/Bills">
    <html>
      <head>
        <title>Arved</title>
      </head>
      <body>
        <xsl:for-each select="BillDTO">
          <h1>
            <xsl:value-of select="@BillID"/>
          </h1>
          <table Width="450">
            <tr>
              <th>Toode</th>
              <th>Toote liik</th>
              <th>Kogus</th>
              <th>Hind</th>
              <th>Lõpp hind</th>
            </tr>
            <xsl:for-each select="BillRows/BillRowDTO">
              <tr align="center">
                <td>
                  <xsl:value-of select="Product/ProductName"/>
                </td>
                <td>
                  <xsl:value-of select="Product/ProductType/ProductTypeName"/>
                </td>
                <td>
                  <xsl:value-of select="@Quantity"/>
                </td>
                <td>
                  <xsl:value-of select="Price/@PriceValue"/>
                </td>
                <td>
                  <xsl:value-of select="@FinalPrice"/>
                </td>
              </tr>
            </xsl:for-each>
          </table>
          <h4 align="middle">
            <xsl:value-of select="Waiter/Email"/>
          </h4>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>
</xsl:stylesheet>

XML teisendus XSLT

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>
  <xsl:template match="/Bills">
    <xsl:element name="Bills">
      <xsl:for-each select="BillDTO">
        <xsl:element name="Bill">
          <xsl:attribute name="BillID">
            <xsl:value-of select="@BillID"/>
          </xsl:attribute>
          <xsl:for-each select="BillRows/BillRowDTO">
            <xsl:element name="BillRow">
              <xsl:attribute name="BillRowID">
                <xsl:value-of select="@BillRowID"/>
              </xsl:attribute>
              <xsl:attribute name="FinalPrice">
                <xsl:value-of select="@FinalPrice"/>
              </xsl:attribute>
              <xsl:attribute name="Quantity">
                <xsl:value-of select="@Quantity"/>
              </xsl:attribute>
              <xsl:element name="Product">
                <xsl:attribute name="ProductID">
                  <xsl:value-of select="Product/@ProductID"/>
                </xsl:attribute>
                <xsl:element name="ProductName">
                  <xsl:value-of select="Product/ProductName"/>
                </xsl:element>
                <xsl:element name="ProductType">
                  <xsl:attribute name ="ProductTypeID">
                    <xsl:value-of select="Product/ProductType/@ProductTypeID"/>
                  </xsl:attribute>
                  <xsl:element name="ProductTypeName">
                    <xsl:value-of select="Product/ProductType/ProductTypeName"/>
                  </xsl:element>
                </xsl:element>
              </xsl:element>
              <xsl:element name="Price">
                <xsl:attribute name="PriceID">
                  <xsl:value-of select="Price/@PriceID"/>
                </xsl:attribute>
                <xsl:attribute name="PriceValue">
                  <xsl:value-of select="Price/@PriceValue"/>
                </xsl:attribute>
              </xsl:element>
            </xsl:element>
          </xsl:for-each>
          <xsl:element name="Waiter">
            <xsl:attribute name="WaiterID">
              <xsl:value-of select="Waiter/@ApplicationUserID"/>
            </xsl:attribute>
            <xsl:element name="Email">
              <xsl:value-of select="Waiter/Email"/>
            </xsl:element>
          </xsl:element>
        </xsl:element>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

Retsensioonid

Retsensioonid meeskond Raamaturiiul kodutööde kohta.

API teenused

Äriprojektina võiks idee kasutamist leida, eriti kui mõelda äri poolt edasi näiteks kasutajal oleks võimalik määrata omale lemmik autoreid ning kui mõni selle autori raamat müüki tuleb, saab teavituse.

Tarkvara projekti ülesehitus on tuttav ja arusaadav, projekt on jagatud eraldi kaheksasse projekti BL – business layer, DAL.App.EF - rakenduse EF data access layer, DAL.EF, DAL.Interfaces, Domain, presentatsiooni projekt WebApp, üldpilt on hea, Helper projekt on veidi õnnetu eraldi projektis ning ilmselt taaskasutust mõnes teises projektis tähtsust ei omaks, seega võiks olla integreeritud mõne olemasoleva projekti juurde.

Dataannotatsioonid on kasutusel nii domeeni objektide (andmebaasi piirnagud) kui andme ülekande objektides. Osad attribuudid on justkui vales kohas, näiteks Display atribuut domeeni objekti küljes, kuigi õigem koht oleks ilmselt vastav ViewModel.

Näha on et on otsitud ja proovitud erinevaid lahendusi probleemide lahendamisel, õigesti on tähelepandud, et raamatu info uuendamisel ei ole korrektne lahendus seotud objektide kustutamine ning seejärel dto-st leiduvate seosobjektide taaslisamist. Ehk raamatuinfo uuendamisel kustutakse kõik raamatukategooriad ning seejärel lisatakse uuesti ainult valitud.

EnumFactory tundub üleliigne ja keerukust lisav, eelkõige kasutatakse factory'id runtime uute objekide loomisel, enumite korral on tegu muutumatute numbri-nime seostega. Sama tulemuse saaks ilmselt lihtsa extensioonina lahendada. Enumite olemusest tingituna nende konverteerimine dtodeks lisaväärtust ei loo, enumid ei saa objektideks areneda.

Mõned äriloogilised lahendused tahaksid ülevaatamist (analüüsi) ja võibolla uuesti tegemist, näiteks BookAuthorRepository's leidub meetod RemoveByBookId millega kustutakse raamatu järgi autor. Tegu on ilmselt modeleerimise veaga ning ehk on paslik nüüd meenutada Priit Raspeli juhendamisi, näiteks et autor võib eksisteerida ka ilma ühegi raamatuta. Raamatu kaudu ei tohiks autori kustutamine kuidagi sündida.

InvoiceRepository's on meetod All, millele lisatud mittekohustuslik tagastatavate kirjete arvu piirav parameeter. Sellega võib kaasneda meetodi väärkasutust, kus sõltuvalt andmete hulgast teenus käitub erinevalt. Ennekõike tuleks leida võimalus teenusele filtri kaasa andmist kas klassi või LINQ kujul või kui ikkagi vaja piirata arvuliselt kirjete arvu luua eraldi meetod, näiteks Limit (LINQ pakub sisemiselt võimalust piirata kirjete arvu Take käsuga).

Kui kategooria kustutamisel on see seotud raamatuga, siis antakse süsteemne Foreign Key viga, vastav kontroll võiks toimuda koodis ning tagastada kliendi poolele kasutatav veakood (nt Forbidden 403)

Meeskonna wiki lehel toodud andmemudeli kirjelduses on mainitud et transpordimeetodid peidetakse kustamise asemel ära, tegelikkuses on realiseeritud tarnemeetodi reaalne kustutamine baasist DeleteShippingMethod

ShippingMethod lisamine/muutmine API kaudu on ilmselt ülearune kuna ühtegi kaubavedu (va. "Tulen ise järele") ei ole võimalik kasutusele võtta ilma täiendavate muudatusteta rakenduse äriloogikas.

BookDiscountService on implenteeritud baas osas, huvitav oleks olnud näha rohkem äriloogikat, näiteks allahindluse tagastamist etteantud päeval ja kliendiga.

DBContexti kirjeldamisel on keelatud kaskaad kustutamine, võiks lisada selgituse mis eesmärki see täidab.

Lõpetuseks võib veel märkida, et api koodist ei leidnud kasutajate põhist teenuste kasutamise logide loomist ega säilitamist.


XML Retsensioon

XML andmefail on korralikult koostatud kuid mõned parandused võiks teha. Olemas on rohkem kui minimaalne 4 loogilist dimensiooni ning on ka vähemalt 3-l dimensioonil olemas teisi atribuute peale ID. Millegi pärast on mõne dimensiooni ID pandud elementi mitte atribuudiks(nt: Author, BookDiscount). CDATA on kasutatud, kuid mitte igal pool näiteks Quality stringi oleks võinud ka CDATA sissse panna. XSD skeemfail valideerub vastu XMLi ning tundub olevat korras peale samade tähelepanekutega, mis olid XMLis.

XMLi transformatsiooni fail on lihtne kuid toimiv, läbi käiakse kõik raamatud ning lisatakse järjest elemente nende külge. Transformatsiooniga oleks võinud rohkem atribuute panna, mitte neid rohkem elementideks panna. Tehtav XML oleks selle tõttu lühem ja paremini loetav.

HTMLi transformatsiooni fail on hästi loetav, näidatakse kõiki raamatuid ja andmeid nende kohta ja need on selgesti eraldatud. Kasutatud on tingimusi, et ei tekiks arusaamatusi, tühjasid lünkasid või mõne koha rõhutamiseks(nt: tavahinna läbikriipsutamine soodustuse kehtimisel). Kasutatud on ka palju concat-i, mis teeb loetavuse kohati kergemaks ja faili ise väiksemaks. Võibolla raamatu kirjelduse peale oleks võinud panna väikese pealkiri, et tegu on lühikirjeldusega vms.

Üldiselt on kõik tehtud korralikult, üheski polnud vigu, mis takistaks valideerimist. Ainukesed vead olid, mille parandamine teeks failide loetavuse paremaks(nt: ühtlane stiil, rohkem atribuutide kasutamist XML-iks transformatsioonis).


Klientrakendus

Klientrakenduse projekt on arusaadava ülesehitusega ning kood on hästi loetav.

Klientrakendust on väga lihtne kasutada ning kasutaja leiab väikese vaevaga kõik vajaliku üles. Tavakasutaja saab süsteemis raamatuid vaadata ning otsida neidnii autori, pealkirja kui ka kirjastuse järgi. Raamatuid on võimalik lisada ostukorvi ning seejärel juba tellimus ära täita. Ostu sooritamisel valideeritakse korralikult sisestatud email ning ei ole võimalik sooritada tellimust ilmselgelt vigase emailiga. Probleemina võiks välja tuua selle, et raamatu lisamisel ostukorvi ei anta kliendile mingit tagasisidet ning tekib küsimus, kas toode lisandus ikka ostukorv. Samuti võiks tellimuse peal olla olemas võimalus ka ostukorvis olevate raamatute koguste muutmiseks.

Administraatori poole pealt tuleb välja osa puudulikku funktsionaalsust. Ei tööta raamatute ja tellimuste uuendamine. Kasutajaliides on selleks küll olemas, aga muudatused ei salvestata. Veidi suurem probleem on uute raamatute lisamine. Kui puudulike andmetega salvestamise nuppu vajutada, siis kaob võimalus formaadi ja köite valimiseks. Kui kõik väljad on õigesti täidetud, siis annab süsteem veateate. Koodi vaadates jäi mulje et plaanis oli ka igale raamatule pilt lisada - seda lahendust oleks kindlasti olnud huvitav näha. Kahju, et seda realiseerida ei jõutud.

Kokkuvõtvalt võib öelda, et suurem Klientrakendus oli visuaalselt ilus ja arusaadav. Tavakasutajale vajalik funktsionaalsusest oli klientrakenduses täiesti olemas ning süsteemis saab edukalt oste sooritada. Administraatori osa vajaks veel veidi parandamist.