Sisällys
Luokka, joka hoitaa tietokannan kanssa kommunikoinnin. Luokka on toteutettu staattisena kirjastoluokkana, mutta koska se tarvitsee erinäisiä parametreja JDBC-yhteyden muodostamiseen, täytyy ne asettaa setDBServer-metodilla, ennen kuin muita metodeita voidaan käyttää. Metodi asettaa kaikki tarvitavat parametrit staattisiin muuttujiin ja rekisteröi JDBC-ajurin.
Metodi hakee tietoa tietokannasta. Soveltuu yksinkertaisiin tietokantahakuihin. Rakentaa saamiensa parametrien perusteella SQL-kyselyn ja muodostaa saadusta ResultSetistä DBResult-olion (tämä sen takia, että käytetty tietokantayhteys luodaan ja suljetaan metodin sisällä, eikä JDBC takaa että ResultSetiä voidaan enää käyttää yhteyden sulkemisen jälkeen). Virhetilanteen kohdatessa tuotetaan DBException. Finally-haarassa varmistetaan, että kaikki tietokantaresurssit varmasti vapautetaan.
Metodi hakee sivun logiikka XML:än. Ensin haetaan logicxml-taulusta XML:n URL käyttäen getDromDatabase-metodia. Tämän jälkeen haetaan itse XML URLin osoittamasta paikasta käyttäen apumetodia getStringFromURL. Virhetilanteissa tuotetaan DBException.
Metodi avaa URL-yhteyden annetulla URLilla ja lukee sen sisällön merkkijonoon. Ei sisällä mitään virheenkäsittelyä. Tarkoitettu sisäiseen käyttöön (getPageLogic ja getLogicXSL).
Metodi tarkastaa käyttäjän sisäänkirjautumisen. Yrittää hakea myyjätietoja contact-taulusta annetun käyttäjänimen ja salasanan perusteella. Mikäli mitään ei löydy, tuotetaan InvalidLoginException. Jos tiedot löytyvät, ne palautetaan.
Metodi lataa tietokantaan talletetun tarjouksen. Tarjous on offer-taulussa XML-muodossa. Mikäli mitään ei löytynyt, tuotetaan DBException.
Metodi tallettaa tarjouksen tietokantaan. Tarjous annetaan metodille XML-muodossa, joten pystyäkseen asettamaan offer-taulun contact_id- ja customer_id-kentät, metodin täytyy kaivaa ne ensin ulos XML:stä. Tähän käytetään apumetodia getSellerAndCustomerIds. Tämän jälkeen kyseiset ID:t, aikaleima sekä annettu tarjouksen XML talletetaan XML-tauluun INSERT-lauseella.
Metodi päivittää tarjousta, joka on jo talletettu tietokantaan. Muuten kuten saveOffer, mutta käyttää tietojen talletukseen UPDATE-lausetta.
Luokka tietokantahakujen tuloksien palauttamiseen. Kopioi jossainmäärin ResultSetin yleistä toiminnallisuutta. Sisältää rivejä ja sarakkeita, joissa tietoa Object-olioina. Tietoja voi hakea joko yksitellen tai muuttamalla koko tuloksen XML:äksi. Tuloksen voi muutta myös DOM-puuksi. Tiedot ovat talletettu sarakkeita vastaaviin vektoreihin. Sarakevektorit taas ovat laitettu hajautustauluun, josta ne saa sarakkeen nimen perusteella.
Konstruktorissa annetaan hakutuloksen sisältävä ResultSet ja taulun nimi, josta tulos saatiin. Luo sarakevektorit ja hajautustaulun. Laskee rivit ja sarakkeet.
Metodi palauttaa annetulta riviltä ja sarakkeelta löytyvän Objectin. Metodista on kaksi versiota, joista toisessa sarakkeeseen viitataan nimellä ja toisessa indeksillä. Jos sarakkeen nimi tai indeksi on virheellinen tuotetaan NoSuchColumnException. Jos rivin indeksi on virheellinen tuotetaan NoSuchRowException.
Metodi palauttaa annetulta riviltä ja sarakkeelta löytyvän arvon merkkijonona. Arvo haetaan ensin kutsumalla getObject-metodia. Merkijonoksi muuntaminen tapahtuu toString-metodilla, joten tämän pitäisi onnistua aina, mikäli arvo ei ole null. Jos arvo on null, palautetaan null. Tästäkin metodista on versio sekä sarakkeen nimelle, että indeksille.
Metodi palauttaa annetulta riviltä ja sarakkeelta löytyvän arvon kokonaislukuna. Arvo haetaan ensin kutsumalla getObject-metodia. Mikäli arvo on null tai jokin muu, kuin Number-luokan ilmentymä, tuotetaan InvalidValueTypeException. Muutoin palautetaan luvun kokonaislukuarvo. Tästäkin metodista kaksi versiota.
Sisältää staattisen filter-metodin, joka uorittaa tuotelistan prosessoinnin tuoteriippuvuuksien ja asiakkaan profiilitietojen perusteella. Varsinaiseen prosessointiin luokka käyttää DependencyCore-luokkaa. Parametreina saatujen DBResult-olioiden muuttamiseen DependencyCoren ymmärtämiksi Item- ja DependencyRule- olioiksi käytetään sisäluokkia ProductFactory ja DependencyFactory. Koska Item on interface, sille on tehty toteutus ProductItem, joka myös on DependencyFilterin sisäluokka. Kun DependencyCore on merkinnyt tuotelistan tuotteet, rakennetaan alkuperäisestä syötteenä annetusta listasta DOM-puu, (DBResultin toDOM-metodilla) joka käydään läpi ja siihen lisätään selectable-tagit DependencyFilterin merkintöjen mukaisesti. Lopuksi DOM-puu muutetaan XML-merkkijonoksi.
Sisältää staattisen metodin createProducts, joka muuttaa DBResult-olion ProductItem taulukoksi. Käy yksinkertaisesti läpi kaikki DBResultin rivit ja luo niiden pohjalta ProductItem-olioita. Ainoa DBReultista käytettävä tieto on id, tila alustetaan arvolla YES. Mikäli tapahtuu jokin virhe (esim. DBResult ei sisällä tarvittuja tietoja) kirjoitetaan siitä merkintä lokiin ja palautetaan tyhjä taulukko.
Sisältää staattisen metodin createDependencies, joka muuttaa DBResult-olion DependencyRule-taulukoksi. Käy läpi DBResultin rivit ja luo niiden pohjalta DependencyRule-olioita. Mikäli tapahtuu jokin virhe (esim. DBResult ei sisällä tarvittuja tietoja) kirjoitetaan siitä merkintä lokiin ja palautetaan tyhjä taulukko.
Kuvaa kahden tuotteen välistä riippuvuutta. Tuotteisiin viitataan Object-tyyppisillä tunnisteilla source ja target. Riippuvuuden tyyppi ilmaistaan kokonaisluvulla. Tyyppiä varten on määritelty vakiot TYPE_INCLUDE ja TYPE_EXCLUDE, muun tyyppisiä riippuvuuksia ei ole. Konstruktorissa annetaan tuotteiden tunnisteet ja tyyppi. Mikäli lähde tai kohde ovat samoja tai tyyppi on jokin muu kuin TYPE_INCLUDE tai TYPE_EXCLUDE, tuotetaan IllegalArgumentException. Luokassa on lisäksi aksessorit lähteelle, kohteelle ja tyypille.
Sisältää staattisen metodin processDependencies, joka prosessoi annetun Item-taulukon (available [=tuotelista]) toisen Item-taulukon (selected [=profiilitiedot]) ja näiden välisten riippuvuussuhteiden (dependencies) perusteella.
Available-taulukon Item:ien tilaksi asetetaan joko YES, NO tai MORE seuraavan logiikan mukaan: (Merkitään availablea A:lla ja selectediä S:llä. Itemien a ja b välistä include-sääntöä merkitään a+>b ja exclude-sääntöä a->b)
Jos a kuuluu A:han ja b kuuluu S:ään ja on olemassa sääntö a->b tai b->a, on a:n tila NO. Jos a ja b kuuluvat A:han ja on olemassa sääntö a+>b ja b:n tila on NO, on myös a:n tila NO. Jos a kuuluu A:han ja on olemassa sääntö a+>b ja b:n tila ei ole NO, mikäli se sisältyy A:han, on a:n tila MORE. Muutoin a:n tila on YES.
Metodi käyttää apuna luokkaa ItemNode, jonka avulla muodostetaan available-taulukon alkioista verkko. Jokainen verkon solmu (ItemNode-olio) vastaa yhtä available-taulukon alkiota. Siitä on kaaret niihin solmuihin, jotka tarvitsevat sen (eli on olemassa vastaava include-sääntö). Ajatuksena on ensin luoda verkon solmut (tilaksi alustetaan YES), prosessoida sitten include-säännöt ja lisätä niitä vastaavat verkon kaaret (ja asettaa vastaavien solmujen ja Itemien tilaksi MORE). Lopuksi käsitellään exclude-säännöt. Kun jonkin solmun (ja Itemin) tilaksi asetetaan NO, voidaan nyt käydä rekursiivisesti läpi kaikki sitä tarvitsevat solmut ja asettaa niidenkin tilaksi NO. Rekursio on toteutettu ItemNode-luokassa. Sääntöjen käsittelyn helpottamiseksi available-taulukon Itemejä vastaavat solmut asetetaan ensin hajautustauluun, josta ne löytyvät nopeasti Itemin ID:n avulla. Selected-taulukon Itemeille taas luodaan HashSet, jonka avulla saadaan selville mitkä Itemit sisältyvät selected-taulukkoon.
Luokka helpottamaan riippuvuussuhteiden setvimistä, kuten DependencyCore:n kohdalla kerrottiin. Sisältää yhden Item-olion, sen tilan sekä listan niistä ItemNode-olioista, jotka tarvitsevat tätä (eli verkon kaaret). Konstruktorissa annetaan Item ja sen sekä kyseisen ItemNoden tilaksi asetetaan YES.
Metodi addRequirer lisää yhden ItemNoden tarvitsijoiden listaan.
Metodi setState asettaa ItemNoden ja sen sisältämän Itemin tilan. Mikäli tila on NO, käydään läpi kaikki tarvitsijalistaan merkityt solmut ja kutsutaan niillekkin metodia setState(NO). Kutsua ei kuitenkaan tehdä, jos solmun tila on jo valmiiksi NO. Tämä estää algoritmia jäämästä ikuiseen silmukkaan, mikäli verkko sisältää syklejä. Jotta prosessointi toimisi oikein, ei setStatea saa kutsua arvolla NO ennenkuin kaikki verkon kaaret ovat varmasti asetettu.
Servlet-osajärjestelmä on TAHKO:n keskeisin osajärjestelmä. Se välittää käyttäjän tekemät http-requestit eteenpäin TAHKO:n sisällä. Kaikki kommunikaatio käyttäjän/käyttöliittymän kanssa tapahtuu Servlet-osajärjestelmän välityksellä.
Servlet-osajärjestelmässä on vain yksi luokka: TahkoServlet, joka on aivan tavallinen HttpServlet:in aliluokka. Kuten servletit yleensä, se kuormittaa metodit init(), doGet() ja doPost().
Init() hakee web.xml-tiedostossa määritellyt alkuparametrit (esim. tietokannan nimi, tilapäistiedostojen hakemisto) ja alustaa niiden perusteella Servletin, DBSlaven ja TomcatWrapperin.
doPost() pelkästään delegoi saamansa kutsut doGet() -metodille.
TahkoServletin doGet()-metodi on TAHKO:n sydän. Se käskee käyttäjältä saatujen pyyntöjen perusteella Db-osajärjestelmää tekemään tietokantahakuja, ApacheWrapperia sekä käsittelemään xml-tiedostoja että kääntämään ja suorittamaan JSP-sivuja. Käyttäjälle doGet() palauttaa käyttöliittymä-html -sivuja. doGet()-metodin toteuttaminen oli suoraviivaista suunnitelman perusteella, koska monimutkaisuus on piilotettu muihin osajärjestelmiin.
Tämä on toteutukseltaan yksinkertainen työkaluluokka, joka sisältää vain yhden staattisen funktion. Sitä käytetään muuntamaan profiili-xml PDF-muotoon. Välivaiheena on fo-xml, joka saadaan Profiili-xml:stä xsl-muunnoksen avulla. Muunnokseen käytetään XmlProcessor -luokkaa. Apachen Fop muuntaa näin saadun fo-xml:n PDF:ksi. Virhetilanteessa aiheutetaan PDFProcessingException, joka on TahkoExceptionin aliluokka.
Tämä on toteutukseltaan yksinkertainen työkaluluokka. Sitä käytetään xsl-muunnosten tekemiseen xml-dokumenteille. Ainoan staattisen funktion toteutus sisältää tavanomaisia kutsuja Apachen Xalaniin. Xalanin käytön aikana syntyneet virheet kirjoitetaan tahkon lokiin. Virhetilanteessa aiheutetaan XMLProcessingException, joka on TahkoExceptionin aliluokka.
Tämä luokka kapseloi Apachen Tomcat JSP-koneen TAHKO:n tarkoituksia varten. Tomcatia käytetään tässä luokassa paitsi jsp-sivujen ajamiseen, niin myös niiden kääntämiseen. TomcatWrapper tallettaa jo servleteiksi käännetyt jsp-sivut HashMap:iin, jotta niitä ei joka ajokerralla tarvitsisi kääntää uudestaan (kääntäminen on todella hidas toimenpide). Tällainen Apachen Tomcatin integroiminen osaksi omaa ohjelmaa on monimutkaista verrattuna Fop:n ja Xalanin integrointiin.
Tavallisten alustusten lisäksi konstruktorissa luodaan HashMap, johon talletetaan <servlet,pageId> -pareja sitä mukaa kun JSP-sivuja käännetään.
Translate-funktio kääntää JSP-sivun ja sijoittaa tuloksena saadun servletin HashMap:iin. Tomcatin rakenteesta johtuen tämä on helpommin sanottu kuin tehty. Joudutaan tekemään omat (sisäluokka-) implementaatiot JspCompilationContext, Mangler ja Options -rajapinnoille sekä kirjoittamaan aliluokat JspLoader ja ClassLoader -luokille.
Service-funktio etsii HashMap:sta pageId:tä vastaavan servletin, ja välittää tälle annetun requestin. Jotta Service-metodi voisi palauttaa servletin työn tuloksen Stringinä, se välittää servletille itse tehdyn implementaation HttpServletResponse -rajapinnasta. Tämä implementaatio käyttää apunaan itse tehtyä ServletOutputStream:n aliluokkaa, ja pystyy siten palauttamaan Stringin getResponse() -metodillaan. TAHKO:ssa tämä string on XML-muotoinen, niinsanottu data-xml.
Tahko-järjestelmää voidaan konfiguroida muuttamalla kolmen tyyppisiä XML- ja XSL-tiedostoja. Yksi näistä muutettavista XML-tiedostoista on käyttöliittymä-XML. Käyttöliittymä-XML luo pohjan koko järjestelmän perusrakenteelle; se kertoo, millaisista sivuista järjestelmä koostuu. Näistä "sivuaihioista" muodostetaan HTML-sivuja logiikka-XSL:n logiikkasääntöjen ja käyttöliittymä-XSL:n ulkoasusääntöjen perusteella.
Esimerkkisovelluksen, tarjouskonfiguraattorin, käyttöliittymä-XML sisältää seuraavat elementit, jotka on listassa lueteltu hierarkkisesti rakenteensa perusteella:
DATA-tagi pitää sisällään kaiken XML-dokumenttiin kuuluvan datan. Se on päätagi, joka kokoaa alleen siis kaiken, mitä käyttöliittymä-XML:ssa on.
Käyttöliittymä koostuu "hakukentistä" eli HTML-lomakkeista, joilla haetaan tietoa ja ohjaillaan sivustolla navigointia erilaisten sääntöjen perusteella. UI-tagi sisältää hakukentän määrittelyyn tarvittavan tiedon. Koneisto, joka tämän määrittelyn perusteella luo varsinaisen sivun, on XSL-tiedostoissa.
FORM-tagi sisältää lomakkeen määrittelyn. Lomake on aina UI-tagin sisällä.
Taulu 5.1. FORM-lomakkeen attribuutit ja niiden käyttö
Tagi | Attribuutit | Mahd. arvot |
---|---|---|
action | Haun käsittelevä logiikkasivu | Asiakkaan tai tuotteen valinta (select_customer|select_product) |
target | Kohde, jossa tulos näytetään | _self. Hakutulos näytetään samassa kehyksessä, josta kutsu lähtee. |
submit | Submit-napin teksti | Mikä tahansa merkkijono. Käytetty Ok:ta. |
method | Metodi, jolla lomake lähetetään | POST tai GET. Käytetty POST:ia. |
INPUT-tagi määrittelee varsinaisen hakukentän, joss haut määritellään. Tämä voi olla tyypiltään esimerkiksi tekstikenttä.
INPUT-tagi sisältää SELECT_CUSTOMER-tagin, jos haku on asiakashaku ja SELECT_PRODUCT-tagin, jos hakutulos on tuotteita.
DB-RESULT-tagin sisällä ovat tietokantahaun tulokset. Tällä tagilla on attribuutti TABLE, joka on sen taulun nimi, josta tiedot haetaan. Tämän kentän arvoksi käy mikä tahansa taulu, josta haetaan tietoja, tässä tapauksessa tarjouskonfiguraattorin tietoja.
RECORDS-tagin sisällä ovat kaikki tietokantahan palauttamat rivit kukin omana RECORD-taginaan, tai NO_RECORDS_FOUND-tagi, jos haussa ei löytynyt yhtään riviä. Tämä tagi on aina tyhjä, ts. sen sisällä ei ole muita tageja.
SELECTABLE-tagi kertoo, voiko käyttäjä valita tuotteen. Tagi on käytössä ainoastaan tilanteissa, joissa valitaan tuotteita. Kertoo, onko tuote valittavissa vai ei, tai valitaanko mukaan myös muita tuotteita tämä tuote valittaessa.
Taulu 5.2. SELECTABLE-tagin attribuutit ja niiden käyttö
Attribuutti | Selitys | Mahd. arvot |
---|---|---|
type | Kertoo, onko tuote valittavissa vai ei, tai valitaanko mukaan myös muita tuotteita | Y: tuote on valittavissa |
N: Tuotetta ei voida valita. Jokin on jo valittu, tai asiakkaalla aikaisemmin oleva tuote estää valinnan | ||
M: Jos tuote valitaan, lisätään mukaan tarjoukseen myös muita tuotteita, jotka ovat pakollisia, jotta kyseistä tuotetta voi käyttää. |
COLUMN-tagi sisältää tietokantahaun palauttaman rivin yhden sarakkeen.
Tarjous-XML on senhetkinen tarjous datamuotoisena. Tästä dokumentista muodostetaan XSL-tyylitiedoston avulla HTML-sivu, joka esittää tarjouksen senhetkisen tilanteen.
Esimerkkisovelluksen tarjous-XML sisältää seuraavat elementit, jotka on listassa lueteltu hierarkkisesti rakenteensa perusteella:
DATA-tagi on juuritagi, joka pitää sisällään kaiken datan.
UI-tagi määrittelee näytettävän käyttöliittymän samaan tapaan kuin käyttöliittymä-XML:ssa.
SHOW_OFFER määrittelee, että mukana näytetään seuraava profiili. Tagia käytetään tarjouksen tilanteen näyttöön tarkoitetussa kehyksessä.
PROFILE-tagi sisältää tarjouksen senhetkisen tilanteen tiedot, jotka esitetään ELEMENT- ja edelleen FIELD-tagien sisällä.
ELEMENT-tagien sisällä eritellään jokainen tarjouksen osa. Oheisessa taulukossa ELEMENT-tagin attribuutit:
Taulu 5.3. ELEMENT-tagin attribuutit ja niiden käyttö
Attribuutti | Selitys | Mahd. arvot |
---|---|---|
name | Tarjouksen osan nimi | seller: myyjän tiedot |
customer: asiakkaan tiedot | ||
product: yksittäisen tuotteen tiedot | ||
freetext: tervehdys tai muu vastaava vapaamuotoinen teksti | ||
discount: tarjouksen alennusprosentti | ||
valid: tarjouksen voimassaoloaika |
FIELD-tagi sisältää tarjouksen eri osiin liittyvät yksittäiset tiedot. Tagi on aina ELEMENT-tagin sisällä. Tagilla on yksi mahdollinen attribuutti: NAME, joka kertoo osaan liittyvän tiedon sarakkeen nimen tietokannassa. Sen mahdollisia arvoja ovat kaikki sarakkeiden nimet, jotka haetaan kannasta ja pidetään tallessa profiilissa.
XSL-tiedostoissa on yleisiä eri dokumentteihin liittyviä XSL-tageja, jotka on esitelty seuraavassa taulukossa.
Taulu 5.4. Yleisiä dokumenttiin liittyviä XSL-tageja
Attribuutti | Selitys |
---|---|
match=/ | Juurielementti määrittää dokumentin peruskomponentit eli dokumentin alun, Java-skriptit ja dokumentin lopun. |
match=ui/form | Elementissä määritellään hakukentän lomake ja lomakkeen kentät. |
match=select_customer/db_result/records: Tässä määritellään asiakashaun tuloksena saatavan taulukon alku, otsikot ja loppu.
match=select_customer/db_result/no_records_found: Tässä kerrotaan käyttäjälle, ettei haussa löytynyt asiakkaita.
match=select_customer/db_result/records/record: Tässä näytetään hakutuloksena saatu rivi. Valintalinkki on JavaScript-kutsu, joka lisää halutun asiakkaan oikean puoleisen kehyksen setCustomer-kenttään ja lähettää lomakkeen.
match=select_product/db_result/records: Tässä määritellään taulukon alku, otsikkorivi ja loppu, jossa hakutulos näytetään.
match=select_product/db_result/no_records_found: Tässä kerrotaan käyttäjälle, ettei haussa löytynyt tuotteita.
match=select_product/db_result/records/record: Tässä näytetään hakutuloksena saatu rivi. Valintalinkki on JavaScript-kutsu, joka lisää halutun tuotteen oikean puoleisen kehyksen addProduct kenttään ja lähettää lomakkeen. Jos tuotetta ei voida jostain syystä valita kerrotaan siitä käyttäjälle.
match=ui/show_offer: Tässä rakennetaan tarjouksen sen hetkisen tilanne. Määritellään tarvittavat kentät ja otsikot. Myyjän, asiakkaan ja tuotteiden tiedot käsitellään omassa templatessa.
match=profile/element[@name='seller']: Tässä muutetaan myyjän tiedot html:ksi, jotta ne voidaan näyttää.
match=profile/element[@name='customer']: Tässä muutetaan asiakkaan tiedot html:ksi, jotta ne voidaan näyttää.
match=profile/element[@name='product']: Tässä muutetaan yksittäisen tuotteen tiedot html:ksi, jotta ne voidaan näyttää. Tuotteiden kappalemääriä, yksikköhintoja ja yhteishintoja varten määritellään omat kentät, joiden arvoina on oletusarvot tai käyttäjän jo aikaisemmin antamat uudet arvot.
Lisäksi Tahko-järjestelmän esimerkkisovelluksen toimintojen toteutuksessa on käytetty selainpään Java-skriptiä. Tämä mahdollistaa ennen kaikkea hintojen nopean laskemisen sitä mukaa, kun asiakas muuttaa jonkin osakomponentin hintaa, tai valitsee uuden tuotteen. Toiminta on paljon nopeampaa, kun välillä ei tarvitse tehdä palvelinkutsuja.
Java-skriptin käyttö ei ole kuitenkaan ollenkaan välttämätöntä rakennettaessa sovelluksia Tahko-järjestelmällä.
Kun tarjoukseen lisättyjen tuotteiden hintoja tai kappalemääriä muutetaan, lasketaan yksittäisen tuotteen kokonaishinta, kaikkien tuotteiden kokonaishinta ja alennuksen jälkeen saatava loppuhinta Java-skriptin avulla. Laskemisen suorittaa yksi funktio, joka tekee kaiken tarvittavan laskennan. Funktiota kutsutaan jokaisen tuotteen kappalemäärä-, kappalehinta- ja kokonaishinta- sekä tuotteiden kokonaishinta - ja tarjouksen loppuhinta -kentissä onChange-tapahtuman käsittelyssä. Kokonaishinnoissa kutsutaan funktiota siksi, että ne eivät ole editoitavia kenttiä, joten niihin tehdyt muutokset eivät vaikuta tarjouksen lopulliseen hintaan. Vain tuotteen kappalemäärän, kappalehinnan ja alennusprosentin muuttaminen vaikuttaa tarjouksen loppuhintaan.
Tuotteen lisääminen toimii siten, että tuotehakutuloksena saadusta listasta painetaan Lisää-linkkiä, jolloin kutsutaan addProduct-funktiota lisättävän tuotteen id parametrina. Funktio lisää lisättävän tuotteen id:n tarjouskehyksen lomakkeessa olevaan addProduct-piilokenttään. Tämän jälkeen tarjouslomake lähetetään ja tuote lisätään tarjoukseen.
Tuote poistetaan tarjouksesta painamalla Poista-linkkiä tarjouskehyksessä halutun tuotteen kohdalta, jolloin kutsutaan removeProduct-funktiota parametrina poistettavan tuotteen id. Funktio lisää poistettavan tuotteen id:n tarjouskehyksen lomakkeessa olevaan removeProduct-piilokenttään. Tämän jälkeen tarjouslomake lähetetään ja tuote poistetaan tarjouksesta.
Tuotteen lisääminen toimii siten, että tuotehakutuloksena saadusta listasta painetaan Lisää-linkkiä, jolloin kutsutaan addProduct-funktiota lisättävän tuotteen id parametrina. Funktio lisää lisättävän tuotteen id:n tarjouskehyksen lomakkeessa olevaan addProduct-piilokenttään. Tämän jälkeen tarjouslomake lähetetään ja tuote lisätään tarjoukseen.