Títol: Esquema conceptual de Autor: Adrià Ramirez Papell

Data de lectura: 14 de Juny de 2011

Director: Antoni Olivé Departament: Enginyeria de Serveis i Sistemes d’Informació

Co-director: Albert Tort Departament: Enginyeria de Serveis i Sistemes d’Informació

Centre: Facultat d’Informàtica de Barcelona (FIB) Universitat: Universitat Politècnica de Catalunya (UPC) BarcelonaTech DADES DEL PROJECTE

Títol: Esquema conceptual de Magento

Autor: Adrià Ramirez Papell Titulació: Enginyeria en Informàtica Crèdits: 37,5 Director: Antoni Olivé Ramon Co-director: Albert Tort Pugibet Departament: Enginyeria de Serveis i Sistemes d’Informació

MEMBRES DEL TRIBUNAL (nom i signatura)

Presidenta: Cristina Gómez Seoane

Vocal: Javier Verdu Mula

Secretari (Director): Antoni Olivé Ramon

Codirector: Albert Tort Pugibet

QUALIFICACIÓ

Qualificació numèrica: Qualificació descriptiva:

Data:

1 2 Avui, que presento aquest projecte, vull agraïr el seu support: Als amics de la carrera, de Sant Miquel i de l’Esbart, per tenir la pacièn- cia com a resposta a les meves negatives als plans i activitats que pro- posaven. Als companys de feina, per preguntar insistentment com avançava el projecte i si encara no l’havia acabat. A la família, que m’ha vist desaparèixer hores per dur-lo a terme. A l’Albert Tort, pel suport com a co-director resolent dubtes i ajudant- me a enfocar el projecte, especialment en dies de vacances i caps de setmana, dedicant-hi un esforç superior al que li podia haver demanat.

3 Índex

I Conceptes previs 13

1 Introducció 14 1.1 Context ...... 14 1.1.1 La modelització conceptual ...... 14 1.1.2 Verificació i validació d’esquemes conceptuals ...... 15 1.1.3 Enginyeria inversa ...... 15 1.1.4 Sistemes de comerç electrònic ...... 15 1.2 Motivació del projecte ...... 15 1.2.1 CSTL aplicat a un cas real ...... 15 1.2.2 L’esquema conceptual de l’osCommerce com a precedent ...... 16 1.2.3 L’esquema conceptual de Magento ...... 16 1.2.4 Motivacions personals ...... 16 1.3 Objectius ...... 17 1.4 Metodologia ...... 17 1.4.1 Anàlisi i elaboració de l’esquema conceptual ...... 17 1.4.2 Documentació de la memòria ...... 17 1.4.3 Validació de l’esquema conceptual dissenyat ...... 18 1.4.4 Organització i seguiment del projecte ...... 18 1.5 Estructura de la memòria ...... 19

2 La modelització conceptual 20 2.1 Definició dels conceptes rellevants ...... 20 2.2 La qualitat dels esquemes conceptuals ...... 21 2.3 El principi de necessitat ...... 22

3 Magento i els sistemes de comerç electrònic 23 3.1 El comerç electrònic ...... 23 3.1.1 Les botigues online ...... 23 3.1.2 Ús del comerç electrònic ...... 24 3.1.3 Sistemes de comerç electrònic ...... 24 3.2 Comparació de sistemes de comerç electrònic ...... 25 3.2.1 Objectius ...... 25 3.2.2 Metodologia ...... 25 3.2.3 Comparació ...... 26 3.2.4 Conclusions ...... 32 3.3 Magento ...... 33

II L’esquema 36

4 Conceptual Schema organitzation 37 4.1 Structure overview ...... 37

4 4.2 Structural Schema ...... 37 4.3 Behavioral Schema ...... 38

5 Structural Schema 40 5.1 Scope ...... 40 5.2 Schema presentation ...... 41 5.3 Schema Overview ...... 41 5.4 Store Configuration ...... 44 5.4.1 Stores ...... 44 5.4.2 Store Configuration ...... 46 5.4.3 Currencies ...... 51 5.4.4 Locations ...... 54 5.4.5 Shipping Methods ...... 57 5.4.6 Payment Methods ...... 70 5.4.7 Taxes ...... 82 5.5 Customers ...... 85 5.5.1 Customers ...... 85 5.5.2 Sessions ...... 88 5.6 Store Administration ...... 90 5.6.1 Products ...... 90 5.6.2 Product Options ...... 109 5.6.3 Product Types ...... 112 5.6.4 Categories ...... 118 5.6.5 Attributes ...... 121 5.6.6 Attribute Values ...... 125 5.6.7 Price Rules ...... 130 5.7 Additional Activities ...... 135 5.7.1 Product Tags ...... 135 5.7.2 Product Reviews ...... 137 5.7.3 Buying Process Information ...... 139 5.7.4 Newsletters ...... 143 5.8 Online Catalog ...... 146 5.8.1 Shopping Carts ...... 146 5.8.2 Shopping Cart Items ...... 149 5.8.3 Shopping cart items of non-simple products ...... 155 5.8.4 Orders ...... 161 5.8.5 OrderLines ...... 167 5.8.6 Invoices ...... 169 5.8.7 Shipments ...... 173 5.8.8 Refund ...... 175

6 Behavioral Schema. Use Case Specification 178 6.1 Scope ...... 178 6.2 Schema presentation ...... 178 6.3 Use Case Overview ...... 179 6.4 Store Configuration ...... 184 6.4.1 Add a website ...... 184 6.4.2 Edit a website ...... 184 6.4.3 Delete a website ...... 184 6.4.4 Select the default website ...... 185 6.4.5 Add a store ...... 185 6.4.6 Edit a store ...... 185 6.4.7 Delete a store ...... 185 6.4.8 Select the default store of a website ...... 186

5 6.4.9 Add an store view ...... 186 6.4.10 Edit an store view ...... 186 6.4.11 Delete an store view ...... 186 6.4.12 Select the default store view of a store ...... 187 6.4.13 Select the default language ...... 187 6.4.14 Change the catalog configuration values ...... 188 6.4.15 Change the stock configuration values ...... 188 6.4.16 Change the wishlist configuration values ...... 188 6.4.17 Change the sales configuration values ...... 189 6.4.18 Change the customer configuration values ...... 189 6.4.19 Change the tax configuration values ...... 190 6.4.20 Change the shipping configuration values ...... 190 6.4.21 Change the tell to a friend configuration values ...... 190 6.4.22 Change the currency configuration values ...... 191 6.4.23 Change the enabled currencies ...... 191 6.4.24 Update currency rates ...... 192 6.4.25 Change the geographical configuration values ...... 192 6.4.26 Change shipping method values ...... 193 6.4.27 Change payment method values ...... 193 6.4.28 Add a customer tax class ...... 193 6.4.29 Edit a customer tax class ...... 194 6.4.30 Delete a customer tax class ...... 194 6.4.31 Add a product tax class ...... 194 6.4.32 Edit a product tax class ...... 195 6.4.33 Delete a product tax class ...... 195 6.4.34 Add a tax rate ...... 195 6.4.35 Edit a tax rate ...... 196 6.4.36 Delete a tax rate ...... 196 6.4.37 Add a tax rule ...... 196 6.4.38 Edit a tax rule ...... 196 6.4.39 Delete a tax rule ...... 197 6.5 Customers ...... 198 6.5.1 Create a Customer ...... 198 6.5.2 Change password ...... 198 6.5.3 Change customer details ...... 198 6.5.4 Administrate address book ...... 199 6.5.5 Edit a customer ...... 199 6.5.6 Delete a customer ...... 200 6.5.7 Show account information ...... 200 6.5.8 Show address book ...... 200 6.5.9 Add a customer group ...... 200 6.5.10 Edit a customer group ...... 201 6.5.11 Delete a customer group ...... 201 6.5.12 Add an administrator ...... 201 6.5.13 Edit an administrator ...... 202 6.5.14 Delete an administrator ...... 202 6.5.15 Add a role ...... 202 6.5.16 Edit a role ...... 203 6.5.17 Delete a role ...... 203 6.5.18 Log In ...... 203 6.5.19 Log Out ...... 204 6.5.20 Open session ...... 204 6.5.21 Finish session ...... 204 6.5.22 Change the current Website ...... 205

6 6.5.23 Change the current Store ...... 205 6.5.24 Change the current Store View ...... 205 6.5.25 Change the current Currency ...... 205 6.6 Store Administration ...... 207 6.6.1 Add a product ...... 207 6.6.2 Edit a product ...... 207 6.6.3 Delete a product ...... 208 6.6.4 Add a product category ...... 208 6.6.5 Edit a product category ...... 208 6.6.6 Move a product category ...... 209 6.6.7 Delete a product category ...... 209 6.6.8 Add an attribute ...... 209 6.6.9 Edit an attribute ...... 210 6.6.10 Delete an attribute ...... 210 6.6.11 Add an attribute set ...... 210 6.6.12 Edit an attribute set ...... 211 6.6.13 Delete an attribute set ...... 211 6.6.14 Administrate tier prices of a product ...... 212 6.6.15 Add a catalog price rule ...... 212 6.6.16 Edit a catalog price rule ...... 213 6.6.17 Delete a catalog price rule ...... 213 6.6.18 Add a shopping cart price rule ...... 213 6.6.19 Edit a shopping cart price rule ...... 214 6.6.20 Delete a shopping cart price rule ...... 214 6.7 Additional Activities ...... 215 6.7.1 Tag a product ...... 215 6.7.2 Add a tag ...... 215 6.7.3 Edit a tag ...... 215 6.7.4 Delete a tag ...... 216 6.7.5 Show tags of a customer ...... 216 6.7.6 Show most popular tags ...... 216 6.7.7 Show all tags ...... 217 6.7.8 Add a review ...... 217 6.7.9 Edit a review ...... 217 6.7.10 Delete a review ...... 218 6.7.11 Add a review property ...... 218 6.7.12 Edit a review property ...... 218 6.7.13 Delete a review property ...... 218 6.7.14 Show reviews of a product ...... 219 6.7.15 Show reviews of a customer ...... 219 6.7.16 Add a product to the wish list ...... 219 6.7.17 Remove a product from the wish list ...... 220 6.7.18 Add a product to the compare list ...... 220 6.7.19 Remove a product from the compare list ...... 220 6.7.20 Share wish list ...... 221 6.7.21 Subscribe to a product price alert ...... 221 6.7.22 Subscribe to a product stock alert ...... 221 6.7.23 Show wish list ...... 221 6.7.24 Show ready to compare products ...... 222 6.7.25 Show recently compared products ...... 222 6.7.26 Show recently viewed products ...... 222 6.7.27 Show compare ...... 222 6.7.28 Add a newsletter template ...... 223 6.7.29 Edit a newsletter template ...... 223

7 6.7.30 Delete a newsletter template ...... 223 6.7.31 Add a newsletter ...... 224 6.7.32 Edit a newsletter ...... 224 6.7.33 Subscribe to the newsletter ...... 224 6.7.34 Unsubscribe from the newsletter ...... 225 6.7.35 Delete a newsletter subscription ...... 225 6.7.36 Unsubscribe a newsletter subscription ...... 225 6.7.37 Show shopping cart ...... 225 6.7.38 Download a product ...... 226 6.7.39 Show downloadable products of a customer ...... 226 6.7.40 Tell to a friend ...... 226 6.7.41 View product information ...... 227 6.7.42 Search products ...... 227 6.7.43 Show the best purchased products ...... 227 6.7.44 Reset the temporal information ...... 227 6.8 Online catalog ...... 229 6.8.1 Place an order ...... 229 6.8.2 Checkout an order with multiple addresses ...... 231 6.8.3 Add an order ...... 232 6.8.4 Cancel an order ...... 234 6.8.5 Hold an order ...... 234 6.8.6 Unhold an order ...... 234 6.8.7 Reorder ...... 235 6.8.8 Add a gift message to a placed order ...... 235 6.8.9 Print an Order ...... 235 6.8.10 Send order information by email ...... 236 6.8.11 Show previous orders of a customer ...... 236 6.8.12 Add an invoice ...... 236 6.8.13 Register an invoice payment ...... 237 6.8.14 Cancel an invoice ...... 237 6.8.15 Add a shipment ...... 237 6.8.16 Add a refund ...... 238 6.8.17 Cancel a refund ...... 239 6.8.18 Add a comment to an order, invoice, shipment or refund...... 239 6.9 Store Reports ...... 240 6.9.1 Show the sales report ...... 240 6.9.2 Show the tax report ...... 240 6.9.3 Show the shipping report ...... 240 6.9.4 Show the invoices report ...... 240 6.9.5 Show the refunds report ...... 241 6.9.6 Show the coupon codes report ...... 241 6.9.7 Show the products in carts report ...... 241 6.9.8 Show the abandoned carts report ...... 242 6.9.9 Show the bestseller products report ...... 242 6.9.10 Show the ordered products report ...... 242 6.9.11 Show the most viewed products report ...... 243 6.9.12 Show the stock of products report ...... 243 6.9.13 Show the downloads report ...... 243 6.9.14 Show the new customer accounts report ...... 244 6.9.15 Show the customers by orders total report ...... 244 6.9.16 Show the customers by number of orders report ...... 244 6.9.17 Show the customer reviews report ...... 244 6.9.18 Show the product reviews report ...... 245 6.9.19 Show the tags by customer report ...... 245

8 6.9.20 Show the tags by product report ...... 245 6.9.21 Show the popular tags report ...... 246 6.9.22 Show the search terms report ...... 246

7 Behavioral Schema. Events Specification 247 7.1 Scope ...... 247 7.2 Schema presentation ...... 248 7.3 Events Overview ...... 249 7.4 Store Configuration ...... 256 7.4.1 SetDefaultWebsite ...... 256 7.4.2 SetDefaultStoreOfWebsite ...... 256 7.4.3 SetDefaultStoreViewOfStore ...... 257 7.4.4 SetDefaultLanguage ...... 257 7.5 Customers ...... 259 7.5.1 CreateCustomer ...... 259 7.5.2 PasswordChange ...... 260 7.5.3 EditCustomerDetails ...... 260 7.5.4 NewCustomerAddress ...... 261 7.5.5 DeleteCustomerAddress ...... 262 7.5.6 DefaultDeliveryAddressChange ...... 263 7.5.7 DefaultBillingAddressChange ...... 264 7.5.8 EditCustomerAddress ...... 264 7.5.9 LogIn ...... 265 7.5.10 RestorePreviousShoppingCart ...... 266 7.5.11 RestorePreviousProductLists ...... 267 7.5.12 LogOut ...... 268 7.5.13 SaveCurrentProductLists ...... 269 7.5.14 NewSession ...... 269 7.5.15 DeleteSession ...... 270 7.5.16 SetCurrentWebsite ...... 270 7.5.17 SetCurrentStore ...... 271 7.5.18 SetCurrentStoreView ...... 271 7.5.19 SetCurrentCurrency ...... 272 7.6 Store Administration ...... 273 7.6.1 NewProduct ...... 273 7.6.2 EditProduct ...... 277 7.6.3 DeleteProduct ...... 282 7.6.4 MoveCategory ...... 283 7.6.5 NewAttribute ...... 283 7.6.6 EditAttribute ...... 285 7.6.7 DeleteAttribute ...... 287 7.7 Additional Activities ...... 288 7.7.1 NewReview ...... 288 7.7.2 NewAdministratorReview ...... 289 7.7.3 AddProductToWishlist ...... 290 7.7.4 AddCommentToWishlistItem ...... 291 7.7.5 RemoveCommentFromWishlistItem ...... 291 7.7.6 RemoveProductFromWishlist ...... 292 7.7.7 AddProductToCompareList ...... 293 7.7.8 RemoveProductFromCompareList ...... 294 7.7.9 SingUpForPriceAlert ...... 294 7.7.10 SingUpForStockAlert ...... 295 7.7.11 SignUpForNewsletter ...... 296 7.7.12 SignDownFromNewsletter ...... 297

9 7.7.13 TellToAFriendUsed ...... 298 7.7.14 ProductViewed ...... 299 7.7.15 ResetTemporalInfo ...... 300 7.8 Online catalog ...... 301 7.8.1 ProductViewed ...... 301 7.8.2 AddProductToShoppingCart ...... 302 7.8.3 UpdateShoppingCart ...... 303 7.8.4 ApplyCouponCode ...... 304 7.8.5 OrderConfirmation ...... 304 7.8.6 DuplicateAccount ...... 307 7.8.7 NewAdministrationShoppingCart ...... 308 7.8.8 AddProductToShoppingCart ...... 308 7.8.9 ChangeCurrencyOfAdministrationShoppingCart ...... 310 7.8.10 ChangeEMailAndGroupOfAdministrationShoppingCart ...... 310 7.8.11 ApplyCouponCode ...... 311 7.8.12 AddProductToShoppingCart ...... 311 7.8.13 AddGiftMessage ...... 313 7.8.14 AddGiftMessageToItem ...... 313 7.8.15 OrderConfirmation ...... 314 7.8.16 DeleteAdministrationShoppingCart ...... 316 7.8.17 CancelOrder ...... 317 7.8.18 HoldOrder ...... 317 7.8.19 UnholdOrder ...... 318 7.8.20 Reorder ...... 318 7.8.21 AddInvoice ...... 319 7.8.22 PayInvoice ...... 320 7.8.23 CancelInvoice ...... 321 7.8.24 AddShipment ...... 321 7.8.25 AddRefund ...... 322 7.8.26 CancelRefund ...... 323 7.8.27 AddComment ...... 324

III Anàlisi i validació de l’esquema 325

8 Anàlisi de l’esquema 326 8.1 Comparació amb l’osCommerce ...... 326 8.1.1 Metodologia ...... 326 8.1.2 Conceptes únics del domini de Magento ...... 327 8.1.3 Conceptes únics del domini de l’osCommerce ...... 327 8.1.4 Variació de conceptes en els dos sistemes ...... 327 8.1.5 Conclusions ...... 329 8.2 Millores suggerides a Magento ...... 330

9 Proves automàtiques d’esquemes conceptuals 333 9.1 Un procés de proves genèric ...... 333 9.1.1 Descripció del procés ...... 333 9.1.2 Significat dels resultats obtinguts ...... 334 9.1.3 Avantatges de les proves automàtiques ...... 334 9.2 El procés de proves d’un esquema conceptual ...... 335 9.2.1 Adaptació dels conceptes ...... 335 9.2.2 Estructura d’un cas de test d’esquemes conceptuals ...... 335 9.2.3 Rellevància dins el cicle de desenvolupament de software ...... 336 9.2.4 Modelització conceptual dirigida per tests ...... 337

10 9.3 Entorn proposat ...... 337 9.3.1 USE / USEx ...... 337 9.3.2 El llenguatge CSTL ...... 338 9.3.3 CSTL Processor ...... 338

10 Validació de l’esquema conceptual de Magento 340 10.1 Estratègia de les proves ...... 340 10.1.1 Visió General ...... 340 10.1.2 Descripció de la Fixture ...... 341 10.1.3 Descripció dels casos de test ...... 342 10.2 Resultats de les proves ...... 344 10.2.1 Errors detectats durant l’especificació de l’esquema en USEx ...... 345 10.2.2 Errors detectats en l’execució de les proves automàtiques ...... 346 10.2.3 Conclusions ...... 347 10.3 Avaluació de l’entorn CSTL/USEx ...... 348 10.3.1 Suggeriments de millora per a l’eina USEx ...... 348 10.3.2 Suggeriments de millora per a l’eina CSTLProcessor ...... 349 10.3.3 Errors detectats en l’entorn de test ...... 351

IV Documentació del treball 352

11 Eines utilitzades 353 11.1 Descripció ...... 353 11.1.1 Magic Draw ...... 353 11.1.2 Entorn XML ...... 354 11.1.3 Zend Server ...... 354 11.1.4 Entorn de les proves automàtiques ...... 354 11.1.5 Sistemes operatius ...... 355 11.1.6 Edició de text i documentació ...... 355 11.2 Resum de llicències i versions ...... 356

12 Aspectes organitzatius i econòmics 357 12.1 Planificació temporal ...... 357 12.1.1 Planificació inicial ...... 357 12.1.2 Seguiment real ...... 358 12.2 Valoració econòmica ...... 360 12.3 Possibilitats de treball futur ...... 361

13 Conclusions 362

V Annexos i Bibliografia 367

A Detall en codi CSTL del procés de validació de l’esquema conceptual 368 A.1 Especificació dels esdeveniments validats ...... 368 A.2 Especificació dels casos de test ...... 375 A.2.1 Structural Schema ...... 375 A.2.2 NewSession ...... 376 A.2.3 LogIn ...... 376 A.2.4 AddProductToShoppingCart ...... 378 A.2.5 OrderConfirmation ...... 380 A.2.6 NewProduct ...... 385

11 B Eines desenvolupades de suport al procés de documentació 392 B.1 Estructura dels fitxers de documentació ...... 392 B.2 Fulles de transformació XSLT ...... 397

[Referències Bibliogràfiques] 398

12 Part I

Conceptes previs

13 1 Introducció

El projecte final de carrera que es documenta en aquesta memòria té com a objectiu desenvolu- par l’esquema conceptual d’un sistema de comerç electrònic existent, Magento, a través d’un procés d’enginyeria inversa i, posteriorment, validar-lo amb l’execució de proves automàtiques. Addicionalment, i partint de l’esquema conceptual obtingut, es vol confeccionar una proposta de millores en el sistema i comparar-lo amb l’esquema conceptual d’un altre sistema de comerç electrònic (osCommerce), ja desenvolupat anteriorment. Aquest capítol pretén situar el lector abans d’entrar en la matèria del projecte. D’entrada, la secció 1.1 defineix els conceptes de Modelització Conceptual, Validació, Enginyeria Inversa i Sistemes de Comerç Electrònic, conceptes clau per a la comprensió del projecte. A la secció 1.2 es parla dels motius que han portat a la seva realització. Les seccions 1.3 i 1.4 defineixen els objectius del projecte i la metodologia seguida per tal d’assolir-los. Finalment, la secció 1.5 defineix el contingut de la resta de capítols d’aquesta memòria.

1.1 Context

1.1.1 La modelització conceptual

La modelització conceptual és una activitat necessària en el desenvolupament d’un sistema d’in- formació i té com a objectiu principal definir l’esquema conceptual d’aquest sistema [7]. Un esquema conceptual defineix el coneixement general que un sistema d’informació nec- cessita saber per desenvolupar correctament les seves funcions [8]. En el desenvolupament actual de sistemes d’informació, pren especial rellevància la capacitat que tinguin els enginyers, desenvolupadors finals del sistema, per captar les necessitats de les parts interessades que l’utilitzaran o hi tindran algun tipus d’interacció. S’entén per parts interes- sades totes aquelles persones, organitzacions o altres sistemes que afectin o siguin afectats pel sistema que es vol dissenyar1. Es fan necessàries, doncs, un conjunt d’activitats que facilitin l’entesa entre desenvolupadors i parts interessades, amb l’objectiu d’expressar de manera clara i concreta quines seran les característiques del sistema que es dissenyarà: què n’esperen les parts interessades i què els hi oferirà. A aquestes espectatives les anomenem requisits. La branca de l’enginyeria del software

1En modelització conceptual s’utiliza el terme ’Stakeholder’, definit per R. Edward Freeman a[3].

14 dedicada a la realització, estudi i millora de les activitats a les que ens referim és l’Enginyeria de Requisits (veure [8]). La modelització conceptual s’enmarca dins de l’Enginyeria de Requisits: la modelització con- ceptual persegueix definir el coneixement que el sistema ha de tenir per a respondre a aquestes expectatives. La conceptualització d’aquest coneixement existeix sempre, tot i que en molts casos només resideix en la ment dels seus desenvolupadors [8]. És important, però, representar aquest coneixement de manera explícita, amb un esquema. Fer-ho, obliga a un anàlisi més profund del sistema i converteix el resultat en una font d’informació on resideix l’essència d’allò que el sistema coneix i és capaç de realitzar [8].

1.1.2 Verificació i validació d’esquemes conceptuals

Un aspecte clau de la modelització conceptual és comprovar que l’esquema dissenyat conté tot el coneixement que necessita i que tot el coneixement que conté és correcte i rellevant. En termes generals, parlem d’evaluar la qualitat de l’esquema conceptual. Aquesta evaluació ha d’involucrar les parts interesssades, que usualment no coneixen el llenguatge amb què s’especifica l’esquema. En l’actualitat s’utilitzen múltiples tècniques per a aquesta tasca, la majoria basades en la inspecció i revisió manual de l’esquema dissenyat. Si l’esquema conceptual desenvolupat s’expressa en un llenguatge formal i es dota d’exe- cutabilitat, l’evaluació de l’esquema dissenyat es pot dur a terme a través del disseny de testos automàtics, de naturalesa semblant als testos de software que ja s’utilitzen habitualment en els cicles de desenvolupament (veure [17]). Les tècniques exposades poden incidir en dos aspectes qualitatius de l’esquema: la veri- ficació i la validació. En el primer cas, es vol assegurar la coherència interna de l’esquema dissenyat. En el segon, l’objectiu és assegurar que l’esquema representa acuradament el coneix- ement rellevant i necessari, que ha set expressat per les parts involucrades.

1.1.3 Enginyeria inversa

Tal com es defineix a [1], l’enginyeria inversa és el procés d’analitzar un sistema per identificar-ne els components i les seves interrelacions i crear representacions del sistema en una altra forma o en un nivell més alt d’abstracció. L’objectiu de l’enginyeria inversa és entendre un sistema software amb la intenció de facilitar-ne la millora, correcció, documentació i redisseny.

1.1.4 Sistemes de comerç electrònic

Un sistema de comerç electrònic és un sistema d’informació que automatitza algunes parts del procés de compra o de venda de productes o serveis a través de mitjans electrònics, el principal dels quals és Internet. Magento és un sistema de comerç electrònic comercial molt rellevant en el seu segment. Tot i tractar-se d’un producte comercial, el seu codi font és obert i compta amb una àmplia comunitat de desenvolupadors que n’extenen les seves possibilitats a través d’extensions.

1.2 Motivació del projecte

1.2.1 CSTL aplicat a un cas real

Alguns aspectes d’aquest projecte s’enmarquen en la recerca feta pel Grup de Recerca en Mod- elització Conceptual2, del departament d’Enginyeria del Software i Sistemes d’Informació de la

2Web del GMC: http://guifre.lsi.upc.edu/index.html

15 UPC. Des d’aquest grup, i especialment en el marc de la tesi doctoral de l’Albert Tort, s’està treballant en l’aplicació de la metodologia de testos automàtics, utilitzada actualment per a la validació del software en fase de desenvolupament, a la fase d’especificació (veure [17]). Amb aquesta finalitat, s’han desenvolupat les següents eines:

I CSTL, un llenguatge especialitzat per a escriure proves automàtiques d’esquemes concep- tuals.

I CSTLProcessor, un prototip d’aplicació que permet executar les proves definides. La necessitat de validar, també, les eines desenvolupades, aconsella la seva utilització en la validació d’un esquema conceptual real i elaborat de forma independent a la creació d’aquestes eines.

1.2.2 L’esquema conceptual de l’osCommerce com a precedent

El departament d’Enginyeria del Software i Sistemes d’Informació de la UPC disposa ja de di- versos esquemes conceptuals. El més rellevant per aquest projecte és l’esquema elaborat l’any 2007, també com a projecte final de carrera (veure [15]). L’esquema correspon a un sistema de comerç electrònic, osCommerce, i s’ha utilitzat com a exemple en diverses publicacions del departament3. Escollir ara un sistema de comerç electrònic per elaborar-ne el seu esquema conceptual, permet recollir i reutilitzar part del coneixement adquirit en l’elaboració de l’esquema de osCom- merce. A més, disposar de dos esquemes conceptuals que modelitzen un coneixement proper (o coincident en més d’un aspecte), permet comparar-los. En termes del coneixement que repre- senten, la comparació de dos esquemes conceptuals és equivalent a una comparació entre els dos sistemes corresponents. El projecte actual pren com a referència l’esquema conceptual de l’osCommerce i vol mantenir- hi diverses similituds. Aquells conceptes del domini que apareguin en els dos sistemes seran modelitzats intentant preservar el màxim la capacitat de comparació entre els dos esquemes con- ceptuals. El format d’organització i presentació de l’esquema conceptual també s’ha mantingut. En la redacció de la memòria, el projecte s’ha tractat com un element bibliogràfic més, citant- ne les definicions oportunes quan l’autor ho ha cregut necessari.

1.2.3 L’esquema conceptual de Magento

Magento és un sistema open-source, i a més permet als usuaris el desenvolupament d’exten- sions que n’ampliïn les seves funcionalitats. Per aquest motiu, hi ha una comunitat considerable de programadors, professionals o ama- teurs, que desitgen modificar, corregir o ampliar aspectes del sistema. Malgrat això, o potser pre- cisament per l’ús d’aquesta metodologia, no existeix cap document accessible que en defineixi el seu esquema conceptual. Cobrint aquest buit, es facilitaria a la comunitat de desenvolupadors la comprensió i modificació del sistema.

1.2.4 Motivacions personals

Com a autor del projecte, tenia especial interès en dedicar el meu PFC a l’àmbit de la mo- delització conceptual i l’enginyeria de requisits. L’experiència com a alumne a l’assignatura d’Enginyeria de Requisits, així com el contacte amb els professors d’aquesta i actuals director i co-director del projecte, ho afavorien. La proposta del projecte va sorgir d’ells, però encaixava en les meves expectatives i tenia interès en treballar-hi. 3El fet que l’esquema conceptual de osCommerce fos dissenyat per l’autor de les eines de validació de l’esquema el desaconsella, en termes d’independència, per a posada en pràctica d’aquestes eines (veure secció 1.2.1).

16 1.3 Objectius

Els principals objectius del projecte són: 1. Desenvolupar, a través d’un procés d’enginyeria inversa, l’esquema conceptual que repre- senta el coneixement que requereix el sistema Magento. 2. Validar l’esquema desenvolupat a través de l’execució de proves automàtiques. 3. Utilitzar el coneixement adquirit durant el disseny de l’esquema per a la l’assoliment dels següents sub-objectius:

(a) Analitzar possibles millores i propostes per al sistema Magento. (b) Utilitzar l’esquema conceptual de Magento com a eina per a la comparació d’aquest sistema amb osCommerce.

4. Posar en pràctica el llenguatge i eines proposades per a l’assoliment de l’objectiu 2, detectant- ne possibles aspectes de millora.

1.4 Metodologia

1.4.1 Anàlisi i elaboració de l’esquema conceptual

La metodologia utilitzada per a l’elaboració de l’esquema conceptual ha seguit les línies mar- cades pel projecte de l’esquema conceptual d’osCommerce (veure [15]): s’ha seguit un procés iteratiu que combinava l’anàlisi del sistema estudiat, l’aprenentatge dels llenguatges i metodolo- gies de modelització utilitzats i l’elaboració de l’esquema conceptual. L’esquema conceptual s’ha dissenyat a través d’un procés d’enginyeria inversa, a partir de les següents activitats:

I Experimentació amb el sistema

I Interpretació de la documentació disponible

I Anàlisi de la base de dades del sistema Les diferents parts de l’esquema conceptual s’han elaborat per estricte ordre cronològic: s’ha dissenyat d’entrada l’esquema estructural, s’han especificat posteriorment els casos d’ús, i final- ment els esdeveniments de domini. S’ha decidit limitar l’abast de l’esquema de comportament elaborat (que inclou casos d’ús i esdeveniments), centrant l’estudi en els casos d’ús i esdeveniments més rellevants. Els criteriss per a aquesta limitació es detallen a les seccions 5.1 i 6.1. Un cop s’ha disposat d’una versió prou refinada de l’esquema conceptual, s’ha decidit iniciar la fase de proves. A partir d’aquest punt, l’esquema conceptual només s’ha modificat per a solventar els errors detectats durant aquesta fase.

1.4.2 Documentació de la memòria

L’esquema conceptual s’ha documentat utilitzant les eines següents (veure capítol 11):

I fitxers XML per a la documentació textual: especificació de les restriccions, regles de derivació i operacions, així com la descripció textual. L’estructura de les fitxes ha estat dissenyada específicament per al projecte.

I MagicDraw per als esquemes gràfics.

17 S’han desenvolupat també fulles de conversió XSLT que presentaven la informació de les fitx- es en pàgines HTML i documents LATEX. Les pàgines HTML han servit per a ús intern durant l’execució del projecte. Els documents LATEX resultants, en canvi, conformen els capítols 5, 6 i 7 d’aquesta memòria. Aquests capítols, més el capítol 4, s’han redactat en anglès amb la intenció d’incloure’ls a l’espai que la web oficial de Magento dedica al la comunitat de desenvolupadors4. Els capítols restants s’han redactat amb l’eina LYX, un editor d’arxius LATEX que proporciona una interfície “What you see is what you get” a l’usuari i permet la integració amb arxius .tex.

1.4.3 Validació de l’esquema conceptual dissenyat

Durant la fase de desenvolupament, s’ha validat l’esquema conceptual a partir d’activitats d’in- specció, revisió i contrast amb el comportament real del sistema analitzat. Posteriorment, s’ha dut a terme una fase de proves, que ha consistit en les següents activitats:

1. Especificar l’esquema dissenyat en llenguatge USE, permetent que la eina USEx el pugui interpretar (veure capítol 11). 2. Dissenyar el pla de proves: definir les proves que es podrien aplicar a l’esquema elaborat i seleccionar aquelles que finalment es portaran a terme. 3. Executar les proves.

Les activitats 1 i 3 han permès detectar errors a l’esquema conceptual dissenyat, que han estat corregits de forma paral·lela a l’activitat corresponent. El disseny de les proves executades s’ha fet seguint la metodologia definida pel departament i detallada als capítols 9 i 10. L’especificació completa d’aquesta metodologia es pot consultar a [17].

1.4.4 Organització i seguiment del projecte

Abans d’iniciar el projecte, l’autor, el director i el co-director van definir-ne els objectius i les fases descrites a continuació:

1. Adquisició de coneixement: anàlisi dels sistemes de comerç electrònic, experimentació amb el sistema i anàlisi de la documentació disponible, del codi i de la base de dades.

2. Elaboració de l’esquema conceptual: desenvolupament de les diverses parts de l’esque- ma.

3. Proves: transcripció de l’esquema a un entorn executable, definició del pla de proves i execució de les proves.

4. Anàlisi de l’esquema: elaboració de la proposta de millores i comparació amb osCom- merce.

5. Elaboració de la memòria: redacció de la documentació, revisió d’errors i maquetació del document.

6. Preparació de la lectura. Tal i com es detalla a la secció 12.1, algunes d’aquestes fases s’han realitzat en paral·lel, sovint perquè la matèria ho exigia.

4Concretament, la web oficial de Magento ofereix un fòrum i una Wiki perquè els desenvolupadors puguin compartir qualsevol tipus d’informació o documentació relacionada amb el sistema. Fòrum de Magento: http://www.magentocommerce.com/boards/. Wiki de Magento: http://www.magentocommerce.com/wiki/.

18 Amb el projecte ja iniciat i la tercera fase relativament avançada, es va elaborar un informe, requerit per la normativa de la Facultat d’Informàtica de Barcelona5, on es definien els objectius, les tasques que restaven per desenvolupar i la seva planificació temporal. Al llarg de tota la realització del projecte, s’han anat establint reunions periòdiques entre l’autor i el co-director. Durant la fase d’elaboració de l’esquema conceptual, aquests contactes han servit per enfocar el projecte i controlar-ne la seva evolució. També s’han fet reunions amb el director del projecte en aquest sentit. Durant la fase de proves, les reunions amb el co-director s’han centrat, a més de les activitats descrites anteriorment, en la resolució dels reptes que plantejava la utilització de les eines de test.

1.5 Estructura de la memòria

La memòria d’aquest projecte s’ha estructurat en diverses parts, capítols, seccions i subseccions.

Part I: Conceptes previs La primera part de la memòria defineix conceptes relacionats amb l’àmbit del projecte. Després de la introducció del capítol actual, el capítol 2 aprofundeix en la definició de la modelització conceptual. El capítol 3 defineix el comerç electrònic i analitza els sistemes disponibles al mercat i, final- ment, el sistema analitzat en aquest projecte: Magento.

Part II: L’esquema La feina realitzada per a l’assoliment dels objectius d’aquest projecte es documenta en aquesta part i la següent. El capítol 4 defineix l’estructura en la qual s’especificarà l’esquema dissenyat, que es descriu al llarg dels capítols 5, 6 i 7. Tots els capítols d’aquesta part estan redactats en anglès.

Part III: Anàlisi i validació de l’esquema L’anàlisi de possibles millores i la comparació amb osCommerce es documenten en el capítol 8. Seguidament, el capítol 9 defineix la metodologia de proves d’esquemes conceptuals. L’apli- cació d’aquesta metodologia al projecte que ens ocupa, i els resultats obtinguts, es documenten al capítol 10.

Part IV: Documentació del treball En la tercera part es documenten aspectes complementaris a la realització del treball. Així, els capítols 11 i 12 descriuen les eines utilitzades i la planificació del projecte, així com les possibilitats de treball futur. Finalment, les conclusions del treball es documenten al capítol 13.

Part V: Annexos i Bibliografia L’última part tanca el treball amb dos annexos i les referències bibliogràfiques consultades. En l’annex A, es documenten les proves automàtiques realitzades en el capítol 10. L’annex B detalla algunes eines de suport al procés de modelització desenvolupades per l’autor d’aquest projecte.

5La normativa de PFC’s per a l’Enginyeria Informàtica de pla 2003 es pot consultar a www.fib.upc.edu/fib/estudiar- enginyeria-informatica/enginyeries-pla-2003/PFC.html

19 2 La modelització conceptual

Aquest capítol aprofundeix les nocions sobre modelització conceptual donades a la introducció. A la secció 2.1 es defineixen els conceptes més rellevants relacionats amb la matèria1. La secció 2.2 defineix què s’entén per qualitat d’un esquema conceptual i la utilització de les proves automàtiques per a validar-la. Finalment, la secció 2.3 justifica la necessitat de definir l’esquema conceptual d’un sistema d’informació.

2.1 Definició dels conceptes rellevants

En la definició de modelització conceptual donada en la secció 1.1.1 es parla del desenvolupa- ment dels sistemes d’informació. Un sistema d’informació es pot definir com a: “un sistema dissenyat per un enginyer que recull, emmagatzema, processa i distribueix informació sobre un determinat domini”. Es consid- era que qualsevol sistema d’informació té tres funcions principals:

I Memòria: mantenir la representació de l’estat en què es troba el domini.

I Informativa: proporcionar informació sobre l’estat en què es troba el domini.

I Activa: portar a terme accions que canviïn l’estat del domini. Un sistema d’informació interpreta la informació amb la qual tracta, i aquesta informació corres- pon a el coneixement d’un domini determinat. El domini, també anomenat sistema objecte o univers de discurs, és un conjunt d’objectes reals i relacions entre aquests objectes, que conformen la realitat amb la que el sistema d’infor- mació tracta. Per la majoria dels sistemes d’informació el domini és una organització, però es podria tractar d’un vehicle, l’atmosfera o una partida d’escacs. Entenem com a base d’informació la representació de les entitats d’un determinat domini i de les relacions entre aquestes entitats, i la seva classificació en tipus. La base d’informació no existeix físicament. És una descripció abstracta que ilustra un estat particular del domini. L’esquema conceptual d’un domini descriu els conceptes utilitzats per explicar aquell domini. Els conceptes són creats a la ment humana, i permeten classificar els objectes i relacions reals.

1Les definicions donades en aquest capítol han estat extretes, o inspirades, en el llibre de l’Antoni Olivé [8].

20 L’esquema conceptual d’un sistema d’informació descriu el subconjunt de conceptes del do- mini que estan representats en aquell sistema d’informació. Al capítol 4 es descriu en detall l’estructura d’un esquema conceptual. Per al present projecte, entenem com a model conceptual la forma en la qual modelitzem (conceptualitzem) el domini. Assumir que la conceptualització d’un domini estarà formada per entitats i relacions entre aquestes entitats és escollir una forma de conceptualitzar aquest esque- ma. És possible que el mateix domini es pugui modelitzar com a un conjunt de fets i proposicions lògiques. El terme model conceptual també s’utilitza sovint com a sinònim d’esquema conceptual, o també per designar el conjunt de l’esquema conceptual més la base d’informació. Finalment, es necessita definir un llenguatge de modelització conceptual amb el qual poder expressar els esquemes. En la majoria dels casos, i també en aquest projecte, els es- quemes conceptuals s’expressen en Unified Modelling Language (UML).

2.2 La qualitat dels esquemes conceptuals

La qualitat d’un esquema conceptual és el grau en el qual l’esquema té unes determinades propietats que li permeten complir la seva funció amb eficàcia [5]:

I Completesa: un esquema conceptual ha de conèixer tots aquells aspectes del domini que descriu que són rellevants per al sistema d’informació. En la mesura en què algun coneixement no quedi descrit a l’esquema, el corresponent sistema d’informació no podrà desenvolupar les seves funcions com és degut.

I Correctesa: el coneixement que descriu un esquema conceptual ha de ser cert i rellevant per a les funcions que el sistema ha de dur a terme.

I Correctesa sintàctica: l’esquema ha de respectar les regles del llenguatge amb el qual està escrit.

I Comprensibilitat: tots els actors afectats per l’esquema han de ser capaços d’entendre correctament la part de l’esquema que és rellevant per a ells.

I Simplicitat: són preferibles els esquemes que expressin el mateix coneixement utilitzant menys construccions o més simples, doncs els esquemes més simples són també més comprensibles.

I Principi de conceptualització: un esquema conceptual ha d’incloure només aspectes conceptualment rellevants. Aspectes com la representació externa de les dades o la seva organització interna no s’inclouran en un esquema conceptual.

L’especificació de l’esquema conceptual en un llenguatge executable permet validar-ne la propi- etat de correctesa sintàctica. Així, dotar d’executabilitat un esquema no és només una activitat necessària per a la realització de proves automàtiques, sinó que li és complementària.

D’altra banda, l’execució de les proves automàtiques persegueix validar les propietats de com- pletesa i correctesa.

21 2.3 El principi de necessitat

Per desenvolupar un sistema d’informació és necessari definir el seu esquema conceptual [8]. Tot sistema d’informació encarna un esquema conceptual. Fent servir un símil amb altres branques de l’enginyeria, també tot edifici o màquina encarna els plànols o el disseny previ que se n’ha fet abans de construir-lo. Quedi expressada en un document de forma explícita i en un llenguatge més o menys formal, o simplement en la ment de l’enginyer, qualsevol projecte d’enginyeria necessita una fase prèvia d’especificació i disseny. La formalització per escrit i la estandarització d’aquest disseny comporta millores evidents en la qualitat final del sistema d’informació dissenyat.

22 3 Magento i els sistemes de comerç electrònic

L’objectiu d’aquest capítol és introduïr alguns conceptes relacionats amb el domini del sistema que es modelitza en aquest projecte. En la secció 3.1 es presenta el comerç electrònic i la seva situació actual. La secció 3.2 és el resultat d’un anàlisi comparatiu de diversos sistemes de comerç electrònic. Magento, el sistema escollit per al projecte, es descriu a la secció 3.3.

3.1 El comerç electrònic

3.1.1 Les botigues online

Es defineix el comerç electrònic com al procés de compra i venda de productes i serveis a través de mitjans electrònics, el principal dels quals és Internet. A la web es poden trobar multitud de llocs web que són o inclouen “botigues online”, és a dir, punts de venda als quals un internauta pot accedir per fer-hi les seves compres. És molt habitual trobar negocis o botigues tradicionals ofereixen la venda per Internet com a un complement. En presentem alguns exemples1:

www.ford.com eu.thenorthface.com/hp-eu-en www.kipling.com/int

www.y-3store.com shopfr.samsungmobile.com www.custo-barcelona.com/ca

shop.timeout.com boutique.nespresso.com/kr/capsules us.buyolympus.com

1Tots els casos detallats funcionen amb el sistema Magento. La llista completa es pot consultar a www.magentocommerce.com/showcase

23 També existeixen negocis enfocats en la pràctica totalitat a aquest tipus de venda. Un dels exemples més clars és la venda de bitllets d’avió per Internet.

3.1.2 Ús del comerç electrònic

L’ús d’Internet és cada vegada més extès i, amb ell, la pràctica de realitzar compres a través de la xarxa. Només a Catalunya, el 68,3 % de llars tenen accés a Internet i el 37,6 % l’han utilitzat alguna vegada per fer compres (veure [13]). Repetint una situació que ja és habitual, la tendència d’aquests indicadors en els últims anys és de créixer a un ritme moderat i constant:

% de persones de 16 a 74 anys 2006 2007 2008 2009 2010 Ús d’Internet en els darrers tres mesos 53,2 56,2 64,2 66,0 71,8 Compres per Internet en els darrers tres mesos 13,5 18,3 16,2 19,8 22,6

Taula 3.1: Font: [13].

Així doncs, considerant que la població de Catalunya és aproximadament de 7,5 milions d’habitants, en els tres mesos anteriors a l’estudi van comprar per Internet 1,7 milions de per- sones aproximadament. El volum total de vendes per Internet a l’estat espanyol va ser de 7.760 milions d’euros l’any 2009, segons es pot consultar a [2]. Les compres de productes o serveis més habituals a Catalunya són (veure[13]):

I Bitllets d’avió i tren (55,3 % dels usuaris que han comprat per internet en els darrers 3 mesos han comprat, entre d’altres, bitllets d’avió i tren).

I Reserves d’allotjaments turístics (55,2 %).

I Entrades d’espectacles (46,1 %).

I Roba i material esportiu (27,2 %).

I Equipament electrònic (20,2 %).

I Llibres, revistes, diaris, etc. (18,6 %).

Habitualment, però, els sistemes que suporten el comerç electrònic relacionat amb el turisme (bitllets d’avió, reserves d’allotjaments, etc.) tenen unes necessitats més específiques i queden fora de l’abast dels sistemes que analitzarem en aquest projecte (veure secció 3.1.3).

3.1.3 Sistemes de comerç electrònic

Les botigues online estan gestionades per un sistema informàtic, que desenvolupa algunes de les funcions del cicle de compra de manera automàtica. Les següents funcions són les més habituals:

I Oferir un catàleg actualitzat de productes o serveis que els clients poden consultar. L’ad- ministrador de la botiga hauria de poder modificar aquest catàleg.

I Registrar les dades dels clients, inclosa l’adreça d’enviament a la qual volen rebre els productes comprats.

I Registrar els detalls de la compra.

I Automatitzar el pagament a través de serveis com PayPal o per targeta de crèdit.

I Automatitzar l’enviament dels productes a través de serveis de transport especialitzats.

24 I Opcionalment, poden oferir altres possibilitats de publicitat, recollida de preferències dels clients, gestió de factures i devolucions, etc. Complementàriament, el negoci que ofereix la botiga online desenvolupa aquelles activitats no automatizades, sovint lligades al manteniment del portal web o a la gestió d’incidències. El sistema informàtic que gestiona la botiga online pot ser dissenyat específicament per a cada cas. En la majoria dels casos, però, és possible definir un sistema genèric i configurable que satisfaci la necessitat d’un ampli ventall de negocis. En el moment de crear una nova botiga online, no és necessari definir i implementar un sistema nou sinó adaptar el sistema genèric a cada cas partircular. Tal i com es comentava a la secció 3.1.2, casos com la venda de bitllets d’avió o l’allotjament turístic tenen uns requisits molt específics (gestionar canvis de tarifes de manera intensiva, per exemple). Per aquest motiu, s’acostumen a desenvolupar sistemes ad-hoc, que no analitzarem en aquest projecte.

3.2 Comparació de sistemes de comerç electrònic

Prèviament a l’inici de l’elicitació de l’esquema conceptual, es va realitzar un anàlisi comparatiu dels diferents sistemes de comerç electrònic existents. En aquesta secció, es detallen els objec- tius, la metodologia utilitzada per a la comparació i els resultats i conclusions obtinguts (3.2.1, 3.2.2, 3.2.3 i 3.2.4 respectivament).

3.2.1 Objectius

L’anàlisi s’ha realitzat amb els següents objectius:

I Obtenir informació sobre els sistemes de comerç electrònic existents i la seva rellevància en el mercat.

I Escollir el sistema que representi un cas d’estudi més rellevant per a l’elicitació del seu esquema conceptual.

3.2.2 Metodologia

Criteris S’han establert 8 criteris rellevants per a l’anàlisi de cada sistema:

Rellevància Popularitat a la web: nombre de descàrregues, índex de resultats a google, presència en fòrums especialitzats... Existència de botigues online basades en el sistema.

Capacitat d’observació Possibilitat de consultar obertament el codi font (sistemes open-source). Existència d’una versió de demostració, utilitzable de manera on-line. Possibilitat d’accedir al disseny de la base de dades.

Altres dades d’interès Llenguatge de programació i sistema de la base de dades. Observacions (impressions personals, existència de documentació, utilitat del treball per a la comunitat i d’altres)

25 Inicialment, s’han pre-seleccionat 8 sistemes segons criteris de popularitat a la web i en les observacions de l’usuari. Posteriorment, s’han analitzat la resta de criteris per aquells sistemes pre-seleccionats. L’anàlisi també ha tingut en compte si el sistema analitzat guardava algun tipus de relació amb osCommerce, doncs és un aspecte rellevant per a l’anàlisi comparatiu que es veurà al capítol 8.

Fonts d’informació La llista de sistemes per analitzar s’ha confeccionat a través de la informació de diverses pàgines o articles de la web:

I Article de ItwebExperts: www.itwebexperts.com

I Pàgines de amb recull d’informació sobre diferents Sistemes de Comerç Electrònic:

– www.free-carts.com – www.comparatif-ecommerce.com – www.shopping-cart-reviews.com

I Llista a la Sistemes de Comerç Electrònic gratuïts (Wikipedia):

– en.wikipedia.org/wiki/List_of_Open_Source_eCommerce_Software

Posteriorment, s’ha consultat informació de cada sistema a les respectives pàgines oficials.

Abast L’anàlisi presentat en aquesta secció es va dur a terme el mes de Febrer de l’any 2010. Per tant, reflecteix la situació observada a l’inici d’aquest projecte. En el moment de presentar la memòria d’aquest projecte, és possible que part de la informació hagi quedat obsoleta.

3.2.3 Comparació

Priorització dels sistemes La popularitat de cada sistema ha set una de les dades més difícils de contrastar. No s’ha trobat cap recull de dades independent sobre número de descàrregues o de botigues on-line que utilitzéssin cada sistema. S’ha decidit, finalment, utilitzar com a referència el nombre d’entrades a Google per a cada sistema, utilitzant el seu nom com a paraula clau. Si bé aquesta és una dada totalment aproxi- mativa (de fet, varia segons la filial geogràfica de google on es fa la cerca), dóna una idea clara i comparable de la popularitat del sistema. En la majoria d’articles i llocs web dedicats al tema, es constata que els sistemes dels que més es parlava coincideixen amb els que més entrades tenen al buscador Google. Segons la popularitat, es defineixen 3 grups:

I Més de 5 milions d’entrades: Magento, ZenCart, X-Cart i VirtueMart.

I Més d’un milió: CRE Loaded, PrestaShop i cubeCart.

I Menys d’un milió: OSCMax, NopCommerce, osCSS, uberCart i Batavi. Per als sistemes amb més d’un milió d’entrades a Google, s’ha buscat la informació corresponent als criteris establerts en la secció 3.2.2. Addicionalment, s’ha realitzat aquest anàlisi per a Batavi, que presenta algunes particularitats interessants.

26 Magento S’han obtingut les dades següents per al sistema Magento (web oficial: www.magentocommerce.com).

Rellevància - 1.5 Milions de downloads - 7.55 milions d’entrades a Google - Diverses empreses independents que treballen en la construcció de ’botigues magento’ (llista de les botigues franceses a www.blog-ecommerce.com/devis-magento - 287,862 visites al dia - Llista de botigues on-line que l’utilitzen a la web oficial: www.magentocommerce.com/showcase. - 12 grans empreses, amb explicacions detallades de cada cas: TimeOut, Homedics, TCHO, Myla, Wander... - 3 ’case studies’ molt detallats de com algunes empreses han adaptat Magento al seu negoci: Warehouse, PlanetOrganic UK i Eastwood. - Llista addicional de botigues que utilitzen Magento: www.exceedonline.co.nz/eCommerce-Software/Who-Uses-Magento.

Capacitat d’observació Open source, dues versions: - Community edition: totalment OpenSource (llicència OSL 3.0) - Enterprise edition: desenvolupament Osource, llicència comercial, 8900 $ / any, característiques addicionals, suport tècnic de l’empresa. - Demos online: la entreprise edition i community edition. En tots els casos, es pot accedir a la botiga i a l’interfície d’administrador: www.magentocommerce.com/demo. - Instal·lable? Sí, provat personalment. Accés a la BD si tenim el sistema instal·lat.

Altres dades d’interès PHP + mySQL. - Wiki per a la comunitat: conté un apartat ’Magento Architecture’. Molt pobre. - Rumors d’una versió Magento Light per a petits comerciants – Interfície d’administrador molt clara i senzilla. – Bastant nou (2007) – Recomanada per a sistemes robustos i grans empreses amb capacitat de invertir-hi diners.

ZenCart S’han obtingut les dades següents per al sistema ZenCart (web oficial: www.zen-cart.com).

Rellevància - 8,85 milions d’entrades a google - 94,307 visites al dia - llista de webs que donen ’hosting’ amb zenCart incorporat a:www.zen-cart.com Llista de botigues que l’utilitzen i s’han adscrit a la web oficial (4250): www.zen-cart.com/index.php?main_page=showcase

27 Capacitat d’observació OpenSource, gratuït, llicència GPL. - Demos online: front-end (botiga) a la pàgina oficial demo.opensourcecms.com/zencart/, back-end (gestió de l’administrador) a www.comparatif-ecommerce.com/comparatif-ecommerce (no-oficial) - Instal·lable? Sí, provat personalment. - Accés a la BD si tenim el sistema instal·lat. - Documentació de l’esquema conceptual de la BD a upload.wikimedia.org/wikipedia/commons/f/f6/ZenCart_DB_Schema.jpg

Altres dades d’interès PHP + mySQL. – Desenvolupat a partir de osCommerce. El paquet base ja inclou d’entrada moltes característiques que a osCommerce eren extensions. Per contra, ha evolucionat com a un producte diferent i les noves extensions de osCommerce no són compatibles. – Pàgina oficial molt simple. – Funciona des de 2003. La última actualització és del 2007. Qualificada de ’madura’ i ’estable’ en diversos articles. (exemple: http://frolickingfrog.wordpress.com/2009/01/03/zen-cart- premiere-open-source-e-commerce-solution/ ) – Recomanada per a petites empreses, més senzilla. – Documentació bastant extensa: esquema de la BD i documentació de tot el codi PHP (classes, funcions, atributs, etc.) a www.zen-cart.com/docs/phpdoc-1-3-8/

X-Cart S’han obtingut les dades següents per al sistema X-Cart (web oficial: www.x-cart.com).

Rellevància - 350.000 downloads de la versió d’avaluació - 21,042 visites diàries a la web oficial - diverses webs de hosting i creació de botigues ( www.x-shops.com , marketplace.x-cart.com/hosting/ ). - Té 98 M d’entrades al google però moltes no estan relacionades. La entrada « x-cart ecommerce » dóna uns 3 M d’entrades, un resultat semblant a « Magento ecommerce » o « ZenCart ecommerce ». No s’ha trobat cap llista oficial de botigues adscrites.

Capacitat d’observació Té licència propietària, però es pot descarregar una versió gratuïta i amb el codi obert « for evaluation purposes only ». - Demo online: completa al lloc web oficial. Botiga (xcart-demo.qtmsoft.com/demo/home.php) i portal d’administració (xcart-demo.qtmsoft.com/demo/admin/home.php). - Instal·lable? Sí segons la web, però no ha estat provat personalment. Accés a la BD si tenim el sistema instal·lat.

28 Altres dades d’interès PHP + mySQL. – Suport empresarial – Múltiples extensions i productes relacionats a la venta – Documentació bona, però orientada al client-administrador, no al desenvolupador. – Experiència des del 2000. – Portal d’administració molt complet, potser excessivament.

VirtueMart S’han obtingut les dades següents per al sistema VirtueMart (web oficial: http://virtuemart.net).

Rellevància - 9,85 milions d’entrades a google - 77,062 visites diàries a la web oficial - Llista de 1401 botigues inscrites a la web oficial. - Llista no oficial de botigues franceses: www.boutiques-virtuemart.com.

Capacitat d’observació Opensource, gratuït, llicència GPL. Demo online: - A la web oficial, però la part d’administrador dóna problemes d’accés. - Demo no oficial d’una versió anterior (2008): Botiga: (link) i Administració (link) Instal·lable? Sí segons la web, però no ha estat provat personalment. Accés a la BD si tenim el sistema instal·lat.

Altres dades d’interès PHP + MySQL. Necessita Joomla, un ’content management system’ (CMS) opensource i molt popular. Documentació: – Manual del desenvolupador i d’usuari (no conté info sobre la estructura de la BD). – Alguns apunts de bones pràctiques de programació i d’actualització del sistema – Codi php amb documentació (auto-generada). Descriu funcions i classes (si n’hi ha) de cada fitxer. – Apartat de la comunitat molt pobre. Opinions d’articles a internet: – Per a llocs web petits i mitjans (segons wikipedia) – Codi poc net i difícil de modificar (Font1 i Font2) – Difícil d’instal·lar i mal documentat (Font ) – Permet la construcció de llocs web més complexos que englobin la pròpia botiga (Font)

PrestaShop S’han obtingut les dades següents per al sistema PrestaShop (web oficial: www.prestashop.com).

29 Rellevància - 1,38 M entrades google - 19.813 visities diàries a la web oficial - 40.000 usuaris Llista de botigues a la web oficial. Alguns exemples: www.madeleinemarket.com i www.headict.com. Llista no oficial de la comunitat francesa (400 botigues): prestashop.free.fr

Capacitat d’observació Opensource, llicència OSL 3 i gratuït - Demo online completa a la pàgina oficial (link). - Instal·lable? Sí segons la web, però no ha estat provat personalment. Accés a la BD si tenim el sistema instal·lat.

Altres dades d’interès PHP + MySQL + Ajax (per a a la interfície). – Interfície d’admin senzilla: és fàcil i agradable però permet poques opcions. – Interfície ’front-end’ molt ben aconseguida. – Orientació totalment OpenSource.

CRELoaded S’han obtingut les dades següents per al sistema CRELoaded (web oficial: www.creloaded.com).

Rellevància - 1,4 M d’entrades a google - 12.147 visites diàries a la web oficial i 7.871 al portal de la comunitat Petita llista a la web oficial (link). Exemples: www.vividracing.com i www.magpiejewellery.net.

Capacitat d’observació Tres versions: - CE: open source, gratuït, GPL. - Pro: versió estàndard, de pagament, llicència comercial. - B2B:versió amb característiques adicionals, de pagament, llicència comercial. Demos online: - Demo del portal d’administrador en les 3 versions a la pàgina oficial (link). - Demo de la botiga (www.demo-creloaded.com). Instal·lable? Només la versió CE. No s’ha provat personalment. Accés a la BD si tenim el sistema instal·lat.

Altres dades d’interès PHP + MySQL. – Portal dedicat a la comunitat: www.creloaded.org. Bastant extens. – Sistema de seguretat avançat (PCI). – Desenvolupat a partir de osCommerce. El paquet base ja inclou d’entrada moltes característiques que a osCommerce eren extensions. Les noves extensions que surten per a osCommerce segueixen sent compatibles.

30 CubeCart S’han obtingut les dades següents per al sistema CubeCart (web oficial: www.cubecart.com).

Rellevància - 2,1 M d’entrades a google - 8,277 visites diàries a la pàgina oficial Llista de 2763 botigues a la web oficial (link). Exemples: www.stripsrus.co.uk i www.mycanadianpharmacy.ca.

Capacitat d’observació Dues versions: - v4: De pagament, llicència comercial. Es pot descarregar una versió d’evaluació durant 30 dies. - v3: és gratuïta i el codi és consultable, però no és OpenSource. Demo online: - Botiga: les dues versions a la web oficial (link) - Administració: no s’ha trobat. Accés a la BD si tenim el sistema instal·lat.

Altres dades d’interès PHP + MySQL. - Orientació totalment comercial. Ofereixen suport per als clients que comprin el programari. – Documentació i apartat “comunitari” pràcticament nul: només un fòrum.

Batavi S’han obtingut les dades següents per al sistema Batavi (web oficial: www.batavi.org).

Rellevància - 80.000 entrades a google - 1.035 visites diàries a la pàgina oficial No hi ha pràcticament botigues en funcionament encara.

Capacitat d’observació Opensource, gratuït, llicència GPL. - Demo online a la pàgina oficial. - Instal·lable? Sí segons la web, però no ha estat provat personalment. Es desconeix si permet l’accés a la BD.

31 Altres dades d’interès PHP. No s’ha trobat informació respecte a la BD. – Creat per antics desenvolupadors de osCommerce conjuntament amb ICEshop.nl, empresa proveïdora de hosting per a botigues on-line. S’autodefineix com a la versió 3.0 d’osCommerce que mai va arribar a sortir. – En destaquen que és el primer sistema dissenyat amb arquitectura orientada a objectes i arquitectura de 3 capes (disseny, codi i base de dades). Dóna especial importància a les APIs de integració amb altres eines, destacant especialment el catàleg obert de productes ICEcat (en.wikipedia.org/wiki/Open_ICEcat). – Encara està en una versió Beta. – Articles amb informació sobre Batavi: http://www.e-commerce-site.co.uk/batavi-open-source-e-commerce i en.wikipedia.org/wiki/Batavi.

3.2.4 Conclusions

Sobre la situació dels sistemes de comerç electrònic Pràcticament tots els sistemes analitzats satisfan la majoria dels criteris establerts relatius a la capacitat d’observació:

I Pràcticament tots els sistemes analitzats estan desenvolupats en PHP, utilitzen una base de dades MySQL i funcionen en un servidor Apache.

I En els casos anteriors, la base de dades es pot consultar si tenim el sistema instal·lat en una màquina local (directament a través del gestor mySQL). En canvi, no s’ha trobat cap cas en què es pogués consultar en la demo online.

I Pràcticament tots els sistemes descrits tenen, almenys, una versió gratuïta i amb llicència lliure GPL o OSL. En tot cas, en tots ells es pot consultar el codi PHP.

I Alguns sistemes estan mantinguts exclusivament per a comunitats internautes, mentre que els que tenen versió de pagament estan desenvolupats per una empresa comercial que, habitualment, ofereix també atenció directa al client.

I El nombre de botigues creades amb cada sistema ha set la dada més difícil de trobar: en la majoria de casos, la pàgina oficial del sistema conté una llista de botigues registrades, però que dista molt de ser completa.

Sobre cada un dels sistemes analitzats Els criteris de rellevància, i les observacions de l’autor, marquen la diferència entre els diversos sistemes. Les opinions expressades en comentaris i anàlisis de pàgines especialitzades, quan tenen un caràcter relativament unànim, també permeten extreure’n conclusions diferenciadores. Magento Sembla ser el sistema ’professional’ per excel·lència. Es tracta d’un sistema comercial, però té una versió open-source. Hi ha multitud de comentaris, blogs i alguna entrevista parlant-ne com a un sistema fiable i segur, amb el qual evitar problemes. ZenCart També és una de les solucions per excel·lència, però molt més enfocada ’openSource’. La web, els fòrums, la organització, tot té un aire molt més proper a una comunitat de desenvolupadors que a una empresa. Està desenvolupat a partir de osCommerce. X-Cart Té menys presència que les dues anteriors en la majoria de blogs i webs especialtizades. Per contra, hi ha multitud de webs de hosting i proveïdors de serveis X-Cart disponibles. La serietat i la quantitat d’informació de la pàgina oficial també dónen la sensació d’un sistema gran i popular.

32 VirtueMart Està associat al gestor de continguts Joomla, que obté valoracions molt favorables en la majoria de pàgines especialitzades. Les valoracions, però, no són tant positives per al propi sistema. En algun anàlisi es compara Virtuemart amb osCommerce, en quant a capacitats i característiques, dient després que, en canvi, aquest quedava per sota de les noves versions com ZenCart o CRE Loaded. PrestaShop Molt senzill, fàcil d’utilitzar i amb una interfícia molt amigable. Sembla orientat a un segment d’usuaris amb unes necessitats més modestes. Lligat al món de l’OpenSource. Té la documentació justa per saber-lo fer funcionar i administrar. CRELoaded Sense tenir la popularitat de Magento o ZenCart, tampoc ofereix una proposta alternativa com ho fa PrestaShop. Com a punt positiu, té un apartat del portal comunitari dedicat a documentar l’arquitectura del sistema, on hi tindria cabuda el treball realitzat en aquest projecte. CubeCart Es tracta també d’un producte comercial, com Magento, però menys popular i donant menys suport a la comunitat de desenvolupadors. Batavi És molt poc conegut comercialment, relativament nou i no té cap versió estable. Sembla bastant innovador a nivell tècnic, la qual cosa el podria fer interessant de cara l’elicitació del model conceptual.

Decisió final S’ha decidit que en aquest projecte s’elicitarà l’esquema conceptual del sistema Magento, pels motius que s’exposen a continuació:

I Per complir satisfactòriament els criteris establerts.

I Per ser un dels sistemes amb més popularitat.

I Per ser un dels sistemes més complexos i que integren un coneixement més ampli de l’univers de la venda per Internet.

3.3 Magento

Objectius L’objectiu de magento és subministrar una solució base per a un portal d’e-comerç personalitzat i adaptat a les exigències i característiques pròpies de cada negoci. Segons el que s’expressa en la seva pàgina oficial2: “Magento is a feature-rich, professional open-source eCommerce solution that offers mer- chants complete flexibility and control over the look, content, and functionality of their online store. Magento’s intuitive administration interface contains powerful marketing, merchandising and content management tools to give merchants the power to create sites that are tailored to their unique business needs. Completely scalable and backed by an extensive support network, Magento offers companies the ultimate eCommerce solution.”

Evolució del sistema Magento apareix per primera vegada al mercat com a sistema estable el 31 de març de 2008. L’ha desenvolupat la companyia Varien, dedicada anteriorment a la creació de comerços on-line (molts d’ells amb el sistema osCommerce). Des dels inicis fins a l’actualitat, Varien ha diversificat la oferta sistemes Magento, oferint actualment 3 versions principals:

2Pàgina oficial de Magento http://www.magentocommerce.com

33 I Magento Community: gratuïta i descarregable des de la pàgina oficial, amb llicència lliure OSL 3.03. És la versió hereu del sistema original. La pàgina oficial la dirigeix per a “de- senvolupadors experts i entusiastes tèncincs en entorns no crítics. Destinat a aquells amb el temps i els recursos necessaris per a resoldre problemes de manera independent”. Es manté viva i rep actualitzacions periòdiques, proporcionades per la pròpia companyia. No s’ofereix, però, suport ni garantia.

I Magento Professional: s’ofereix a partir de 2.995 $ a l’any, i la pàgina oficial la recomana “per a petits negocis que treballen amb Magento [...] i necessiten un producte amb la garantia i funcionalitats addicionals que no es troben a Magento Community”.

I Magento Enterprise: és la versió més completa de Magento, oferta per 12.990 $ a l’any, i va dirigida a “negocis de qualsevol tamany que busquin un producte ja preparat per a la posada en funcionament [...]. Té el suport total de l’equip Magento”. Totes tres versions mantenen obert el codi font del sistema, que pot ser consultat i modificat si es disposa de la versió corresponent. La pàgina oficial de Magento disposa d’una comparació de les funcionalitats ofertes per cada una de les versions4.

Funcionalitats El sistema Magento està format per dues parts diferenciades:

I El portal de comerç electrònic o botiga virtual, amb el qual interactuen els clients per real- itzar les seves compres.

I L’administració de la botiga, destinada al responsable de la gestió del portal. La botiga virtual permet als clients realitzar el cicle de compra complet: visualitzar els productes oferts, afegir-los al “cistell de la compra” virtual, confirmar la compra i realitzar el pagament. Els usuaris es poden registrar al sistema. D’aquesta manera, el sistema enregistra infor- mació sobre les compres fetes per l’usuari, amb l’objectiu de poder-li oferir facilitats addicionals: destacar productes que puguin interessar al client, desar les seves dades personals per no haver- les de reintroduïr a cada compra, etc. Magento ofereix diverses possibilitats per a gestionar els processos de pagament de la com- pra i transport dels productes comprats: des de la mera anotació de la compra, deixant les transaccions de pagament i transport a processos offline, a la completa automatització del procés a través de tercers: PayPal o targetes de crèdit, per al pagament, i empreses de transport espe- cialitzades: DHL, FedEx o UPS. La part d’administració de la botiga permet, entre d’altres:

I Actualitzar el catàleg de productes

I Modificar l’aparença del portal de vendes

I Gestionar o definir aspectes del seu funcionament: impostos que s’aplicaran sobre el preu final, zones geogràfiques de venda, política d’ofertes, etc.

I Gestionar les operacions posteriors a la compra: facturació, transports, devolucions, etc.

I Obtenir informes de l’activitat del portal de comerç

I Anotar una venda en nom d’algun client, amb l’objectiu de gestionar a través del sistema vendes realitzades a través d’altres canals més minoritaris per a l’empresa venedora (via telefònica, per exemple).

3L’especificació de la llicència OSL 3.0 es pot consultar a www.opensource.org/licenses/osl-3.0.php 4La comparació completa es pot consultar a http://www.magentocommerce.com/product/compare i a www.wikigento.com/general/magento-version-ce-vs-version-ee

34 Algunes funcionalitats complementàries al procés de compra, com la creació de comentaris d’anàlisi dels productes oferts, poden ser dutes a terme des d’ambdues parts del sistema.

Comunitat de desenvolupadors El desenvolupament open-source de Magento ha permès aglutinar una extensa comunitat de desenvolupadors, a la qual la companyia dóna suport5 i se’n nodreix. Magento ofereix la possibilitat de desenvolupar extensions que n’amplïin les seves possibili- tats, que es poden descarregar des de la web. En alguns casos les extensions han esdevingut verdaders productes comercials paral·lels, generant clients disposats a pagar una quantitat con- siderable per a obtenir-los. La col·laboració mútua entre Varien i la comunitat Magento ha donat fruits diversos: fòrums de suport, traduccions a múltiples llengües o la creació d’una Wikipedia especialitzada. A més, algunes de les propostes realitzades per usuaris s’han acabat incorporant al producte principal. És en aquest context de col·laboració on els resultats d’aquest projecte es volen emmarcar.

Aplicació comercial La utilització de Magento en projectes comercials ha potenciat la figura de l’intermediari “expert” en els aspectes tècnics del sistema, que s’encarrega de la posada a punt d’un portal de comerç electrònic o botiga online a partir dels requisits de la pròpia botiga. Depenent dels tractes comercials establerts, és habitual que l’intermediari s’ocupi també de tasques de manteniment del portal. Malgrat això, la usabilitat de Magento permet que un usuari no expert en informàtica dugui a terme les operacions més cotidianes: actualització del catàleg, modificació de preus, gestió de comandes, etc. Magento és actualment un dels sistemes de referència del mercat. Segons la seva pàgina oficial6, la xifra total de descàrregues ha assolit ja els 3 milions i es tenen comptabilitzades més de 90.000 botigues online actives. Marques reconegudes com Adidas, Nespresso, Samsung, Nokia, The North Face, TimeOut o Custo Barcelona gestionen el seu portal de comerç electrònic amb el sistema Magento.

Aplicació en el projecte actual Per a l’anàlisi i observació de Magento, que permetran elaborar-ne l’esquema conceptual, s’ha utilitzat la versió següent: Magento Community Edition v1.4.0.1. Aquesta versió ha estat descarregada i instal·lada en un entorn Windows, utilitzant les eines detallades al capítol 11.

5Veure el portal oficial dedicat a la Comunitat Magento http://www.magentocommerce.com/boards/ 6Dades consultades a l’abril de 2011.

35 Part II

L’esquema

36 4 Conceptual Schema organitzation

Chapters 5, 6 and 7 describe the Magento’s conceptual Schema in UML / OCL notation. Due to its size, the schema must be organized and divided in parts, facilitating its comprensibility. This chapter presents the organizative structure that has been used to document Magento’s Conceptual Schema. Section 4.1 presents a global view, which is detailed in sections 4.2 and 4.31.

4.1 Structure overview

Magento’s Conceptual Schema is organised in the same way as in osCommerce Conceptu- al Schema (see [15]). It’s structure can be divided in two parts: structural and behavioral (sub)schema’s. Structural Schema presents the static knowledge of the system: entities, properties of those entities and constraints. Those concepts are represented in UML diagrams and OCL expressions. Behavioral Schema describes the features that the system offers to its users, also known as its dynamic behaviour. The description is made by Use Cases, which are specified in natural language and related to the main system Events. Events’ effects and restrictions are specified in OCL.

4.2 Structural Schema

A structural schema consists of a taxonomy of entity types (a set of entity types with their gen- eralization/specialization relationships and the taxonomic constraints), a set of relationship types (either attributes or associations), the cardinality constraints of the relationship types and a set of other constraints formally defined in OCL. Entity and relationship types may be base or derived. The population of the base entity and relationship types is explicitly represented in the Information Base (IB). If they are derived, there is a formal derivation rule in OCL that defines their population in terms of the population of other types. Magento’s Structural Schema can be considered a large schema. Therefore, it can’t be rep- resented together, so it has been divided in several sub-schemas that group related concepts.

1Some definitions in this chapter are citations of [15, 17]

37 Sub-schemas are interrelated and, as a whole, they perform the complete Structural Schema and represent the knowledge about the analysed domain. In order to facilitate its comprensibility, sub-schemas have been classified in five groups. First, we present three simplified diagrams, that contain only the main entities and relation- ships of the system. Afterwards, the set of sub-schemas is detailed. In order to facilitate schema’s readability and comprensibility, each sub-schema is presented in the following format:

I Sub-schema’s Name

I Overview: brief explanation about the knowledge represented by the sub-schema.

I Schema Diagram: UML class diagram specifying the domain knowlege represented by the sub-schema. Entities that are specified in other sub-schema’s but have relationships with entities specified here are shown in white colour, hidding it’s attributes. Sometimes, the UML sub-schema’s diagram must be separated in two or more pages due to document formatting.

I Operations: auxiliar operations used in constraints and derivation rules.

I Derivation Rules: derivation rules, specified in OCL operations.

I Constraints: integrity constraints that have not been expressed graphically. All of them are specified as boolean OCL operations.

I Description: sub-schema’s detailed description in natural language. Sometimes, it can be followed by an State-Chart Diagram to facilitate its understanding.

4.3 Behavioral Schema

The behavioral schema consists of a set of use cases and event types. Event types are modelized as UML classes with the <> stereotype. In general, there are three kinds of event types: domain events, action requests and queries. Domain events have characteristics, constraints and effects. The characteristics of an event are the set of relationships (attributes or associations) in which it participates. Each event type has an operation called effect() that gives the effect of an event occurrence. The characteristics of the event are used as input parameters for the effect() operation. We define the effect by the postcondition of the operation. Both the event constraints and the postcondition are specified in OCL. The postcondition defines the state of the IB after the event occurrence. Therefore, the effect of a domain event is a state that satisfies the postcondition and all IB constraints. Note that the OCL expressions used in constraints, derivation rules and pre/post conditions are without side- effects. These expressions are evaluated over the IB and their evaluation cannot change the IB. Behavioral schema presents use case specification in natural language, following the format specified below:

I Use Case Name

I Primary Actor: main use case actor.

I Precondition: conditions that must be satisfied to execute the use case, specified in natural language.

I Trigger: action that initiates the use case.

38 I Main Success Scenario: an specification of the interaction between the system and the use case actors, specified in a numered sequence of sentences in natural language.

I Extensions: specification of alternative interactions to the main scenario. It’s first number indicates the stage of the main scenario where the extension starts.

In some stages of scenario specification (main or extensions), the main system Events are in- voked, with the following syntaxis: [-> EventName] Those Events are specified in chapter 7, using de format detailed below:

I Event Name

I Event Characteristics: the set of relationships (attributes or associations) in which it par- ticipates and are used as input parameters for the effect() operation.

I Initial Integrity Constraints: conditions that must be satisfied before the Event starts. They are specified in OCL.

I Effect: effect that productes the Event’s execution in the information base. It is specified in OCL.

39 5 Structural Schema

In this chapter we develop the structural schema of the Magento information system. Section 5.1 defines the scope of the schema elicitation, while section 5.2 describes the way the schema is presented. Section 5.3 presents an overview of the whole schema, which is detailed in section 5.4 and furthermore.

5.1 Scope

A conceptual schema represents the knowledge that a system needs to know about its domain, and about the functions it has to perform [8]. Thus, knowledge related to the way the system is presented, or technical aspects that depend on the technologies it is implemented are analysed in further stages of the development process. When eliciting this schema from Magento, those aspects have been identified and not included in the conceptual schema. More precisely, they refer to the following aspects:

I Design and presentation

I Configuration of the integration with external systems: Magento can communicate to exter- nal systems such as Pay Pal, Google or Visa and configuration parameters are needed.

I Static web pages management: by default, Magento generates all its web pages depending on the state of its information base, but it offers administrators to include their own static pages too.

I Other technical aspects: CSS, Javascript or RSS configuration.

I Advanced configuration: system time-outs, backups, compilation, URL redirection and more. Furthermore, the purpose of this project is to describe the conceptual schema of an e-commerce system, Magento. Thus, those knowledge that does not characterize the buying activity has not been included in the schema:

I Search engine: included in Magento, allows users searching products by keywords and manages a history of the last and more used search terms.

40 Finally, Magento has a huge list of extensions that can be added to the core system. Each extension needs its own knowledge to perform its functions, and the available extensions never cease to change. The conceptual schema specified does not represent the knowledge need by any of those extensions.

5.2 Schema presentation

The main purpose of the Magento’s structural schema is to provide a description of the concep- tualization of the Magento’s domain. The structural schema is too large to be presented together. Therefore, this chapter begins with three UML general view diagrams with the most important conceptual entity types and their relationship types. Next, the whole schema is structured in several diagrams of greater detail in order to make it more understandable. Each diagram corresponds to a part of the whole detailed schema and groups together related concepts which can be seen as a set of knowledge about the information system. Entities which appear in a structural schema fragment but are fully specified in other conceptual grouping diagrams are drawn without showing their attributes. Each fragment of the whole structural schema diagram is represented in UML and is intro- duced by a brief textual overview. Derived types and integrity constraints are specified in OCL. The structural schema specification uses standard UML with some extensions (specification of derived types and constraints by OCL operations, constant and permanent stereotypes, etc.) explained in [8].

5.3 Schema Overview

Three UML diagrams are used to represent a simplified conceptual schema of the Magento’s domain. More details about each concept are given in the next section, where the whole schema is fully specified.

Figure 5.1: Store Configuration and Customers Schema Overview

41 Store Configuration Subschema’s classified into this group describe the core structure of the Magento System: Web- sites, Stores and StoreViews. Those three concepts act as different granularity levels when con- figuring several system properties. For each of them, the System Administrator can define the desired configuration about Shipping and Payment methods, Languages, Currencies, Locations, Taxes and more (see fig. 5.1).

Customers Magento saves information about Customers, which are relied to a Website. Each Customer can be associated to one or more Addresses. Any use of the system can be conceptually represented by a Session. A Session can be relied to a registered Customer or it can be anonymous (see fig. 5.1).

Figure 5.2: Store Administration and Additional Activities Schema Overview

Store Administration The system mantains information about a catalog of Products, whose properties can be defined at three levels of granularity. Products are classified in Categories. Attributes can be defined for products by the Store Administrator. For each pair Attribute - Product, the Store Administrator can assign an AttributeValue at any granularity level. In addition, Options can be also added to products. In that case, its value is choosen by Customers when purchasing the product (see fig. 5.2).

Additional Activities Magento saves information about the activities performed by customers when they navigate throught the catalog and purchase products. Furthermore, the system offers other features to users such as adding Tags and Reviews to products, or suscribing to a Newsletter (see fig. 5.2).

42 Figure 5.3: Online Catalog Schema Overview

Online Catalog Users that navigate throught the catalog can add the Products they want to buy to a Shopping- Cart. The purchase of a Product in a ShoppingCart is represented by a ShoppingCartItem. Each ShoppingCart is asosociated to an Store View. Depending on several conditions, PriceRules can be associated to a ShoppingCartItem, decrementing it’s price (see fig. 5.3).

43 5.4 Store Configuration

5.4.1 Stores

I Overview Magento allows multi-store support. It defines a hierarchy of Websites, Stores and Store- views.

I Schema Diagram

I Constraints

[1] Websites are identified by its code.

44 Context Website :: isIdentifiedByItsCode() : Boolean body: Website.allInstances() -> isUnique(code)

[2] Store are identified by its name.

Context Store :: isIdentifiedByItsName() : Boolean body: Store.allInstances() -> isUnique(name)

[3] StoreViews are identified by its code and also by its name.

Context StoreView :: isIdentifiedByItsCodeAndName() : Boolean body: StoreView.allInstances() -> isUnique(code) and StoreView.allInstances() -> isUnique(name)

[4] Each language is identified by its name.

Context Language :: isIdentifiedByItsName() : Boolean body: Language.allInstances() -> isUnique(name)

[5] Each time zone is identified by its name.

Context TimeZone :: isIdentifiedByItsName() : Boolean body: TimeZone.allInstances() -> isUnique(name)

I Description Magento is able to manage multiple websites and stores. It organizes them in a hierarchy of Websites, Stores and Store Views. The Website is the top-level structure and it consists of stores. Websites save the infor- mation about customers, shopping carts and orders, as shown in the next sections. This information is shared within the stores and store views of the website. Stores are the middle level structure. Some product attributes and categories are defined at the store level and shared with all the store views of the store. A Store View is a way of presenting a store. In most cases, different storeViews are used to show the same store in different languages. We can also define two store views from the same store in the same language, but oriented to different costumer segments. Store views are identified by its name and code. When customers access the system, they access to the default website. When customers access a website, they access to its default store. When customers access a store, its default store view is shown.

45 5.4.2 Store Configuration

I Overview Magento allows the store administrator to specify several configurations to affect the way that the system works.

I Schema Diagram

46 I Constraints

[1] The catalog price scope should be Global or Website.

Context CatalogConfiguration :: hasACorrectPriceScope() : Boolean body: not (self.catalogPriceScope = Scope::StoreView)

[2] The catalog price scope should be Website or Store View.

Context CatalogConfigurationInWebsite :: hasACorrectRecentlyViewedAndComparedProductsScope() : Boolean body: not (self.recentlyViewedAndComparedProductsScope = Scope::Global)

47 [3] The share account scope should be Global or Website.

Context CustomerConfiguration :: hasACorrectShareAccountScope() : Boolean body: not (self.shareAccountScope = Scope::StoreView)

[4] If the tax is applied after discount, there is no definition about if discounts are applied including taxes or not.

Context TaxConfigurationInWebsite :: definesTheApplyDiscountMethodWhenItsNeeded() : Boolean body: (self.methodRespectingDiscount = TaxRespectingDiscountsMethod::TaxAfterDiscount) = (self.- applyDiscountIncludingTax -> isEmpty())

[5] The the default customer group should not be the group named ’NotLoggedIn’.

Context CustomerConfigurationInStoreView :: doesNotDefineTheNotLoggedInGroupAsDefault() : Boolean body: self.defaultCustomerGroup.name <> ’NotLoggedIn’

I Description Magento’s configuration is organised in several groups. For each group, some properties are defined at the global level, others at the website level and others at the store view level. The Catalog Configuration manages most of the properties about products:

• catalogPriceScope: defines the scope the product prices will be shared at. • allowStockAlert: determines if the customers can indicate the system to notify them when a selected product which is out of stock comes back in stock. • allowGuestsToWriteReviews: allows not registered users to write reviews for a prod- uct. • recentlyViewedComparedProductsScope: defines the scope the recently viewed and comparet list will be shared at. • allowDownloadBeforeOrderIsComplete: allows downloadable products to be down- loaded before an order is already paid. • allowGuestCheckoutForDownloadableItems: allows not registered users to pur- chase downloadable products. • maximumDownloadCount: the maximum of times a downloadable product can be downloaded. • allowPriceAlert: determines if the customers can indicate the system to notify them when a selected product changes its price. • numberOfRecentlyComparedProductsSaved: that will be saved in the recently com- pared products list. • numberOfRecentlyViewedProductsSaved: that will be saved in the recently viewed products list.

The Stock Configuration manages the product stock properties. The last five can be rede- fined for each individual product.

• decreaseStockWhenOrderIsPlaced: determines whether the quantity of a product will automatically decrease when an order that purchases this product is placed. • setItemInStockWhenOrderIsCancelled: determines whether the quantity of a prod- uct will automatically be re-increased when an order that purchases this product is cancelled.

48 • displayOutOfStockProducts: determines whether products Out Of Stock are dis- played to customers. • qtyToBecomeOutOfStock: determine at which quantity the availability of the item will switch from In Stock to Out of Stock. • minQtyAllowedInShoppingCart: the minimum amount the customer is required to purchase of each product in an order. • maxQtyAllowedInShoppingCart: the maximum amount the customer is allowed to purchase of each product in an order. • notifyForQuantityBelow: when a stock of any product goes below this level, Magento will automatically notify the store admin. • backOrderPolicy: determines if customers are allowed to purchase more quantity than is available or not and, if yes, if they are notified when they place an order with unavailable quantities.

The Wishlist Configuration allows to set if wishlists are or not allowed in a website. The Sales Configuration defines:

• allowGiftMessageForOrders: allows customers to add a gift message to its orders. • allowGiftMessageForOrderLines: allows customers to add a gift message to a prod- uct they have ordered.

The Customer Configuration defines:

• shareAccountScope: defines the scope the customer accounts will be shared at. • defaultCustomerGroup: the customer group that a customer will be assigned by default when it is created.

The Tax Configuration defines how taxes are calculated and when are they shown to users:

• shippingTax: this tax will be applied to shipping costs. • method: determines wether the tax amount is calculated for each item, for each or- der line or for all the order. This can make the final tax amount vary due to decimal truncation. • usedAdress: the adress that will be use to calculate the applicable taxes. See taxes and order’s sections. • methodRespectingDiscount: determines whether the taxes are applied before or after discounts. • applyDiscountIncludingTax: if taxes are applied before discounts, determines whether an eventual discount is applied taking into account the tax amount. • applyTaxOnCustomPriceIfAvailable: when a product is sold to a customer with a custom price, determines if taxes are applied to the new or the original price. • displayShippingPriceWithTax: determines if shipping costs are shown to customers with or without taxes. • displayProductPriceWithTax: determines if product prices are shown to customers with or without taxes.

The Shipping Configuration manages the following information:

• shippingOrigin: the postal area from where the shipments will be sent.

49 • allowShippingToMultipleAdresses: determines whether it is allowed to ship prod- ucts from the same order to different adresses. When an Order with multiple shipping addresses is saved, it will split into multiple Orders because only one shipping address per order is allowed. • mawQtyAllowedForMultipleAdresses: maximum quantity that a customer can enter in the for each item in a multiple adresses checkout.

The Tell to a friend Configuration defines:

• status: enables or disables all customers, including guests, to automatically send mails with information about a product. • guestStatus: enables or disables not registered customers (guests) to automatically send mails with information about a product. • maxUsesPerHour: the maximum of times a customer can use the ’tell to a friend’ feature per hour.

The Currency Configuration defines several currency properties that will be commented in the Currency section. Similarly, the Locations Configuration will be commented in the Locations section.

50 5.4.3 Currencies

I Overview Magento is able to work with different currencies.

I Schema Diagram

I Derivation Rules

[1] CurrencyConfigurationInWebsite :: baseCurrency is the website’s base currency, if it is de- fined, or the general system’s base currency otherwise.

Context CurrencyConfigurationInWebsite :: baseCurrency : Currency body:

51 if self.redefinedBaseCurrency -> notEmpty() and CatalogConfiguration.catalogPriceScope = Scope::Website then self.redefinedBaseCurrency else CurrencyConfiguration.genericBaseCurrency endif

[2] CurrencyConfigurationInStoreView :: allowedCurrency are the store view’s allowed curren- cies, if it is defined. If not, the website’s allowed currencies or the generic allowed currencies.

Context CurrencyConfigurationInStoreView :: allowedCurrency : Currency body: if self.redefinedAllowedCurrency -> notEmpty() then self.redefinedAllowedCurrency else if self.storeView.store.website.currencyConfigurationInWebsite.redefinedAllowedCurrency -> notEmpty() then self.storeView.store.website.currencyConfigurationInWebsite.redefinedAllowedCurrency else CurrencyConfiguration.genericAllowedCurrency endif

[3] CurrencyConfigurationInStoreView :: defaultCurrency is the store view’s default currency, if it is defined. If not, the website’s default currency or the generic default currency..

Context CurrencyConfigurationInStoreView :: defaultCurrency : Currency body: if self.redefinedDefaultCurrency -> notEmpty() then self.redefinedDefaultCurrency else if self.storeView.store.website.currencyConfigurationInWebsite.redefinedDefaultCurrency - > notEmpty() then self.storeView.store.website.currencyConfigurationInWebsite.redefinedDefaultCurrency else CurrencyConfiguration.genericDefaultCurrency endif

I Constraints

[1] Each currency is identified by its name.

Context Currency :: isIdentifiedByItsName() : Boolean body: Currency.allInstances() -> isUnique(name)

[2] The base currency of a website is enabled.

Context CurrencyConfigurationInWebsite :: hasABaseCurrencyWhichIsEnabled() : Boolean body: self.baseCurrency.status = Status::Enabled

[3] Only enabled currencies can be used in a Store View.

Context CurrencyConfigurationInStoreView :: allowsOnlyToUseEnabledCurrencies() : Boolean body: self.allowedCurrency -> forAll ( ac | ac.status = Status::Enabled )

[4] Each currency allowed in a store view has a rate to relate it with the base currency of the website the store view belongs to.

Context CurrencyConfigurationInStoreView :: hasTheNecessaryCurrencyRates() : Boolean body: self.allowedCurrency -> forAll ( ac | ac.baseCurrency -> includes(self.storeView.store.web- site.baseCurrency) )

[5] The currency rate should be 1 if both currencies are the same.

Context CurrencyRate :: isOneIfCurrenciesAreEquals() : Boolean body: self.baseCurrency = self.currency implies self.rate = 1

52 I Description Magento has a predefined set of currencies. Administrators can enable or disable them. Each website has a base currency. The priced orders placed in this website are stored in the base currency. A generic base currency can be defined for all websites and it can be redefined specifically for a website. In each store view, the costumer will be able to see the monetary amounts in any of its allowed currencies. By default, those amounts will be shown in the default currency. Also a generic set of allowed currencies and a generic default currency can be defined for all store views and redefined specifically at the website or store view level. A conversion rate should be defined between each allowed currency and the base cur- rency of its website. The rate is defined as follows: X base currency = rate * X allowed currency.

53 5.4.4 Locations

I Overview Magento saves information about customer adresses.

I Schema Diagram

I Derivation Rules

[1] LocationsConfigurationInStoreView :: allowedCountry are the store view’s allowed countries, if defined. If not, the website’s allowed countries or the generic allowed countries.

Context LocationsConfigurationInStoreView :: allowedCountry : Set(Country) body: if self.redefinedAllowedCountry -> notEmpty() then self.redefinedAllowedCountry else if self.storeView.store.website.locationsConfigurationInWebsite.redefinedAllowedCountry - > notEmpty()then self.storeView.store.website.locationsConfigurationInWebsite.redefinedAllowedCountry else LocationsConfiguration.genericAllowedCountry endif

I Constraints

[1] Countries are identified by its name.

54 Context Country :: isIdentifiedByItsName() : Boolean body: Country.allInstances() -> isUnique(name)

[2] Zones are identified by its name and country.

Context Zone :: isIdentifiedByItsNameAndCountry() : Boolean body: Zone.allInstances() -> isUnique(Setname,country)

[3] Municipalities are identified by its name and country.

Context Municipality :: isIdentifiedByItsNameAndCountry() : Boolean body: Municipality.allInstances() -> isUnique(Setname,country)

[4] Postal areas are identified by its postal code and country.

Context PostalArea :: isIdentifiedByItsPostalCodeAndCountry() : Boolean body: PostalArea.allInstances() -> isUnique(SetpostalCode,municipality.country)

[5] If the address country has zones, the address is also associated to a zone of this country.

Context Address :: hasACorrectZoneIfItIsNeeded() : Boolean body: self.country.zone -> notEmpty() implies (self.zone -> notEmpty() and self.country = self.- zone.country)

[6] If the country the municipality is part of has zones, the municipality is also associated to a zone of this country.

Context Municipality :: hasACorrectZoneIfItIsNeeded() : Boolean body: self.country.zone -> notEmpty() implies (self.zone -> notEmpty() and self.country = self.- zone.country)

[7] If the country where an adress is located forces to specify a postal area, the address is asso- ciated to one postal area.

Context Adress :: hasAPostalAreaIfItIsNeeded() : Boolean body: self.country.postalCodeIsMandatory implies self.postalArea -> notEmpty()

[8] The postal area of an address belongs to the municipality of that address.

Context Adress :: hasACorrectPostalArea() : Boolean body: self.municipality.postalArea -> includes( self.postalArea )

[9] The municipality of an address belongs to the country and zone (if the zone is defined) of that address.

Context Adress :: hasACorrectMunicipality() : Boolean body: self.country.municipality -> includes( self.municipality ) and self.zone -> notEmpty() implies self.zone.municipality -> includes ( self.municipality )

[10] If an adress is related to a zone, the zone belongs to the country of that address.

Context Adress :: hasACorrectZone() : Boolean body: self.country.zone -> includes( self.zone )

I Description Magento allows creating customer adresses. Adresses specify where the orders will be billed or delivered. Each adress consists of:

55 • firstName • middleName • lastName • namePrefix • nameSuffix • company • streetAdress • telephone • fax

A Postal Area represents a geographical zone which shares the same postal code. A Municipality is an administrative entity composed of a clearly defined territory and its population and denotes a city, town or village. Each municipality belongs to a Country. When a costumer creates a new adress, he will be able to choose only between the countries allowed in the current store view. Zones are states or provinces of a country. It is important to note that there can be countries with no zones defined.

56 5.4.5 Shipping Methods

I Overview The system can operate with different shipping methods.

I Schema Diagram

57 58 I Operations

[1] context FlatRateInWebsite def: calculateCost (purchasedProducts : Set(OrderLine), delivery : Address) : Money =

if self.method=FlatRateMethod::None then 0 else if self.method = FlatRateMethod::PerOrder then

59 self.cost else self.cost * ( purchasedProducts.quantity -> sum() ) endif endif

[2] context TableRateInWebsite def: calculateCost (purchasedProducts : Set(OrderLine), delivery : Address) : Money =

let usedValue:Decimal = if self.method=TableRateMethod::Weight then lines -> collect ( l | Product.allInstances() -> any(p|l.productSku=p.sku).productInStoreView -> any(piw|piw.storeView=l.order.storeView).weight * l.quantity ) -> sum() else if self.method=TableRateMethod::Price then purchasedProducts.total -> sum() else purchasedProducts.quantity -> sum() endif endif in let underValueItems:Set(TableRateItem) = self.item -> select ( i | i.number < usedValue ) in self.tableRateItem -> any ( i | underValueItems -> forAll ( uvi | uvi.number <= i.number )).cost

[3] context FreeShippingInWebsite def: calculateCost (purchasedProducts : Set(OrderLine), de- livery : Address) : Money =

0

[4] context ExternalShippingMethodInWebsite def: calculateCost (purchasedProducts : Set(OrderLine), delivery : Address) : Money

This operation calls an external service.

I Derivation Rules

[1] ShippingMethodInStoreView :: title is the redefinedStatus. If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ShippingMethodInStoreView :: title : String body: if self.redefinedTitle -> notEmpty() then self.redefinedTitle else if self.shippingMethod.shippingMethodInWebsite -> select ( smiw | smiw.website.store.- storeView -> includes ( self.storeView ) ).redefinedTitle -> notEmpty() then self.shippingMethod.shippingMethodInWebsite -> select ( smiw | smiw.website.store.store- View -> includes ( self.storeView ) ).redefinedTitle else self.shippingMethod.genericTitle endif endif

[2] InternalShippingMethodInStoreView :: methodName is the redefined method name. If there’s no redefinition at that level, it is the nearest upper redefined value.

Context InternalShippingMethodInStoreView :: methodName : String body: if self.redefinedMethodName -> notEmpty() then self.redefinedMethodName else if self.shippingMethod.shippingMethodInWebsite -> select ( smiw | smiw.website.store.- storeView -> includes ( self.storeView ) ).redefinedMethodName -> notEmpty() then self.shippingMethod.shippingMethodInWebsite -> select ( smiw | smiw.website.store.store- View -> includes ( self.storeView ) ).redefinedMethodName else self.shippingMethod.genericMethodName

60 endifendif

[3] ShippingMethodInStoreView :: errorMessage is the redefinedErrorMessage. If there’s no re- definition at that level, it is the nearest upper redefined value.

Context ShippingMethodInStoreView :: errorMessage : String body: if self.redefinedErrorMessage -> notEmpty() then self.redefinedErrorMessage elseif self.shippingMethod.shippingMethodInWebsite -> select ( smiw | smiw.website.store.store- View -> includes ( self.storeView ) ).redefinedErrorMessage -> notEmpty() then self.shippingMethod.shippingMethodInWebsite -> select ( smiw | smiw.website.store.store- View -> includes ( self.storeView ) ).redefinedErrorMessage else self.shippingMethod.genericErrorMessage endif

[4] ShippingMethodInWebsite :: allowedCountry the redefinedAllowedCountry. If there’s no re- definition at the website level, it is the generic value.

Context ShippingMethodInWebsite :: allowedCountry : Set(Country) body: if self.redefinedAllowedCountry -> notEmpty() then self.redefinedAllowedCountry else self.shippingMethod.genericAllowedCountry endif

[5] ShippingMethodInWebsite :: status is the redefinedStatus. If there’s no redefinition at the website level, it is the generic value.

Context ShippingMethodInWebsite :: status : Status body: if self.redefinedStatus -> notEmpty() then self.redefinedStatus else self.shippingMethod.genericStatus endif

[6] ShippingMethodInWebsite :: showIfNotApplicable is the redefinedShowIfNotApplicable. If there’s no redefinition at the website level, it is the generic value.

Context ShippingMethodInWebsite :: showIfNotApplicable : Boolean body: if self.redefinedShowIfNotApplicable -> notEmpty() then self.redefinedShowIfNotApplicable else self.shippingMethod.genericShowIfNotApplicable endif

[7] ShippingMethodInWebsite :: handlingCalculatingMethod is the redefinedHandlingCalculat- ingMethod. If there’s no redefinition at the website level, it is the generic value.

Context ShippingMethodInWebsite :: handlingCalculatingMethod : PriceCalculatingMethod body: if self.redefinedHandlingCalculatingMethod -> notEmpty() then self.redefinedHandlingCalculatingMethod else self.shippingMethod.genericHandlingCalculatingMethod endif

[8] ShippingMethodInWebsite :: handlingFee is the redefinedHandlingFee. If there’s no redefini- tion at the website level, it is the generic value.

Context ShippingMethodInWebsite :: handlingFee : Decimal body:

61 if self.redefinedHandlingFee -> notEmpty() then self.redefinedHandlingFee else self.shippingMethod.genericHandlingFee endif

[9] FlatRateInWebsite :: method is the redefinedMethod. If there’s no redefinition at the website level, it is the generic value.

Context FlatRateInWebsite :: method : FlatRateMethod body: if self.redefinedMethod -> notEmpty() then self.redefinedMethod else self.flatRate.genericMethod endif

[10] FlatRateInWebsite :: cost is the redefinedCost. If there’s no redefinition at the website level, it is the generic value.

Context FlatRateInWebsite :: cost : Money body: if self.redefinedCost -> notEmpty() then self.redefinedCost else self.flatRate.genericCost endif

[11] TableRateInWebsite :: items is the redefinedItems. If there’s no redefinition at the website level, it is the generic value.

Context TableRateInWebsite :: items : ShippingTableItem body: if self.redefinedItems -> notEmpty() then self.redefinedItems else self.tableRate.genericItems endif

[12] TableRateInWebsite :: method is the redefinedMethod. If there’s no redefinition at the web- site level, it is the generic value.

Context TableRateInWebsite :: method : ShippingTableMethod body: if self.redefinedMethod -> notEmpty() then self.redefinedMethod else self.tableRate.genericMethod endif

[13] TableRateInWebsite :: includeVirtualProducts is the redefinedIncludeVirtualProducts. If there’s no redefinition at the website level, it is the generic value.

Context TableRateInWebsite :: includeVirtualProducts : Boolean body: if self.redefinedIncludeVirtualProducts -> notEmpty() then self.redefinedIncludeVirtualProducts else self.tableRate.genericIncludeVirtualProducts endif

[14] FreeShippingInWebsite :: minimumOrderAmount is the redefinedMinimumOrderAmount. If there’s no redefinition at the website level, it is the generic value.

Context FreeShippingInWebsite :: minimumOrderAmount : Money body: if self.redefinedMinimumOrderAmount -> notEmpty() then self.redefinedMinimumOrderAmount

62 else self.freeShipping.genericMinimumOrderAmount endif

[15] ExternalShippingMethodInWebsite :: gatewayURL is the redefinedGatewayURL. If there’s no redefinition at the website level, it is the generic value.

Context ExternalShippingMethodInWebsite :: gatewayURL : URL body: if self.redefinedGatewayURL -> notEmpty() then self.redefinedGatewayURL else self.externalShippingMethod.genericGatewayURL endif

[16] ExternalShippingMethodInWebsite :: maxPackageWeight is the redefinedMaxPack- ageWeight. If there’s no redefinition at the website level, it is the generic value.

Context ExternalShippingMethodInWebsite :: maxPackageWeight : Weight body: if self.redefinedMaxPackageWeight -> notEmpty() then self.redefinedMaxPackageWeight else self.externalShippingMethod.genericMaxPackageWeight endif

[17] ExternalShippingMethodInWebsite :: handlingApplied is the redefinedHandlingApplied. If there’s no redefinition at the website level, it is the generic value.

Context ExternalShippingMethodInWebsite :: handlingApplied : HandlingMethod body: if self.redefinedHandlingApplied -> notEmpty() then self.redefinedHandlingApplied else self.externalShippingMethod.genericHandlingApplied endif

[18] ExternalShippingMethodInWebsite :: allowedSpecificMethods is the redefinedAllowedSpeci- ficMethods. If there’s no redefinition at the website level, it is the generic value.

Context ExternalShippingMethodInWebsite :: allowedSpecificMethods : Set(ExternalSpecificShippingMethod) body: if self.redefinedAllowedSpecificMethods -> notEmpty() then self.redefinedAllowedSpecificMethods else self.externalShippingMethod.genericAllowedSpecificMethods endif

[19] ExternalShippingMethodInWebsite :: freeSpecificMethod is the redefinedFreeSpeci- ficMethod. If there’s no redefinition at the website level, it is the generic value.

Context ExternalShippingMethodInWebsite :: freeSpecificMethod : ExternalSpecificShippingMethod body: if self.redefinedFreeSpecificMethod -> notEmpty() then self.redefinedFreeSpecificMethod else self.externalShippingMethod.genericFreeSpecificMethod endif

[20] ExternalShippingMethodInWebsite :: minimumAmountForFreeShipping is the redefinedMin- imumAmountForFreeShipping. If there’s no redefinition at the website level, it is the generic value.

Context ExternalShippingMethodInWebsite :: minimumAmountForFreeShipping : Money body: if self.redefinedMinimumAmountForFreeShipping -> notEmpty() then self.redefinedMinimumAmountForFreeShipping

63 else self.externalShippingMethod.genericMinimumAmountForFreeShipping endif

[21] UPSInWebsite :: container is the redefinedContainer. If there’s no redefinition at the website level, it is the generic value.

Context UPSInWebsite :: container : USPSContainer body: if self.redefinedContainer -> notEmpty() then self.redefinedContainer else self.uPS.genericContainer endif

[22] UPSInWebsite :: destinationType is the redefinedDestinationType. If there’s no redefinition at the website level, it is the generic value.

Context UPSInWebsite :: destinationType : Destination body: if self.redefinedDestinationType -> notEmpty() then self.redefinedDestinationType else self.uPS.genericDestinationType endif

[23] UPSInWebsite :: pickup is the redefinedPickup. If there’s no redefinition at the website level, it is the generic value.

Context UPSInWebsite :: pickup : UPSPickupMethod body: if self.redefinedPickup -> notEmpty() then self.redefinedPickup else self.uPS.genericPickup endif

[24] USPSInWebsite :: userID is the redefinedUserID. If there’s no redefinition at the website level, it is the generic value.

Context USPSInWebsite :: userID : String body: if self.redefinedUserID -> notEmpty() then self.redefinedUserID else self.uSPS.genericUserID endif

[25] USPSInWebsite :: container is the redefinedContainer. If there’s no redefinition at the web- site level, it is the generic value.

Context USPSInWebsite :: container : USPSContainer body: if self.redefinedContainer -> notEmpty() then self.redefinedContainer else self.uSPS.genericContainer endif

[26] USPSInWebsite :: size is the redefinedSize. If there’s no redefinition at the website level, it is the generic value.

Context USPSInWebsite :: size : USPSSize body: if self.redefinedSize -> notEmpty() then self.redefinedSize else self.uSPS.genericSize

64 endif

[27] FedExInWebsite :: userID is the redefinedUserID. If there’s no redefinition at the website level, it is the generic value.

Context FedExInWebsite :: userID : String body: if self.redefinedUserID -> notEmpty() then self.redefinedUserID else self.fedEx.genericUserID endif

[28] FedExInWebsite :: container is the redefinedContainer. If there’s no redefinition at the web- site level, it is the generic value.

Context FedExInWebsite :: container : FedExContainer body: if self.redefinedContainer -> notEmpty() then self.redefinedContainer else self.fedEx.genericContainer endif

[29] FedExInWebsite :: destinationType is the redefinedDestinationType. If there’s no redefinition at the website level, it is the generic value.

Context FedExInWebsite :: destinationType : Destination body: if self.redefinedDestinationType -> notEmpty() then self.redefinedDestinationType else self.fedEx.genericDestinationType endif

[30] FedExInWebsite :: pickup is the redefinedPickup. If there’s no redefinition at the website level, it is the generic value.

Context FedExInWebsite :: pickup : FedExPickupMethod body: if self.redefinedPickup -> notEmpty() then self.redefinedPickup else self.fedEx.genericPickup endif

[31] DHLInWebsite :: userID is the redefinedUserID. If there’s no redefinition at the website level, it is the generic value.

Context DHLInWebsite :: userID : String body: if self.redefinedUserID -> notEmpty() then self.redefinedUserID else self.dHL.genericUserID endif

[32] DHLInWebsite :: container is the redefinedContainer. If there’s no redefinition at the website level, it is the generic value.

Context DHLInWebsite :: container : DHLContainer body: if self.redefinedContainer -> notEmpty() then self.redefinedContainer else self.dHL.genericContainer endif

65 [33] DHLInWebsite :: password is the redefinedPassword. If there’s no redefinition at the website level, it is the generic value.

Context DHLInWebsite :: password : String body: if self.redefinedPassword -> notEmpty() then self.redefinedPassword else self.dHL.genericPassword endif

[34] DHLInWebsite :: accountNumber is the redefinedAccountNumber. If there’s no redefinition at the website level, it is the generic value.

Context DHLInWebsite :: accountNumber : String body: if self.redefinedAccountNumber -> notEmpty() then self.redefinedAccountNumber else self.dHL.genericAccountNumber endif

[35] DHLInWebsite :: shippingKey is the redefinedShippingKey. If there’s no redefinition at the website level, it is the generic value.

Context DHLInWebsite :: shippingKey : String body: if self.redefinedShippingKey -> notEmpty() then self.redefinedShippingKey else self.dHL.genericShippingKey endif

[36] DHLInWebsite :: internationalShippingKey is the redefinedInternationalShippingKey. If there’s no redefinition at the website level, it is the generic value.

Context DHLInWebsite :: internationalShippingKey : String body: if self.redefinedInternationalShippingKey -> notEmpty() then self.redefinedInternationalShippingKey else self.dHL.genericInternationalShippingKey endif

[37] DHLInWebsite :: packageDescription is the redefinedPackageDescription. If there’s no re- definition at the website level, it is the generic value.

Context DHLInWebsite :: packageDescription : String body: if self.redefinedPackageDescription -> notEmpty() then self.redefinedPackageDescription else self.dHL.genericPackageDescription endif

[38] DHLInWebsite :: isShipmentDurable is the redefinedIsShipmentDurable. If there’s no redefi- nition at the website level, it is the generic value.

Context DHLInWebsite :: isShipmentDurable : Boolean body: if self.redefinedIsShipmentDurable -> notEmpty() then self.redefinedIsShipmentDurable else self.dHL.genericIsShipmentDurable endif

66 [39] DHLInWebsite :: dutyPaymentType is the redefinedDutyPaymentType. If there’s no redefini- tion at the website level, it is the generic value.

Context DHLInWebsite :: dutyPaymentType : DHLDutyPaymentType body: if self.redefinedDutyPaymentType -> notEmpty() then self.redefinedDutyPaymentType else self.dHL.genericDutyPaymentType endif

I Constraints

[1] There is only one shipping method for each shipping method subtype, so shipping methods are identified by its subtypes.

Context ShippingMethod :: isIdentifiedByItsSubtype() : Boolean body: FlatRate.allInstances() -> size() = 1 and TableRate.allInstances() -> size() = 1 and FreeShipping.allInstances() -> size() = 1 and UPS.allInstances() -> size() = 1 and USPS.allInstances() -> size() = 1 and FedEx.allInstances() -> size() = 1 and DHL.allInstances() -> size() = 1

[2] A shipping method in a store view is identified by its title and store view.

Context ShippingMethodInStoreView :: isIdentifiedByItsTitleAndStoreView() : Boolean body: ShippingMethodInStoreView.allInstances() -> isUnique(Settitle,storeView)

[3] Each shipping method should be specified in all existing store views in the system.

Context ShippingMethod :: isSpecifiedInAllStoreViews() : Boolean body: self.storeView -> includesAll( StoreView.allInstances() )

[4] Each shipping method should be specified in all existing websites in the system.

Context ShippingMethod :: isSpecifiedInAllWebsites() : Boolean body: self.website -> includesAll( Website.allInstances() )

[5] Each internal shipping method should be specified in all existing websites in the system.

Context InternalShippingMethod :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfInternalShippingMethod -> includesAll( Website.allInstances() )

[6] Each external shipping method should be specified in all existing websites in the system.

Context ExternalShippingMethod :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfExternalShippingMethod -> includesAll( Website.allInstances() )

[7] The flate rate method should be specified in all existing websites in the system.

Context FlatRate :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfFlatRate -> includesAll( Website.allInstances() )

[8] The table rate method should be specified in all existing websites in the system.

67 Context TableRate :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfTableRate -> includesAll( Website.allInstances() )

[9] The free shipping method should be specified in all existing websites in the system.

Context FreeShipping :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfFreeShipping -> includesAll( Website.allInstances() )

[10] The UPS method should be specified in all existing websites in the system.

Context UPS :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfUPS -> includesAll( Website.allInstances() )

[11] The USPS method should be specified in all existing websites in the system.

Context USPS :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfUSPS -> includesAll( Website.allInstances() )

[12] The FedEx method should be specified in all existing websites in the system.

Context FedEx :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfFedEx -> includesAll( Website.allInstances() )

[13] The DHL method should be specified in all existing websites in the system.

Context DHL :: isSpecifiedInAllWebsites() : Boolean body: self.websiteOfDHL -> includesAll( Website.allInstances() )

[14] There is at least one enabled shipping method

Context Website :: hasOneShippingMethodEnabledAtLeast() : Boolean body: self.shippingMethodInWebsite -> select (pm | pm.status=Status::Enabled) -> size() >= 1

[15] The free shipping method does not have any handling fee defined.

Context FreeShipping :: hasNoHandlingFee() : Boolean body: self.handlingFee.isUndefined() and self.handlingCalculatingMethod.isUndefined()

I Description The Magento store administrator can customize the shipping methods that are available at checkout time. During the checkout process, the chosen method is used to calculate the final shipping and packaging costs for the order. Depending on the selected method, the price can be affected by how many products have been ordered, how much they weigh or other criteria. We present here the Shipping Methods allowed by Magento:

• Flat rate: a single price is used on all orders, regardless of how many items are bought, how much everything weighs, etc. • Table rates: table rate charging sets the price for shipping based on the total weight, the total order’s price or the number of the products ordered. The weight or price is looked up in a table to find the matching range, and then that price is applied. • Free shipping: no cost is added when using this method. The store administrator can establish a minimum order amount for this method to be applicable. • UPS: interacts with the external United Parcel Service website.

68 • USPS: interacts with the external United States Postal Service website. • FedEx: interacts with the external FedEx Corporation website. • DHL: interacts with the external Deutsche Post DHL website.

For all shipping methods, some properties should be defined:

• status: defines if the method is available or not for customers. • title: the name the users will see for this method. • allowedCountry: only the Orders shipped to this countries will be able tu use the current shipping method. • showIfNotApplicable: determinates if the method will be displayed if the customer does not meet the country requirements. • errorMessage: the message that will be displayed when the customer does not meet the country requirements, if the showIfNotApplicable property is set to true. • handlingFee: an additional shipping cost that will be added. • handlingCalculatingMethod: the way the upper cost will be calculated, adding the whole quantity or as a percent.

The first tree shipping methods described above are internally defined by magento. For each one, we should define the method name that will be used to show to the customers. The last four methods interact with external shipping service’s websites to calculate the shipping cost. For that methods, we should define the following properties:

• gatewayURL: the url to connect with the service. • maximumPackageWeight: the maximum weight allowed for an single shipment. • handlingApplied: determines if the handling fee will be applied for each shipment or only once per order. • allowedSpecificMethods: set of specific shipping methods that each external ship- ping service provides. • freeSpecificMethod: defines one specific method of this service that the store de- cides to offer for free. • minimumAmountForFreeShipping: if this minimum amount is not reached by the order total price, the free specific method will be offered with its normal price. • userID: the id account provided by the external shipping service to identificate the store. • container: the type of container that will be tipically used to package the sold items. • destinationType: the destination to which the store will typically deliver its orders. • pickupMethod: the method by which the store will deliver its packages to the shipping service.

The DHL Service has several other properties that should be defined. They are internal configurations to identificate ourselves at the DHL system, and fall out of the Magento conceptual model.

69 5.4.6 Payment Methods

I Overview The system can operate with different payment methods.

I Schema Diagram

70 71 I Derivation Rules

[1] PaymentMethodInWebsite :: allowedCountry the redefinedAllowedCountry. If there’s no re- definition at the website level, it is the generic value.

Context PaymentMethodInWebsite :: allowedCountry : Set(Country) body: if self.redefinedAllowedCountry -> notEmpty() then self.redefinedAllowedCountry else self.shippingMethod.genericAllowedCountry

72 endif

[2] PaymentMethodInWebsite :: status is the redefinedStatus. If there’s no redefinition at the website level, it is the generic value.

Context PaymentMethodInWebsite :: status : Status body: if self.redefinedStatus -> notEmpty() then self.redefinedStatus else self.paymentMethod.genericStatus endif

[3] PaymentMethodInWebsite :: newOrderStatus is the redefinedNewOrderStatus. If there’s no redefinition at the website level, it is the generic value.

Context PaymentMethodInWebsite :: newOrderStatus : OrderStatus body: if self.redefinedNewOrderStatus -> notEmpty() then self.redefinedNewOrderStatus else self.paymentMethod.genericNewOrderStatus endif

[4] PaymentMethodInWebsite :: minimumAllowed is the redefinedMinimumAllowed. If there’s no redefinition at the website level, it is the generic value.

Context PaymentMethodInWebsite :: minimumAllowed : Money body: if self.redefinedMinimumAllowed -> notEmpty() then self.redefinedMinimumAllowed else self.paymentMethod.genericMinimumAllowed endif

[5] PaymentMethodInWebsite :: maximumAllowed is the redefinedMaximumAllowed. If there’s no redefinition at the website level, it is the generic value.

Context PaymentMethodInWebsite :: maximumAllowed : Money body: if self.redefinedMaximumAllowed -> notEmpty() then self.redefinedMaximumAllowed else self.paymentMethod.genericMaximumAllowed endif

[6] PaymentMethodInStoreView :: title is the redefinedTitle. If there’s no redefinition at that level, it is the nearest upper redefined value.

Context PaymentMethodInStoreView :: title : String body: if self.redefinedTitle -> notEmpty() then self.redefinedTitle elseif self.paymentMethod.paymentMethodInWebsite -> select ( smiw | smiw.website.store.- storeView -> includes ( self.storeView ) ).redefinedTitle -> notEmpty() then self.paymentMethod.paymentMethodInWebsite -> select ( smiw | smiw.website.store.store- View -> includes ( self.storeView ) ).redefinedTitle else self.paymentMethod.genericTitle endifendif

[7] CreditCardMethodInWebsite :: allowedCard is the redefinedAllowedCard. If there’s no redefi- nition at the website level, it is the generic value.

Context CreditCardMethodInWebsite :: allowedCard : Set(CreditCardType) body: if self.redefinedAllowedCard -> notEmpty() then

73 self.redefinedAllowedCard else self.creditCardMethod.genericAllowedCard endif

[8] CreditCardMethodInWebsite :: cardValidation is the redefinedCardValidation. If there’s no redefinition at the website level, it is the generic value.

Context CreditCardMethodInWebsite :: cardValidation : Boolean body: if self.redefinedCardValidation -> notEmpty() then self.redefinedCardValidation else self.creditCardMethod.genericCardValidation endif

[9] CreditCardMethodInWebsite :: security3DCardValidation is the redefinedSecu- rity3DCardValidation. If there’s no redefinition at the website level, it is the generic value.

Context CreditCardMethodInWebsite :: security3DCardValidation : Boolean body: if self.redefinedSecurity3DCardValidation -> notEmpty() then self.redefinedSecurity3DCardValidation else self.creditCardMethod.genericSecurity3DCardValidation endif

[10] CreditCardMethodInWebsite :: invoiceAutomatically is the redefinedInvoiceAutomatically. If there’s no redefinition at the website level, it is the generic value.

Context CreditCardMethodInWebsite :: invoiceAutomatically : Boolean body: if self.redefinedInvoiceAutomatically -> notEmpty() then self.redefinedInvoiceAutomatically else self.creditCardMethod.genericInvoiceAutomatically endif

[11] CheckMoneyInWebsite :: payeeName is the redefinedPayeeName. If there’s no redefinition at that level, it is the nearest upper redefined value.

Context CheckMoneyInWebsite :: payeeName : String body: if self.redefinedPayeeName -> notEmpty() then self.redefinedPayeeName else self.checkMoney.genericPayeeName endif

[12] CheckMoneyInWebsite :: receiverName is the redefinedReceiverName. If there’s no redefi- nition at that level, it is the nearest upper redefined value.

Context CheckMoneyInWebsite :: receiverName : String body: if self.redefinedReceiverName -> notEmpty() then self.redefinedReceiverName else self.checkMoney.genericReceiverName endif.

[13] AuthorizeNetInWebsite :: username is the redefinedUsername. If there’s no redefinition at the website level, it is the generic value.

Context AuthorizeNetInWebsite :: username : String body: if self.redefinedUsername -> notEmpty() then self.redefinedUsername

74 else self.authorizeNet.genericUsername endif

[14] AuthorizeNetInWebsite :: key is the redefinedKey. If there’s no redefinition at the website level, it is the generic value.

Context AuthorizeNetInWebsite :: key : String body: if self.redefinedKey -> notEmpty() then self.redefinedKey else self.authorizeNet.genericKey endif

[15] AuthorizeNetInWebsite :: mode is the redefinedMode. If there’s no redefinition at the website level, it is the generic value.

Context AuthorizeNetInWebsite :: mode : TransactionMode body: if self.redefinedMode -> notEmpty() then self.redefinedMode else self.authorizeNet.genericMode endif

[16] AuthorizeNetInWebsite :: notification is the redefinedNotification. If there’s no redefinition at the website level, it is the generic value.

Context AuthorizeNetInWebsite :: notification : Boolean body: if self.redefinedNotification -> notEmpty() then self.redefinedNotification else self.authorizeNet.genericNotification endif

[17] AuthorizeNetInWebsite :: gatewayURL is the redefinedGatewayURL. If there’s no redefinition at the website level, it is the generic value.

Context AuthorizeNetInWebsite :: gatewayURL : URL body: if self.redefinedGatewayURL -> notEmpty() then self.redefinedGatewayURL else self.authorizeNet.genericGatewayURL endif

[18] AuthorizeNetInWebsite :: merchantEmail is the redefinedMerchantEmail. If there’s no redef- inition at the website level, it is the generic value.

Context AuthorizeNetInWebsite :: merchantEmail : Email body: if self.redefinedMerchantEmail -> notEmpty() then self.redefinedMerchantEmail else self.authorizeNet.genericMerchantEmail endif

[19] AuthorizeNetInWebsite :: debugStatus is the redefinedDebugStatus. If there’s no redefinition at the website level, it is the generic value.

Context AuthorizeNetInWebsite :: debugStatus : Status body: if self.redefinedDebugStatus -> notEmpty() then self.redefinedDebugStatus else

75 self.authorizeNet.genericDebugStatus endif

[20] PayFlowProInWebsite :: mode is the redefinedMode. If there’s no redefinition at the website level, it is the generic value.

Context PayFlowProInWebsite :: mode : TransactionMode body: if self.redefinedMode -> notEmpty() then self.redefinedMode else self.payFlowPro.genericMode endif

[21] PayFlowProInWebsite :: method is the redefinedMethod. If there’s no redefinition at the website level, it is the generic value.

Context PayFlowProInWebsite :: method : TransactionMethod body: if self.redefinedMethod -> notEmpty() then self.redefinedMethod else self.payFlowPro.genericMethod endif

[22] PayFlowProInWebsite :: verboseInformation is the redefinedVerboseInformation. If there’s no redefinition at the website level, it is the generic value.

Context PayFlowProInWebsite :: verboseInformation : Boolean body: if self.redefinedVerboseInformation -> notEmpty() then self.redefinedVerboseInformation else self.payFlowPro.genericVerboseInformation endif

[23] PayFlowProInWebsite :: debugStatus is the redefinedDebugStatus. If there’s no redefinition at the website level, it is the generic value.

Context PayFlowProInWebsite :: debugStatus : Status body: if self.redefinedDebugStatus -> notEmpty() then self.redefinedDebugStatus else self.payFlowPro.genericDebugStatus endif

[24] PayFlowProInWebsite :: accountInfo is the redefinedAccountInfo. If there’s no redefinition at the website level, it is the generic value.

Context PayFlowProInWebsite :: accountInfo : PayFlowProAccount body: if self.redefinedAccountInfo -> notEmpty() then self.redefinedAccountInfo else self.payFlowPro.genericAccountInfo endif

[25] PayFlowProInWebsite :: proxy is the redefinedProxy. If there’s no redefinition at the website level, it is the generic value.

Context PayFlowProInWebsite :: proxy : Proxy body: if self.redefinedProxy -> notEmpty() then self.redefinedProxy else self.payFlowPro.genericProxy

76 endif

[26] PayPalMethodInWebsite :: mode is the redefinedMode. If there’s no redefinition at the web- site level, it is the generic value.

Context PayPalMethodInWebsite :: mode : TransactionMode body: if self.redefinedMode -> notEmpty() then self.redefinedMode else self.payPalMethod.genericMode endif

[27] PayPalMethodInWebsite :: accountInfo is the redefinedAccountInfo. If there’s no redefinition at the website level, it is the generic value.

Context PayPalMethodInWebsite :: accountInfo : PayPalAccount body: if self.redefinedAccountInfo -> notEmpty() then self.redefinedAccountInfo else self.payPalMethod.genericAccountInfo endif

[28] PayPalMethodInWebsite :: transferCartLineItems is the redefinedTransferCartLineItems. If there’s no redefinition at the website level, it is the generic value.

Context PayPalMethodInWebsite :: transferCartLineItems : Boolean body: if self.redefinedTransferCartLineItems -> notEmpty() then self.redefinedTransferCartLineItems else self.payPalMethod.genericTransferCartLineItems endif

[29] PayPalExpressCheckoutInWebsite :: guestCheckoutStatus is the redefinedGuestCheckout- Status. If there’s no redefinition at the website level, it is the generic value.

Context PayPalExpressCheckoutInWebsite :: guestCheckoutStatus : Status body: if self.redefinedGuestCheckoutStatus -> notEmpty() then self.redefinedGuestCheckoutStatus else self.payPalExpressCheckout.genericGuestCheckoutStatus endif

[30] GoogleCheckoutInWebsite :: mode is the redefinedMode. If there’s no redefinition at the website level, it is the generic value.

Context GoogleCheckoutInWebsite :: mode : TransactionMode body: if self.redefinedMode -> notEmpty() then self.redefinedMode else self.googleCheckout.genericMode endif

[31] GoogleCheckoutInWebsite :: debugStatus is the redefinedDebugStatus. If there’s no redefi- nition at the website level, it is the generic value.

Context GoogleCheckoutInWebsite :: debugStatus : Status body: if self.redefinedDebugStatus -> notEmpty() then self.redefinedDebugStatus else self.googleCheckout.genericDebugStatus endif

77 [32] GoogleCheckoutInWebsite :: accountInfo is the redefinedAccountInfo. If there’s no redefini- tion at the website level, it is the generic value.

Context GoogleCheckoutInWebsite :: accountInfo : GoogleCheckoutAccount body: if self.redefinedAccountInfo -> notEmpty() then self.redefinedAccountInfo else self.googleCheckout.genericAccountInfo endif

[33] GoogleCheckoutInWebsite :: continueShoppingURL is the redefinedContinueShoppin- gURL. If there’s no redefinition at the website level, it is the generic value.

Context GoogleCheckoutInWebsite :: continueShoppingURL : URL body: if self.redefinedContinueShoppingURL -> notEmpty() then self.redefinedContinueShoppingURL else self.googleCheckout.genericContinueShoppingURL endif

[34] GoogleCheckoutInWebsite :: requireSecureCallback is the redefinedRequireSecureCall- back. If there’s no redefinition at the website level, it is the generic value.

Context GoogleCheckoutInWebsite :: requireSecureCallback : Boolean body: if self.redefinedRequireSecureCallback -> notEmpty() then self.redefinedRequireSecureCallback else self.googleCheckout.genericRequireSecureCallback endif

[35] GoogleCheckoutInWebsite :: disableDefaultTaxTables is the redefinedDisableDefaultTaxTa- bles. If there’s no redefinition at the website level, it is the generic value.

Context GoogleCheckoutInWebsite :: disableDefaultTaxTables : Boolean body: if self.redefinedDisableDefaultTaxTables -> notEmpty() then self.redefinedDisableDefaultTaxTables else self.googleCheckout.genericDisableDefaultTaxTables endif

I Constraints

[1] There is only one payment method for each payment method subtype, so payment methods are identified by its subtypes.

Context PaymentMethod :: isIdentifiedByItsSubtype() : Boolean body: CreditCard.allInstances() -> size() = 1 and ZeroSubtotalCheckout.allInstances() -> size() = 1 and CheckMoney.allInstances() -> size() = 1 and PurchaseOrder.allInstances() -> size() = 1 and AuthorizeNet.allInstances() -> size() = 1 and PayFlowPro.allInstances() -> size() = 1 and PayPalExpressCheckout.allInstances() -> size() = 1 and PayPalDirectPayment.allInstances() -> size() = 1 and PayPalStandard.allInstances() -> size() = 1 and GoogleCheckout.allInstances() -> size() = 1

[2] For a given store view, each payment method has a unique title.

78 Context StoreView :: doesNotHaveTwoPaymentMethodsWithTheSameRedefinedName() : Boolean body: self.paymentMethodInStoreView -> isUnique (title)

[3] Each payment method should be specified in all existing store views in the system.

Context PaymentMethod :: isSpecifiedInAllStoreViews() : Boolean body: PaymentMethod.storeView -> includesAll( StoreView.allInstances() )

[4] Each payment method should be specified in all existing websites in the system.

Context PaymentMethod :: isSpecifiedInAllWebsites() : Boolean body: PaymentMethod.website -> includesAll( Website.allInstances() )

[5] Each credit card method should be specified in all existing websites in the system.

Context CreditCardMethod :: isSpecifiedInAllWebsites() : Boolean body: CreditCardMethod.website -> includesAll( Website.allInstances() )

[6] The simple credit card method should be specified in all existing websites in the system.

Context CreditCard :: isSpecifiedInAllWebsites() : Boolean body: CreditCard.website -> includesAll( Website.allInstances() )

[7] The zero subtotal checkout method should be specified in all existing websites in the system.

Context ZeroSubtotalCheckout :: isSpecifiedInAllWebsites() : Boolean body: ZeroSubtotalCheckout.website -> includesAll( Website.allInstances() )

[8] The check money method should be specified in all existing websites in the system.

Context CheckMoney :: isSpecifiedInAllWebsites() : Boolean body: CheckMoney.website -> includesAll( Website.allInstances() )

[9] The purchase order method should be specified in all existing websites in the system.

Context PurchaseOrder :: isSpecifiedInAllWebsites() : Boolean body: PurchaseOrder.website -> includesAll( Website.allInstances() )

[10] The Authorize.Net method should be specified in all existing websites in the system.

Context AuthorizeNet :: isSpecifiedInAllWebsites() : Boolean body: AuthorizeNet.website -> includesAll( Website.allInstances() )

[11] The Pay Flow Pro method should be specified in all existing websites in the system.

Context PayFlowPro :: isSpecifiedInAllWebsites() : Boolean body: PayFlowPro.website -> includesAll( Website.allInstances() )

[12] Each Pay Pal method should be specified in all existing websites in the system.

Context PayPalMethod :: isSpecifiedInAllWebsites() : Boolean body: PayPalMethod.website -> includesAll( Website.allInstances() )

[13] The Pay Pal Express Checkout method should be specified in all existing websites in the system.

79 Context PayPalExpressCheckout :: isSpecifiedInAllWebsites() : Boolean body: PayPalExpressCheckout.website -> includesAll( Website.allInstances() )

[14] The Pay Pal Direct Payment method should be specified in all existing websites in the system.

Context PayPalDirectPayment :: isSpecifiedInAllWebsites() : Boolean body: PayPalDirectPayment.website -> includesAll( Website.allInstances() )

[15] The Pay Pal Standard method should be specified in all existing websites in the system.

Context PayPalStandard :: isSpecifiedInAllWebsites() : Boolean body: PayPalStandard.website -> includesAll( Website.allInstances() )

[16] The Google Checkout method should specified in all existing websites in the system.

Context GoogleCheckout :: isSpecifiedInAllWebsites() : Boolean body: GoogleCheckout.website -> includesAll( Website.allInstances() )

[17] There is at least one enabled payment method

Context PaymentMethod :: hasOneInstanceEnabledAtLeast() : Boolean body: PaymentMethod.allInstances() -> select (pm | pm.status=Status::Enabled) -> size() >= 1

[18] The PayPal and Google Checkout methods can not define a minimum and maximum order amount allowed

Context PaymentMethodInWebsite :: definesMinAndMaxAllowedOnlyWhenNeeded() : Boolean body: ( self.oclIsTypeOf( PayPalMethodInWebsite ) or self.oclIsTypeOf( GoogleCheckoutInWebsite ) ) implies ( self.minimumAllowed.isUndefined() and self.maximumAllowed.isUndefined() )

[19] The zero subtotal chekout method defines the minimum and maximum order amount allowed as zero.

Context ZeroSubtotalCheckoutInWebsite :: isOnlyAvailableWithFreeOrders() : Boolean body: self.minimumAllowed = 0 and self.maximumAllowed = 0

[20] Google checkout can only be applied at the United Kingdom and the United States.

Context GoogleCheckout :: isOnlyAvailableInUKAndUS() : Boolean body: self.allowedCountry -> forAll ( c | c.name = ’United Kingdom’ or c.name = ’United States’)

I Description The system allows customers to pay through different payment methods, which can be enabled or disabled and configured by the store administrator. Some of the payment methods, like Authorize.net, PayFlow, PayPal or GoogleCheckout, involve an external company. The Credit Card, ZeroSubtotalCheckout and PurchaseOrder methods simply store information for off-line processing. There is also a method available for money order and check payments which do not involve an external merchant. For all shipping methods, some properties should be defined:

• status: defines if the method is available or not for customers.

80 • title: the name the users will see for this method. • allowedCountry: only the Orders shipped to this countries will be able tu use the current shipping method. • newOrderStatus: the Order Status of all new Orders created using this payment method.

For methods using a credit card, we should define which card types are allowed, and if a security validation is needed. Some payment methods have specific or partially shared properties rellevant to the system behaviour:

• minimumTotalAllowed: if the value is defined, the payment method will only be listed for those orders whose total is above the minimum. • maximumTotalAllowed: if the value is defined, the payment method will only be listed for those orders whose total is below the maximum. • invoiceAutomatically: if true, the system will automatically invoice the whole order.

All the payment methods have other specific information about all the data needed to process the payment. It has no effect on the Magento system.

81 5.4.7 Taxes

I Overview In order to supply a flexible use of taxes, product prices are stored tax free. The final price is calculated taking into account the customer tax class, the adress the products will be shipped and the product’s tax class.

I Schema Diagram

I Derivation Rules

[1] TaxRateInStoreView :: name is the redefinedName. If there’s no redefinition at the store view level, it is the generic value.

Context TaxRateInStoreView :: name : String body:

82 if self.redefinedName -> notEmpty() then self.redefinedName else self.taxRate.genericName endif

[2] TaxRate :: applicableAdress are the municipalities where the tax rate can be applied.

Context TaxRate :: applicableAdress : Set(Adress) body: if self.explicitPostalArea -> notEmpty() then self.explicitPostalArea.address -> asSet() else if self.explicitZone -> notEmpty() then self.explicitZone.adress -> union(self.explicitZone.mu- nicipality.address) -> asSet() else self.explicitCountry.address -> union(self.explicitCountry.municipality.address) -> asSet() endif endif

I Constraints

[1] Tax rates are identified by its generic name.

Context TaxRate :: isIdentifiedByGenericName() : Boolean body: TaxRate.allInstances() -> isUnique(genericName)

[2] Tax rules are identified by its name.

Context TaxRule :: isIdentifiedByName() : Boolean body: TaxRule.allInstances() -> isUnique(name)

[3] Customer Tax Classes are identified by its name.

Context CustomerTaxClass :: isIdentifiedByName() : Boolean body: CustomerTaxClass.allInstances() -> isUnique(name)

[4] Product Tax Classes are identified by its name.

Context ProductTaxClass :: isIdentifiedByName() : Boolean body: ProductTaxClass.allInstances() -> isUnique(name)

[5] .

Context TaxRate :: hasZoneAndMunicipalitiesFromTheExplicitCountry() : Boolean body: self.explicitCountry.zone -> includes(self.explicitZone) and self.explicitCountry.zone.municipality.postalArea -> includesAll(self.explicitPostalArea)

[6] If the a taxRate is associated to a Country that has zones and a Zone, the zone should be from this country.

Context TaxRate :: haveACorrectZoneIfNeeded() : Boolean body: ( self.explicitCountry.zone -> notEmpty() and self.explicitZone -> notEmpty() )implies self.- explicitCountry = self.explicitZone.country

[7] A tax rate should be located in either a country, a zone or a municipality, or some of them.

Context TaxRate :: isLocated() : Boolean body: self.explicitCountry -> notEmpty() or self.explicitZone -> notEmpty() or self.explicitPostalArea -> notEmpty()

83 I Description Magento allows Administrators to set up different types of taxes and relate them to the zones where they can be applied. Tax Classes group products or customers that will be treated in the same way when de- ciding which taxes should be applied. Customer Tax Classes group Customers throught Customer Groups, and Product Tax Classes group Products. A Tax Rate define the rate that can be applied as a tax. It also defines where the tax can be applied, by defining a Country, a Zone and/or several Postal Areas. The tax will be applied to Orders which rateApplicationAdress is one of the applicableAdress. Those are calculated when the order is placed, taking the adresses that belong to one of the explicit postal areas defined. If no one is defined, the tax will be able to be applicated to all Orders belonging to the defined Zone, or the whole Country if no Zone is defined. A Tax Rate can be displayed with a different name in each StoreView. This name will be defined as the title attribute of the TaxTitle entity type. If no title is defined for one StoreView, the tax rate identifier will be used instead. There’s no integrity constraint related to tax title’s identification, as a TaxTitle can be identified by its StoreView and TaxRate, the identificable entities that participate in the relationship type that TaxTitle represents. Finally, a Tax Rule defines when a tax Rate will be able to be applied, but now depending on the Customer and Product Tax Classes. A tax rule is associated to three non-empty sets of Customer Tax Classes, Product Tax Classes and Tax Rates. When one rule is applied to an Order Line, it will check if the product ordered and the order’s customer are included in one of its respective tax classes. If true, the tax rates associated to this rule will be able to be applicated, depending on the municipality where the Order is shipped. Moreover, Tax Rules are ordered by priorities, and these priorities can be repeated be- tween rules. When deciding which taxes we should apply to an Order Line, only the rules with the higher priority number will be taken into account. If there are more than one, all of them will be applied.

84 5.5 Customers

5.5.1 Customers

I Overview Magento saves information about system users, which can be either Costumers or Admin- istrators. Magento groupes customers in CostumerGroups.

I Schema Diagram

85 I Derivation Rules

[1] Website :: visibleCustomer are the costumers that can place orders, review and tag prod- ucts, use shopping cards, etc. in this website. Depending on the "shareAccountScope", costumer accounts are visible throught all the websites or just in the website where they where created.

Context Website :: visibleCustomer : Set(Customer) body: if CustomerConfiguration.shareAccountScope = Scope::Global then Customer.allInstances() else self.customer endif

I Constraints

[1] Each customer from the same Website has a different eMail, but if the account visibility is set to Website, it can be repeated through different Websites.

Context Customer :: isIdentifiedByItsEmailAndTheWebsitesWhereIsVisible() : Boolean body: Customer.allInstances() -> isUnique(SeteMail,websiteWhereIsVisible)

[2] Each administrator is identified by its user name.

Context Administrator :: isIdentifiedByItsUserName() : Boolean body: Administrator.allInstances() -> isUnique(userName)

[3] Each administrator is identified by its eMail.

Context Administrator :: isIdentifiedByItsEmail() : Boolean body: Administrator.allInstances() -> isUnique(eMail)

[4] Each role is identified by its name.

Context Role :: isIdentifiedByItsName() : Boolean body: Role.allInstances() -> isUnique(name)

[5] The customer is created in a store view that belongs to the website where it was created.

Context Customer :: isCreatedInACorrectStoreView() : Boolean body: self.websiteWhereIsAssociated.store.storeView -> includes (self.storeViewWhereIsCreated)

[6] At least an instance of customer group called ’NotLoggedIn’ exists in the system.

Context CustomerGroup :: hasTheNotLoggedInInstance() : Boolean body: CustomerGroup.allInstances() -> exists(g|g.name = ’NotLoggedIn’)

[7] At least an instance of customer group called ’General’ exists in the system.

Context CustomerGroup :: hasTheGeneralInstance() : Boolean body: CustomerGroup.allInstances() -> exists(g|g.name = ’General’)

I Description Magento has the following information about Users, even if they are Costumers or Admin- istrators:

86 • firstName: The first name of the user. • lastName: The last name of the user. • password: The password used by a user to be identified when logging in the system. • eMail: The user’s email. There are neither two Costumers nor two Administrators with the same email, but we can have a Customer and an Administrator with the same email. This email is used for identifying costumers when they log in.

Just for Administrators, Magento has the following information:

• userName: An small nickname the administrator will use to identificate himself when logging in the system. • status: Indicates whether the administrator is allowed to access to the admin panel and use it. By this way, an administrator can be disabled without deleting his informa- tion.

Each administrator should be associated to a Role. The role will determine the admin functionalities he will be able to use. For each Costumer, the system saves (or can optionally save) other personal and system information:

• namePrefix • middleName • nameSuffix • dateOfBirth • taxVatNumber: an identifier used for taxation purposes. • createdAt: the time the costumer was created. • lastLoggedIn: the last time the customer logged in the system. • accountConfirmed: indicates whether the system has checked that the customer has entered an existing e-mail.

Costumers are associated to a website. If the user has been created from the front-end, the store view where the registration process have been done is saved. Additonaly, Customers can have many adresses associated. When purchasing a product, the user will choose which of his adresses are used as delivery and billing adresses. The user can previously define what adresses would be used by default, those are the default- Billing and defaultDelivery addresses. Address’ first, middle and last name, name prefix and name suffix may not have the same values in the customer where the address is associated. A customer can specify the store to ship or bill its products to an address where his name doesn’t appear, for example as a gift to a friend. Magento makes you associate each customer to a CustomerGroup. The aim of this Cus- tomer group is to apply some Magento’s features to groups of costumers all at once. A name is defined for each group and identifies it.

87 5.5.2 Sessions

I Overview Each active access to the system is a session. Sessions can be anonymous or can be associated to a registered customer.

I Schema Diagram

I Derivation Rules

[1] Session :: customerGroup for customer sessions, the customer group of the corresponding customer. For anonymous session, the group named ’NotLoggedIn’.

Context Session :: customerGroup : CustomerGroup body: if self.oclIsTypeOf(CustomerSession) then self.oclAsType(CustomerSession).customer.customerGroup else CustomerGroup.allInstances() -> any ( c | c.name = ’NotLoggedIn’ ) endif

[2] Customer :: online indicates whether the costumer is currently using the system. That’s, the customer has an active session

Context Customer :: online : Boolean body: self.customerSession -> notEmpty()

88 I Constraints

[1] Each anonymous session is identified by its IP address.

Context AnonymousSession :: isIdentifiedByItsIPAddress() : Boolean body: AnonymousSession.allInstances() -> isUnique(ipAddress)

[2] Each customer session is identified by its associated customer.

Context CustomerSession :: isIdentifiedByItsCostumer() : Boolean body: CustomerSession.allInstances() -> isUnique(customer)

[3] the current currency should be one of the allowed currencies the session is currently using.

Context Session :: displaysAnAllowedCurrency() : Boolean body: self.storeView.currencyConfigurationInStoreView.allowedCurrency -> includes(self.currentCur- rency)

[4] the session’s store view should be enabled.

Context Session :: isPlacedInAnEnabledStoreView() : Boolean body: self.storeView.status = Status::Enabled

I Description A session maintains information about everyone which is now using the system. There is one CustomerSession for each registered customer logged in the system. There is one AnonymousSession for each person who is currently in the system without being logged in. We will refer to them as Guests. Moreover, there are not Sessions representing an Administrator using now the system. A session saves the following information.

• ipAddress: The IP adress from which a costumer is accessing the site. • sessionStartTime: The time this session has begun. • lastActivityAt: The time the most recent activity in the site has been done.

• currentCurrency: the currency selected by the costumer to see the prices and mon- etary amounts in the site. • storeView: the storeView the customer is currently using.

89 5.6 Store Administration

5.6.1 Products

I Overview The system knows the information about the products of the online store.

I Schema Diagram

90 91 I Derivation Rules

[1] ProductInStoreView :: name is the redefinedName at the current base scope level (the base scope level is defined by the nameScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: name : String body: if self.redefinedName -> notEmpty() and ProductPropertiesScopeConfiguration.nameScope= Scope::StoreView then self.redefinedName

92 else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedName -> notEmpty() and ProductPropertiesScopeConfiguration.nameScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedName else self.product.genericName endif

[2] ProductInWebsite :: netPrice is the redefinedNetPrice. If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInWebsite :: netPrice : Money body: if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedNetPrice -> notEmpty() then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedNetPrice else self.product.genericNetPrice endif

[3] ProductInStoreView :: weight is the redefinedWeight at the current base scope level (the base scope level is defined by the weightScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: weight : Decimal body: if self.redefinedWeight -> notEmpty() and ProductPropertiesScopeConfiguration.weightScope= Scope::StoreView then self.redefinedWeight else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedWeight -> notEmpty() and ProductPropertiesScopeConfiguration.weightScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedWeight else self.product.genericWeight endif

[4] ProductInStoreView :: status is the redefinedStatus at the current base scope level (the base scope level is defined by the statusScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: status : Status body: if self.redefinedStatus -> notEmpty() and ProductPropertiesScopeConfiguration.statusScope= Scope::StoreView then self.redefinedStatus else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedStatus -> notEmpty() and ProductPropertiesScopeConfiguration.statusScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedStatus else self.product.genericStatus endif

[5] ProductInStoreView :: isNewFrom is the redefinedIsNewFrom at the current base scope level (the base scope level is defined by the isNewFromScope property, in product properties scope con- figuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: isNewFrom : Date body: if self.redefinedIsNewFrom -> notEmpty() and ProductPropertiesScopeConfiguration.isNewFrom- Scope= Scope::StoreView then self.redefinedIsNewFrom

93 else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedIsNewFrom -> notEmpty() and ProductPropertiesScopeConfiguration.isNewFromScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedIsNewFrom else self.product.genericIsNewFrom endif

[6] ProductInStoreView :: isNewUntil is the redefinedIsNewUntil at the current base scope level (the base scope level is defined by the isNewUntilScope property, in product properties scope con- figuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: isNewUntil : Date body: if self.redefinedIsNewUntil -> notEmpty() and ProductPropertiesScopeConfiguration.isNewUn- tilScope= Scope::StoreView then self.redefinedIsNewUntil else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedIsNewUntil -> notEmpty() and ProductPropertiesScopeConfiguration.isNewUntilScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedIsNewUntil else self.product.genericIsNewUntil endif

[7] ProductInWebsite :: specialNetPrice is the redefinedSpecialNetPrice. If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInWebsite :: specialNetPrice : Money body: if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedSpecialNetPrice -> notEmpty() then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedSpecialNetPrice else self.product.genericSpecialNetPrice endif

[8] ProductInStoreView :: specialNetPriceFrom is the redefinedSpecialNetPriceFrom at the cur- rent base scope level (the base scope level is defined by the specialNetPriceFromScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: specialNetPriceFrom : Date body: if self.redefinedSpecialNetPriceFrom -> notEmpty() and ProductPropertiesScopeConfiguration.- specialNetPriceFromScope= Scope::StoreView then self.redefinedSpecialNetPriceFrom else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedSpecialNetPriceFrom -> notEmpty() and ProductPropertiesScopeConfiguration.specialNetPriceFromScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedSpecialNetPriceFrom else self.product.genericSpecialNetPriceFrom endif

[9] ProductInStoreView :: specialNetPriceUntil is the redefinedSpecialNetPriceUntil at the current base scope level (the base scope level is defined by the specialNetPriceUntilScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: specialNetPriceUntil : Date body:

94 if self.redefinedSpecialNetPriceUntil -> notEmpty() and ProductPropertiesScopeConfiguration.- specialNetPriceUntilScope= Scope::StoreView then self.redefinedSpecialNetPriceUntil else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedSpecialNetPriceUntil -> notEmpty() and ProductPropertiesScopeConfiguration.specialNetPriceUntilScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedSpecialNetPriceUntil else self.product.genericSpecialNetPriceUntil endif

[10] ProductInStoreView :: description is the redefinedDescription at the current base scope level (the base scope level is defined by the descriptionScope property, in product properties scope con- figuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: description : String body: if self.redefinedDescription -> notEmpty() and ProductPropertiesScopeConfiguration.descrip- tionScope= Scope::StoreView then self.redefinedDescription else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedDescription -> notEmpty() and ProductPropertiesScopeConfiguration.descriptionScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedDescription else self.product.genericDescription endif

[11] ProductInStoreView :: shortDescription is the redefinedShortDescription at the current base scope level (the base scope level is defined by the shortDescriptionScope property, in product prop- erties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: shortDescription : String body: if self.redefinedShortDescription -> notEmpty() and ProductPropertiesScopeConfiguration.short- DescriptionScope= Scope::StoreView then self.redefinedShortDescription else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedShortDescription -> notEmpty() and ProductPropertiesScopeConfiguration.shortDescriptionScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedShortDescription else self.product.genericShortDescription endif

[12] ProductInStoreView :: metaDescription is the redefinedMetaDescription at the current base scope level (the base scope level is defined by the metaDescriptionScope property, in product prop- erties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: metaDescription : String body: if self.redefinedMetaDescription -> notEmpty() and ProductPropertiesScopeConfiguration.metaDe- scriptionScope= Scope::StoreView then self.redefinedMetaDescription else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedMetaDescription -> notEmpty() and ProductPropertiesScopeConfiguration.metaDescriptionScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedMetaDescription else self.product.genericMetaDescription

95 endif

[13] ProductInStoreView :: metaKeyword is the redefinedMetaKeyword at the current base scope level (the base scope level is defined by the metaKeywordScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: metaKeyword : String body: if self.redefinedMetaKeyword -> notEmpty() and ProductPropertiesScopeConfiguration.metaKey- wordScope= Scope::StoreView then self.redefinedMetaKeyword else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedMetaKeyword -> notEmpty() and ProductPropertiesScopeConfiguration.metaKeywordScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedMetaKeyword else self.product.genericMetaKeyword endif

[14] ProductInStoreView :: metaTitle is the redefinedMetaTitle at the current base scope level (the base scope level is defined by the metaTitleScope property, in product properties scope config- uration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: metaTitle : String body: if self.redefinedMetaTitle -> notEmpty() and ProductPropertiesScopeConfiguration.metaTitle- Scope= Scope::StoreView then self.redefinedMetaTitle else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedMetaTitle -> notEmpty() and ProductPropertiesScopeConfiguration.metaTitleScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedMetaTitle else self.product.genericMetaTitle endif

[15] ProductInStoreView :: imageGalleryPath is the redefinedImageGalleryPath at the current base scope level (the base scope level is defined by the imageGalleryPathScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: imageGalleryPath : String body: if self.redefinedImageGalleryPath -> notEmpty() and ProductPropertiesScopeConfiguration.im- ageGalleryPathScope= Scope::StoreView then self.redefinedImageGalleryPath else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedImageGalleryPath -> notEmpty() and ProductPropertiesScopeConfiguration.imageGalleryPathScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedImageGalleryPath else self.product.genericImageGalleryPath endif

[16] ProductInStoreView :: baseImagePath is the redefinedBaseImagePath at the current base scope level (the base scope level is defined by the baseImagePathScope property, in product prop- erties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: baseImagePath : String body: if self.redefinedBaseImagePath -> notEmpty() and ProductPropertiesScopeConfiguration.ba- seImagePathScope= Scope::StoreView then

96 self.redefinedBaseImagePath else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedBaseImagePath -> notEmpty() and ProductPropertiesScopeConfiguration.baseImagePathScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedBaseImagePath else self.product.genericBaseImagePath endif

[17] ProductInStoreView :: smallImagePath is the redefinedSmallImagePath at the current base scope level (the base scope level is defined by the smallImagePathScope property, in product prop- erties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: smallImagePath : String body: if self.redefinedSmallImagePath -> notEmpty() and ProductPropertiesScopeConfiguration.small- ImagePathScope= Scope::StoreView then self.redefinedSmallImagePath else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedSmallImagePath -> notEmpty() and ProductPropertiesScopeConfiguration.smallImagePathScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedSmallImagePath else self.product.genericSmallImagePath endif

[18] ProductInStoreView :: thumbnailPath is the redefinedThumbnailPath at the current base scope level (the base scope level is defined by the thumbnailPathScope property, in product prop- erties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: thumbnailPath : String body: if self.redefinedThumbnailPath -> notEmpty() and ProductPropertiesScopeConfiguration.thumb- nailPathScope= Scope::StoreView then self.redefinedThumbnailPath else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedThumbnailPath -> notEmpty() and ProductPropertiesScopeConfiguration.thumbnailPathScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedThumbnailPath else self.product.genericThumbnailPath endif

[19] ProductInStoreView :: urlKey is the redefinedUrlKey at the current base scope level (the base scope level is defined by the urlKeyScope property, in product properties scope configuration).- If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: urlKey : String body: if self.redefinedUrlKey -> notEmpty() and ProductPropertiesScopeConfiguration.urlKeyScope= Scope::StoreView then self.redefinedUrlKey else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedUrlKey -> notEmpty() and ProductPropertiesScopeConfiguration.urlKeyScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedUrlKey else self.product.genericUrlKey endif

97 [20] ProductInStoreView :: isAvailableForGoogleCheckout is the redefinedIsAvailableFor- GoogleCheckout at the current base scope level (the base scope level is defined by the isAvail- ableForGoogleCheckoutScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: isAvailableForGoogleCheckout : Boolean body: if self.redefinedIsAvailableForGoogleCheckout -> notEmpty() and ProductPropertiesScopeCon- figuration.isAvailableForGoogleCheckoutScope= Scope::StoreView then self.redefinedIsAvailableForGoogleCheckout else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedIsAvailableForGoogleCheckout -> notEmpty() and ProductPropertiesScopeConfiguration.isAvailableForGoogleCheckoutScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedIsAvailableForGoogleCheckout else self.product.genericIsAvailableForGoogleCheckout endif

[21] ProductInStoreView :: giftMessageAllowed is the redefinedGiftMessageAllowed at the cur- rent base scope level (the base scope level is defined by the giftMessageAllowedScope property, in product properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: giftMessageAllowed : Boolean body: if self.redefinedGiftMessageAllowed -> notEmpty() and ProductPropertiesScopeConfiguration.- giftMessageAllowedScope= Scope::StoreView then self.redefinedGiftMessageAllowed else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedGiftMessageAllowed -> notEmpty() and ProductPropertiesScopeConfiguration.giftMessageAllowedScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedGiftMessageAllowed else self.product.genericGiftMessageAllowed endif

[22] ProductInStoreView :: visibleOnCatalog is the redefinedVisibleOnCatalog at the current base scope level (the base scope level is defined by the visibleOnCatalogScope property, in prod- uct properties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: visibleOnCatalog : Boolean body: if self.redefinedVisibleOnCatalog -> notEmpty() and ProductPropertiesScopeConfiguration.visi- bleOnCatalogScope= Scope::StoreView then self.redefinedVisibleOnCatalog else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedVisibleOnCatalog -> notEmpty() and ProductPropertiesScopeConfiguration.visibleOnCatalogScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedVisibleOnCatalog else self.product.genericVisibleOnCatalog endif

[23] ProductInStoreView :: visibleOnSearch is the redefinedVisibleOnSearch at the current base scope level (the base scope level is defined by the visibleOnSearchScope property, in product prop- erties scope configuration). If there’s no redefinition at that level, it is the nearest upper redefined value.

Context ProductInStoreView :: visibleOnSearch : Boolean body: if self.redefinedVisibleOnSearch -> notEmpty() and ProductPropertiesScopeConfiguration.visi- bleOnSearchScope= Scope::StoreView then

98 self.redefinedVisibleOnSearch else if self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.storeView ) ).redefinedVisibleOnSearch -> notEmpty() and ProductPropertiesScopeConfiguration.visibleOnSearchScope!= Scope::Global then self.product.productInWebsite -> select ( piw | piw.website.store.storeView -> includes ( self.- storeView ) ).redefinedVisibleOnSearch else self.product.genericVisibleOnSearch endif

I Constraints

[1] A product is identified by its sku.

Context Product :: isIdentifiedBySku() : Boolean body: Product.allInstances() -> isUnique(sku)

[2] Each product should be related to all existing store views in the system.

Context Product :: isSpecifiedInAllStoreViews() : Boolean body: Product.storeView -> includesAll( StoreView.allInstances() )

[3] Each product should be related to all existing websites in the system.

Context Product :: isSpecifiedInAllWebsites() : Boolean body: Product.website -> includesAll( Website.allInstances() )

[4] The netPrice property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInWebsite :: netPriceIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.de- scriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.netPrice.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.netPrice.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.netPrice.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.netPrice.isUndefined() and (not (associat- edTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.netPrice.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.netPrice.isUndefined()

[5] The weight property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: weightIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.weight.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.weight.isUndefined() and (not (associat- edTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.weight.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.weight.isUndefined() and (not (associat- edTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.weight.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.weight.isUndefined()

99 [6] If Product Properties Obligatoriness Configuration sets that isNewFrom is mandatory, each product should have a isNewFrom defined.

Context ProductInStoreView :: isNewFromIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.isNewFromIsMandatory impliesnot(self.isNewFrom.- isUndefined())

[7] The isNewFrom property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: isNewFromIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.isNewFrom.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.isNewFrom.isUndefined() and (not (as- sociatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.isNewFrom.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.isNewFrom.isUndefined() and (not (asso- ciatedTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.isNewFrom.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.isNewFrom.isUndefined()

[8] If Product Properties Obligatoriness Configuration sets that isNewUntil is mandatory, each product should have a isNewUntil defined.

Context ProductInStoreView :: isNewUntilIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.isNewUntilIsMandatory impliesnot(self.isNewUn- til.isUndefined())

[9] The isNewUntil property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: isNewUntilIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.isNewUntil.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.isNewUntil.isUndefined() and (not (as- sociatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.isNewUntil.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.isNewUntil.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.isNewUntil.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.isNewUntil.isUndefined()

[10] If Product Properties Obligatoriness Configuration sets that specialNetPrice is mandatory, each product should have a specialNetPrice defined.

Context ProductInWebsite :: specialNetPriceIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.specialNetPriceIsMandatory impliesnot(self.- specialNetPrice.isUndefined())

[11] The specialNetPrice property is defined only in products that are from one of the types spec- ified in Product Properties Type Association Configuration.

Context ProductInWebsite :: specialNetPriceIsDefinedOnlyForAssociatedProductTypes() : Boolean body:

100 let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.specialNetPrice.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.specialNetPrice.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.specialNetPrice.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.specialNetPrice.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.specialNet- Price.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.- product.oclIsTypeOf(BundleProduct)) implies self.specialNetPrice.isUndefined()

[12] If Product Properties Obligatoriness Configuration sets that specialNetPriceFrom is manda- tory, each product should have a specialNetPriceFrom defined.

Context ProductInStoreView :: specialNetPriceFromIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.specialNetPriceFromIsMandatory impliesnot(self.- specialNetPriceFrom.isUndefined())

[13] The specialNetPriceFrom property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: specialNetPriceFromIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.specialNetPriceFrom.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Pro- ductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.specialNetPrice- From.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.specialNetPriceFrom.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.specialNetPriceFrom.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Pro- ductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.special- NetPriceFrom.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.specialNetPriceFrom.isUndefined()

[14] If Product Properties Obligatoriness Configuration sets that specialNetPriceUntil is manda- tory, each product should have a specialNetPriceUntil defined.

Context ProductInStoreView :: specialNetPriceUntilIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.specialNetPriceUntilIsMandatory impliesnot(self.- specialNetPriceUntil.isUndefined())

[15] The specialNetPriceUntil property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: specialNetPriceUntilIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.specialNetPriceUntil.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.specialNetPriceUntil.- isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.- product.oclIsTypeOf(ConfigurableProduct)) implies self.specialNetPriceUntil.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.specialNetPriceUntil.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.specialNet- PriceUntil.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.specialNetPriceUntil.isUndefined()

101 [16] If Product Properties Obligatoriness Configuration sets that description is mandatory, each product should have a description defined.

Context ProductInStoreView :: descriptionIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.descriptionIsMandatory impliesnot(self.descrip- tion.isUndefined())

[17] The description property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: descriptionIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.description.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.description.isUndefined() and (not (as- sociatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.description.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.description.isUndefined() and (not (asso- ciatedTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.description.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.description.isUndefined()

[18] If Product Properties Obligatoriness Configuration sets that shortDescription is mandatory, each product should have a shortDescription defined.

Context ProductInStoreView :: shortDescriptionIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.shortDescriptionIsMandatory impliesnot(self.- shortDescription.isUndefined())

[19] The shortDescription property is defined only in products that are from one of the types spec- ified in Product Properties Type Association Configuration.

Context ProductInStoreView :: shortDescriptionIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.shortDescription.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.shortDescription.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.shortDescription.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.shortDescription.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.shortDescrip- tion.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.prod- uct.oclIsTypeOf(BundleProduct)) implies self.shortDescription.isUndefined()

[20] If Product Properties Obligatoriness Configuration sets that metaDescription is mandatory, each product should have a metaDescription defined.

Context ProductInStoreView :: metaDescriptionIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.metaDescriptionIsMandatory impliesnot(self.- metaDescription.isUndefined())

[21] The metaDescription property is defined only in products that are from one of the types spec- ified in Product Properties Type Association Configuration.

Context ProductInStoreView :: metaDescriptionIsDefinedOnlyForAssociatedProductTypes() : Boolean body:

102 let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.metaDescription.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.metaDescription.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.metaDescription.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.metaDescription.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.metaDescrip- tion.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.prod- uct.oclIsTypeOf(BundleProduct)) implies self.metaDescription.isUndefined()

[22] If Product Properties Obligatoriness Configuration sets that metaKeyword is mandatory, each product should have a metaKeyword defined.

Context ProductInStoreView :: metaKeywordIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.metaKeywordIsMandatory impliesnot(self.- metaKeyword.isUndefined())

[23] The metaKeyword property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: metaKeywordIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.metaKeyword.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.metaKeyword.isUnde- fined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.metaKeyword.isUndefined() and (not (associat- edTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) im- plies self.metaKeyword.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.metaKeyword.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.metaKeyword.isUndefined()

[24] If Product Properties Obligatoriness Configuration sets that metaTitle is mandatory, each product should have a metaTitle defined.

Context ProductInStoreView :: metaTitleIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.metaTitleIsMandatory impliesnot(self.metaTi- tle.isUndefined())

[25] The metaTitle property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: metaTitleIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.metaTitle.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.metaTitle.isUndefined() and (not (asso- ciatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.metaTitle.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.metaTitle.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.metaTitle.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.metaTitle.isUndefined()

103 [26] If Product Properties Obligatoriness Configuration sets that imageGalleryPath is mandatory, each product should have a imageGalleryPath defined.

Context ProductInStoreView :: imageGalleryPathIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.imageGalleryPathIsMandatory impliesnot(self.- imageGalleryPath.isUndefined())

[27] The imageGalleryPath property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: imageGalleryPathIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.imageGalleryPath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.imageGalleryPath.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.imageGalleryPath.isUndefined() and (not (asso- ciatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.imageGalleryPath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Pro- ductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.image- GalleryPath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.imageGalleryPath.isUndefined()

[28] If Product Properties Obligatoriness Configuration sets that baseImagePath is mandatory, each product should have a baseImagePath defined.

Context ProductInStoreView :: baseImagePathIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.baseImagePathIsMandatory impliesnot(self.- baseImagePath.isUndefined())

[29] The baseImagePath property is defined only in products that are from one of the types spec- ified in Product Properties Type Association Configuration.

Context ProductInStoreView :: baseImagePathIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.baseImagePath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.baseImagePath.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.baseImagePath.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.baseImagePath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.baseImagePath.- isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.- oclIsTypeOf(BundleProduct)) implies self.baseImagePath.isUndefined()

[30] If Product Properties Obligatoriness Configuration sets that smallImagePath is mandatory, each product should have a smallImagePath defined.

Context ProductInStoreView :: smallImagePathIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.smallImagePathIsMandatory impliesnot(self.- smallImagePath.isUndefined())

[31] The smallImagePath property is defined only in products that are from one of the types spec- ified in Product Properties Type Association Configuration.

104 Context ProductInStoreView :: smallImagePathIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.smallImagePath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.smallImagePath.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.smallImagePath.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.smallImagePath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.smallImagePath.- isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.- oclIsTypeOf(BundleProduct)) implies self.smallImagePath.isUndefined()

[32] If Product Properties Obligatoriness Configuration sets that thumbnailPath is mandatory, each product should have a thumbnailPath defined.

Context ProductInStoreView :: thumbnailPathIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.thumbnailPathIsMandatory impliesnot(self.- thumbnailPath.isUndefined())

[33] The thumbnailPath property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: thumbnailPathIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.thumbnailPath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.thumbnailPath.isUnde- fined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.- oclIsTypeOf(ConfigurableProduct)) implies self.thumbnailPath.isUndefined() and (not (associated- Types -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.thumbnailPath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.thumbnailPath.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.thumbnailPath.isUndefined()

[34] If Product Properties Obligatoriness Configuration sets that urlKey is mandatory, each product should have a urlKey defined.

Context ProductInStoreView :: urlKeyIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.urlKeyIsMandatory impliesnot(self.urlKey.isUn- defined())

[35] The urlKey property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: urlKeyIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.urlKey.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.urlKey.isUndefined() and (not (associat- edTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.urlKey.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.urlKey.isUndefined() and (not (associated- Types -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct))

105 implies self.urlKey.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.urlKey.isUndefined()

[36] If Product Properties Obligatoriness Configuration sets that isAvailableForGoogleCheckout is mandatory, each product should have a isAvailableForGoogleCheckout defined.

Context ProductInStoreView :: isAvailableForGoogleCheckoutIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.isAvailableForGoogleCheckoutIsMandatory im- pliesnot(self.isAvailableForGoogleCheckout.isUndefined())

[37] The isAvailableForGoogleCheckout property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: isAvailableForGoogleCheckoutIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.isAvailableForGoogleCheckout.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.isAvailable- ForGoogleCheckout.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.product.oclIsTypeOf(ConfigurableProduct)) implies self.isAvailableForGoogleCheckout.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.- oclIsTypeOf(VirtualProduct)) implies self.isAvailableForGoogleCheckout.isUndefined() and (not (as- sociatedTypes -> exists ( pt | pt = ProductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.isAvailableForGoogleCheckout.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.isAvailableFor- GoogleCheckout.isUndefined()

[38] If Product Properties Obligatoriness Configuration sets that giftMessageAllowed is manda- tory, each product should have a giftMessageAllowed defined.

Context ProductInStoreView :: giftMessageAllowedIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.giftMessageAllowedIsMandatory impliesnot(self.- giftMessageAllowed.isUndefined())

[39] The giftMessageAllowed property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: giftMessageAllowedIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.giftMessageAllowed.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.giftMessageAllowed.- isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.- product.oclIsTypeOf(ConfigurableProduct)) implies self.giftMessageAllowed.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.giftMessageAllowed.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Pro- ductType::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.giftMes- sageAllowed.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.product.oclIsTypeOf(BundleProduct)) implies self.giftMessageAllowed.isUndefined()

[40] If Product Properties Obligatoriness Configuration sets that visibleOnCatalog is mandatory, each product should have a visibleOnCatalog defined.

Context ProductInStoreView :: visibleOnCatalogIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.visibleOnCatalogIsMandatory impliesnot(self.- visibleOnCatalog.isUndefined())

106 [41] The visibleOnCatalog property is defined only in products that are from one of the types specified in Product Properties Type Association Configuration.

Context ProductInStoreView :: visibleOnCatalogIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.visibleOnCatalog.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.visibleOnCatalog.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.visibleOnCatalog.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.visibleOnCatalog.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.visibleOn- Catalog.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.- product.oclIsTypeOf(BundleProduct)) implies self.visibleOnCatalog.isUndefined()

[42] If Product Properties Obligatoriness Configuration sets that visibleOnSearch is mandatory, each product should have a visibleOnSearch defined.

Context ProductInStoreView :: visibleOnSearchIsMandatoryIfDefined() : Boolean body: ProductPropertiesObligatorinessConfiguration.visibleOnSearchIsMandatory impliesnot(self.- visibleOnSearch.isUndefined())

[43] The visibleOnSearch property is defined only in products that are from one of the types spec- ified in Product Properties Type Association Configuration.

Context ProductInStoreView :: visibleOnSearchIsDefinedOnlyForAssociatedProductTypes() : Boolean body: let associatedTypes : Set(ProductType) = ProductPropertiesTypeAssociationConfiguration.- descriptionAssociatedToProductType in (not (associatedTypes -> exists ( pt | pt = ProductType::Simple)) and self.product.oclIsTypeOf(SimpleProduct)) implies self.visibleOnSearch.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Grouped)) and self.product.oclIsTypeOf(GroupedProduct)) implies self.visibleOnSearch.isUn- defined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Configurable)) and self.prod- uct.oclIsTypeOf(ConfigurableProduct)) implies self.visibleOnSearch.isUndefined() and (not (associ- atedTypes -> exists ( pt | pt = ProductType::Virtual)) and self.product.oclIsTypeOf(VirtualProduct)) implies self.visibleOnSearch.isUndefined() and (not (associatedTypes -> exists ( pt | pt = Product- Type::Downloadable)) and self.product.oclIsTypeOf(DownloadableProduct)) implies self.visibleOn- Search.isUndefined() and (not (associatedTypes -> exists ( pt | pt = ProductType::Bundle)) and self.- product.oclIsTypeOf(BundleProduct)) implies self.visibleOnSearch.isUndefined()

I Description Product information is saved in a multi-level structure, depending on the scope. We detail, firstly, those properties that are defined globally (its value for a given product is the same anywhere in the system):

• sku: a unique identifier for each distinct product that can be purchased. • quantity: the product’s quantity in stock. • stockStatus: even if some quantity of this product is in stock, the product can be set to ’Out of Stock’.

The following attributes redefine, for this product, some generic stock configuration prop- erties of the system. They are also defined globally. See the store configuration section for further information.

107 • qtyToBecomeOutOfStock • minQtyAllowedInShoppingCart • maxQtyAllowedInShoppingCart • backOrderPolicy • notifyForQuantityBelow

Most of the product properties works as follows: for a given product and property, a generic (global) value should be defined. But this value can be redefined for each website or store view. The property’s value for a determinate product in a store view is calculated as the more specific redefinition value. We can also tell the system to ignore some redefinitions, by changing the corresponding property in the Scope Properties Configuration. We detail here only the most important:

• name • netPrice: the net price of this product, without applying taxes or special offers. It can be redefined at the website level, but not at the store view level. • specialNetPrice: a different price that can be used as a temporary offer. It can be redefined at the website level, but not at the store view level. • weight: the product’s weight. Used for calculating the shipping costs in some shipping methods. .

The Product Properties Type Association Configuration determine, for each property, that only the products that are from the types defined here can have this property defined. The Product Properties Scope Configuration determine, for each property, the lower scope where redefinitions are taken into account when calculating the value for this property in a store view. The Product Properties Obligatoriness Configuration determine, for each property, if all products should mandatory have this property defined (at least at the global level) or not. A product is available in a Website if it can be purchased in the stores of this website. Magento establishes three types of relations between two different products:

• relatedProduct: meant to be purchased in addition to the item the customer is view- ing. • upSellProduct: items that customers would ideally buy instead of the product they are viewing. • crossSellProduct: items that a customer who is buying the product in question might enjoy, but that are not directly related.

108 5.6.2 Product Options

I Overview Magento allows several options to be defined for each product, in order to offer multiple options of a product without needing to create many separate but very similar products

I Schema Diagram

I Derivation Rules

[1] OptionInStoreView :: name is the specific name defined for this store view, or the generic name if it is not defined.

Context OptionInStoreView :: name : String body: if self.redefinedName -> notEmpty() then self.redefinedName else self.option.genericName endif

[2] OptionValueInStoreView :: name is the specific name defined for this store view, or the generic name if it is not defined.

Context OptionValueInStoreView :: name : String body:

109 if self.redefinedName -> notEmpty() then self.redefinedName else self.optionValue.genericName endif

I Constraints

[1] Options are identified by its generic name and the product they belong to.

Context Option :: isIdentifiedByItsGenericNameAndProduct() : Boolean body: Option.allInstances() -> isUnique(SetgenericName,product)

[2] Options are also identified by its store view and name.

Context OptionInStoreView :: isIdentifiedByItsStoreViewAndName() : Boolean body: OptionInStoreView.allInstances() -> isUnique(SetstoreView, name)

[3] Option values are identified by its generic name.

Context OptionValue :: isIdentifiedByItsGenericName() : Boolean body: OptionValue.allInstances() -> isUnique(genericName)

[4] Option values are also identified by its store view and name.

Context OptionValueInStoreView :: isIdentifiedByItsStoreViewAndName() : Boolean body: OptionValueInStoreView.allInstances() -> isUnique(SetstoreView, name)

[5] Each option should be specified in all existing StoreViews in the system.

Context Option :: isSpecifiedInAllStoreViews() : Boolean body: self.storeView -> includesAll( StoreView.allInstances() )

[6] Each option value should be specified in all existing StoreViews in the system.

Context OptionValue :: isSpecifiedInAllStoreViews() : Boolean body: self.storeView -> includesAll( StoreView.allInstances() )

[7] An Option Value defines, for the options it belongs to, a relative or a fixed increment, but not both.

Context OptionValueInOption :: definesRelativeOrFixedIncrementButNotAll() : Boolean body: ( self.fixedIncrement.isUndefined() <> self.relativeIncrement.isUndefined() )

[8] An option with user-defined-content defines a relative or a fixed increment, but not both.

Context OptionWithContentDefinedByCustomer :: definesRelativeOrFixedIncrementButNotAll() : Boolean body: ( self.fixedIncrement.isUndefined() <> self.relativeIncrement.isUndefined() )

I Description Usually, there are products which are sold in different Options. The store administrator defines which are the options of each product. When a customer purchases it, the customer should define which value does he give to the options of this product. Options with content defined by the customer allow the customer to give any textual or date value to this option. The sign and increments define an extra amount of money that will be charged or deduced for giving a value to that option. For Options with predefined content, the store administrator should define several Option Values. When a customer purchases this product, he will choose one (or multiple) value(s)

110 for this option. Each Option Value can define an increment that will be applied if the cus- tomer choses it. The system also requires information about:

• isRequired: defines if the customer must give a value to the product when purchasing it, or if it is optional. • skuAppendix: used if the store administrator wants to associate different sku’s to the product depending on the options choosen. The ’skuAppendix’ will be added at the end of the original’s product ’sku’.

111 5.6.3 Product Types

I Overview Products offerded by the store can be classified into six types: Simple, Grouped, Config- urable, Virtual, Downloadable and Bundle products.

I Schema Diagram

112 I Derivation Rules

[1] Product :: productType denotes the product type as an enumeration value.

Context Product :: productType : ProductType body: if self.oclIsTypeOf(SimpleProduct) then ProductType::Simple else if self.oclIsTypeOf(GroupedProduct) then ProductType::Grouped else if self.oclIsTypeOf(ConfigurableProduct) then ProductType::Configurable else if self.oclIsTypeOf(VirtualProduct) then ProductType::Virtual else if self.oclIsTypeOf(DownloadableProduct) then ProductType::Downloadable else ProductType::Bundle endif endif endif endif endif

I Constraints

113 [1] Bundle product items are identified by its name.

Context BundleProductItem :: isIdentifiedByItsName() : Boolean body: BundleProductItem.allInstances() -> isUnique(name)

[2] Downloadable items are identified by its name and downloadable product.

Context DownloadableItem :: isIdentifiedByItsNameAndDownloadableProduct() : Boolean body: DownloadableItem.allInstances() -> isUnique(Setname,downloadableProduct)

[3] Configurable attributes should have its ’isConfigurable’ property set to true.

Context ConfigurableProduct :: hasOnlyCorrectConfigurableAttributes() : Boolean body: self.configurableAttribute -> forAll ( ca | ca.isConfigurable )

[4] A configurable product is able to rate the same attributes than all its associated products.

Context ConfigurableProduct :: isAssociatedToProductsWithTheSameAbleToRateAttributes() : Boolean body: self.associatedProduct -> forAll ( ap | ap.ableToRateAttribute -> select ( a | self.configurableAttribute -> includes(a) ) = self.config- urableAttribute )

[5] Each simple product associated to a configurable product should have a different combination of the values assigned to its configurable attributes.

Context ConfigurableProduct :: hasNoAssociatedProductsRepeated() : Boolean body: self.associatedProduct -> forAll ( p1, p2 | p1 <> p2 implies not ( p1.ableToRateAttribute -> select ( a | self.configurableAttribute -> includes(a) ).storeViewAt- tributeRating.attributeValue = p2.ableToRateAttribute -> select ( a | self.configurableAttribute -> includes(a) ).storeViewAt- tributeRating.attributeValue ))

[6] Bundle product items have one and only one bundle product option as default.

Context BundleProductItem :: hasOnlyOneDefaultOption() : Boolean body: self.bundleProductOption -> select ( bpo | bpo.isDefault) -> size() = 1

[7] Bundle product options are simple, downloadable or virtual products.

Context BundleProductOption :: isAProductOfTheCorrectType() : Boolean body: self.product.oclIsTypeOf( SimpleProduct ) or self.product.oclIsTypeOf( DownloadableProduct ) or self.product.oclIsTypeOf( VirtualProduct )

[8] Grouped, Configurable, Virtual, Downloadable and Bundle products are not associated to the weight attribute.

Context ProductPropertiesTypeAssociationConfiguration :: determineWeightOnlyForCorrectProduct- Types() : Boolean body: self.weightAssociatedToProductType -> excludesAll ( Set ProductType::Grouped, ProductType::Configurable, ProductType::Bundle, ProductType::Virtual, ProductType::Downloadable )

[9] Grouped products are not associated to any attribute related to the price.

114 Context ProductPropertiesTypeAssociationConfiguration :: determinePriceRelatedAttributesOnlyFor- CorrectProductTypes() : Boolean body: self.netPriceAssociatedToProductType -> excludes ( ProductType::Grouped ) and self.specialNetPriceAssociatedToProductType -> excludes ( ProductType::Grouped ) and self.specialNetPriceFromAssociatedToProductType -> excludes ( ProductType::Grouped ) and self.specialNetPriceUntilAssociatedToProductType -> excludes ( ProductType::Grouped )

[10] Grouped are not associated to the price attribute, other product types are.

Context ProductPropertiesTypeAssociationConfiguration :: determineNetPriceOnlyForCorrectProduct- Types() : Boolean body: self.netPriceAssociatedToProductType = SetProductType::Simple,ProductType::Configurable,ProductType::Downloadable,ProductType::Virtual,ProductType::Bundle;

[11] Grouped, Configurable, Virtual, Downloadable and Bundle products are not associated to the allowed gift message attribute.

Context ProductPropertiesTypeAssociationConfiguration :: determineAllowedGiftMessageOnlyFor- CorrectProductTypes() : Boolean body: self.allowedGiftMessageAssociatedToProductType -> excludesAll ( Set ProductType::Grouped, Pro- ductType::Configurable, ProductType::Bundle, ProductType::Virtual, ProductType::Downloadable )

[12] Groped, Configurable and Bundle Products do not have any inventory property, excepting the stock status.

Context Product :: hasInventoryPropertiesOnlyIfNeeded() : Boolean body: ( self.oclIsTypeOf(GroupedProduct) or self.oclIsTypeOf(ConfigurableProduct) or self.oclIsTypeOf(BundleProduct) ) implies ( self.quantity.isUndefined() and self.qtyToBecomeOutOfStock.isUndefined() and self.minQtyAllowedInShoppingCart.isUndefined() and self.maxQtyAllowedInShoppingCart.isUndefined() and self.notifyForQuantityBelow.isUndefined()and self.backOrderPolicy.isUndefined() )

[13] Simple, Virtual and Downloadable Products must define its quantity property.

Context Product :: hasQuantityPropertyWhenNeeded() : Boolean body: ( self.oclIsTypeOf(SimpleProduct) or self.oclIsTypeOf(VirtualProduct) or self.oclIsTypeOf(DownloadableProduct) ) implies self.quantity.isDefined()

[14] The sign and increment attributes are only defined if the downloadable product allows items to be purchased separatelly, but only one increment (fixed or relative) is defined.

Context DownloadableItem :: definesIncrementWhenNeeded() : Boolean body: if self.downloadableProduct.purchaseItemsSeparatelly = Status::Enabled then self.sign.isDefined() and ( self.fixedIncrement.isUndefined() = self.relativeIncrement.isDefined() ) else self.sign.isUndefined() and self.fixedIncrement.isUndefined() and self.relativeIncrement.isUn- defined() endif

[15] A Configuration defines a relative or a fixed increment, but not both.

115 Context ConfigurationOfAConfigurableProduct :: definesRelativeOrFixedIncrementButNotAll() : Boolean body: ( self.fixedIncrement.isUndefined() = self.relativeIncrement.isDefined() )

[16] The sign and increment attributes are only defined if the bundle product allows items to be purchased separatelly, but only one increment (fixed or relative) is defined.

Context BundleProductOption :: definesIncrementWhenNeeded() : Boolean body: if self.bundleProductItem.bundleProduct.priceMethod = PriceMethod::Fixed then self.sign.isDefined() and ( self.fixedIncrement.isUndefined() = self.relativeIncrement.isDefined() ) else self.sign.isUndefined() and self.fixedIncrement.isUndefined() and self.relativeIncrement.isUn- defined() endif

[17] Grouped Products and Bundle Products with the Fixed price do not have any Option.

Context Product :: hasOptionsOnlyIfNeeded() : Boolean body: self.oclIsTypeOf(GroupedProduct) or ( self.oclIsTypeOf(BundleProduct) and self.oclAsType(BundleProduct).- priceMethod = PriceMethod::Fixed ) implies self.option -> isEmpty()

[18] Grouped Products could not be associated to simple products with options.

Context GroupedProduct :: isNotAssociatedToProductsWithOptions() : Boolean body: self.associatedProduct -> forAll ( ap | ap.option -> isEmpty() )

[19] Configurable Products could not be associated to simple products with options.

Context ConfigurableProduct :: isNotAssociatedToProductsWithOptions() : Boolean body: self.associatedProduct -> forAll ( ap | ap.option -> isEmpty() )

[20] Bundle Products could not be associated to products with options.

Context BundleProduct :: isNotAssociatedToProductsWithOptions() : Boolean body: self.bundleProductItem.product -> forAll ( p | p.option -> isEmpty() )

I Description A Simple Product is an individual product, the attributes of which are determined by the store owner. A Grouped Product is a set of simple products that are sold together as a pack. A Configurable Product allows customers to configure some of its attributes when pur- chasing it (such as color, size...). The Configurable Product represents the abstract non- configurated product and is related to a set of Configured Products, each one representing one concrete available configuration. One or more configurableAttributes should be de- fined. The system will rely the configuration chosen by the customer to the simple asso- ciated product that matches it. Although this can be performed in a simpler way by using product options, this method allows the store administrator to keep track of the inventory for each configurated product. A Configurated Product is a Simple Product that can be sold as a concrete configuration of a Configurable Product. Virtual and Downloadable Products represent both non-physical items. The difference is that downloadable products are for digitized stuff that can be downloaded by the customer.- The downloadable product record has a function to either point to a URL or file. Virtual products are more akin to a service.

116 Bundle products could be considered as a combination of grouped and configurable prod- ucts. A Bundle Product has the following attributes:

• shippingMode: determines if the products will be shipped together or separately. • priceMethod: determines the way the price will be calculated. • specialNetPriceReduction: specifies a percentage reduction that will be applied to its original price to define the product’s special net price.

When a customer purchases a bundle product, he has to choose an option (or more) for each bundle product item. Magento saves the following information about bundle product items:

• name • selectionType: determines if only one or more than bundle product options can be choosen by a customer when purchasing the product. • isRequired: determines if no bundle product option can be choosen for this item.

Each bundle product option is associated to one product. Magento also saves the following information:

• defaultQty: the default quantity that the customer will purchase of that product, if he chooses it. • isQtyRedefinable: determines if the customer can change the default quantity to pur- chase or not.

• isDefault: determines if this is the option selected by default for this bundle product item.

117 5.6.4 Categories

I Overview Products are grouped into categories which are arranged hierarchically.

I Schema Diagram

I Operations

[1] context Category def: allParents () : Set(Category) =

self.parent -> union(self.parent.allParents())

I Derivation Rules

[1] CategoryInStoreView :: name is the redefinedName at the current base scope level, if it is defined, or the genericName of the category.

Context CategoryInStoreView :: name : String body: if self.redefinedName -> notEmpty() then self.redefinedName else self.category.genericName

118 endif

[2] CategoryInStoreView :: description is the redefinedDescription at the current base scope level, if it is defined, or the genericDescription of the category.

Context CategoryInStoreView :: description : String body: if self.redefinedDescription -> notEmpty() then self.redefinedDescription else self.category.genericDescription endif

[3] CategoryInStoreView :: imagePath is the redefinedImagePath at the current base scope level, if it is defined, or the genericImagePath of the category.

Context CategoryInStoreView :: imagePath : String body: if self.redefinedImagePath -> notEmpty() then self.redefinedImagePath else self.category.genericImagePath endif

[4] CategoryInStoreView :: pageTitle is the redefinedPageTitle at the current base scope level, if it is defined, or the genericPageTitle of the category.

Context CategoryInStoreView :: pageTitle : String body: if self.redefinedPageTitle -> notEmpty() then self.redefinedPageTitle else self.category.genericPageTitle endif

[5] CategoryInStoreView :: metaKeyword is the redefinedMetaKeyword at the current base scope level, if it is defined, or the genericMetaKeyword of the category.

Context CategoryInStoreView :: metaKeyword : Set(String) body: if self.redefinedMetaKeyword -> notEmpty() then self.redefinedMetaKeyword else self.category.genericMetaKeyword endif

[6] CategoryInStoreView :: metaDescription is the redefinedMetaDescription at the current base scope level, if it is defined, or the genericMetaDescription of the category.

Context CategoryInStoreView :: metaDescription : String body: if self.redefinedMetaDescription -> notEmpty() then self.redefinedMetaDescription else self.category.genericMetaDescription endif

[7] CategoryInStoreView :: status is the redefinedStatus at the current base scope level, if it is defined, or the genericStatus of the category.

Context CategoryInStoreView :: status : Boolean body: if self.redefinedStatus -> notEmpty() then self.redefinedStatus else self.category.genericStatus

119 endif

I Constraints

[1] Categories are identified by its generic name and its parent category.

Context Category :: isIdentifiedByItsGenericNameAndParentCategory() : Boolean body: Category.allInstances() -> isUnique (SetgenericName,parent)

[2] For a given store view, each category has a unique name.

Context StoreView :: doesNotHaveTwoCategoriesWithTheSameRedefinedName() : Boolean body: self.categoryInStoreView -> isUnique (name)

[3] Each category should be related to all existing store views in the system.

Context Category :: isSpecifiedInAllStoreViews() : Boolean body: self.storeView -> includesAll( StoreView.allInstances() )

[4] There are no cycles in category hierarchy.

Context Category :: isAHierarchy() : Boolean body: not self.allParents() -> includes(self)

I Description Magento groups products into categories which are arranged hierarchically. Categories have the following attributes:

• name: the category’s name. • status: determines if the category is visible and accessible from the front end. • urlKey: a relative URL path which can be used to acess automatically to the category page. • description: the category’s description. • imagePath: a path to the image that will be associated to the category. • pageTitle: the title that will be shown in the category’s page. • metaKeyword: meta keywords of the category. • metaDescription: the meta description of the category.

These attributes are defined for the whole system, but they can be redefined at the store view level.

120 5.6.5 Attributes

I Overview Magento allows editing product attributes and creating new user-defined attributes.

I Schema Diagram

121 I Derivation Rules

[1] AttributeInStoreView :: name is the specific name defined for this store view, or the generic name if it is not defined.

Context AttributeInStoreView :: name : String body: if self.redefinedName -> notEmpty() then self.redefinedName else self.attribute.genericName endif

[2] Product :: ableToRateAttribute are the attributes that can be given a value for this product: all the system attributes and the simple attributes associated to the attribute set of this product.

Context Product :: ableToRateAttribute : Set(Attribute) body: self.attributeSet.attribute

I Constraints

122 [1] Attributes are identified by its internal code.

Context Attribute :: isIdentifiedByItsCode() : Boolean body: Attribute.allInstances() -> isUnique(code)

[2] Attribute sets are identified by its name.

Context AttributeSet :: isIdentifiedByItsName() : Boolean body: AttributeSet.allInstances() -> isUnique(name)

[3] Each attribute should be specified in all existing StoreViews in the system.

Context Attribute :: isSpecifiedInAllStoreViews() : Boolean body: Attribute.storeView -> includesAll( StoreView.allInstances() )

[4] Image attributes doesn’t have those attributes defined. Other type of attributes should manda- tory have it defined.

Context Attribute :: hasTheUniqueAndRequiredPropertiesWhenNeeded() : Boolean body: if self.oclIsTypeOf(ImageAttribute) then self.hasUniqueValues -> isEmpty() and self.isRequired -> isEmpty() else self.hasUniqueValues -> notEmpty() and self.isRequired -> Empty() endif

[5] A single valued enumeration attribute can be configurable only if its scope is set to global.

Context SingleValuedEnumerationAttribute :: canBeConfigurableOnlyIfItIHasGlobalScope() : Boolean body: self.scope <> Scope::Global implies self.isConfigurable = false

I Description Attributes are properties of products in the catalog. Users can create, delete them and define some characteristics. For each attribute, users can set the following information:

• code: an internal code to identify this attribute. • genericName: the name this attribute will be shown with in all store views. • scope: the level at which the values of this attribute are shared. • isComparable: determines whether the attribute values are usefull for customers to compare two or more products. • hasUniqueValues: if it is true, the value selected or entered for this attribute for each product must be different. • isRequired: if it is true, a value must be selected for this attribute for each product you create. • associatedToProductType: the Product Types that will include this attribute.

The name of the attribute can be redefined for each storeView. Depending on the type of data that they represent, attributes can be classified into:

• TextualAttribute • BooleanAttribute • PriceAttribute • DateAttribute

123 • IntegerAttribute • DecimalAttribute • ImageAttribute • EnumerationAttribute: can be rated only with a limited set of Literal Values. • MultipleValuedEnumerationAttribute: an Enumeration Attribute that can be rated with more than one literal value at the same time. • SingleValuedEnumerationAttribute: an Enumeration Attribute that can be rated with only one literal value at the same time.

Most types of attributes allow defining a default value. Some of them have also specific properties:

• inputValidation: makes the system to check if the value assigned to this attribute matches an specific pattern, such as an email or an URL. It is only applied to Textual Attributes. • isConfigurable: determines if this attribute can be setted to configurable in a config- urable product. It is only applied to Single Valued Enumeration Attributes. See the product types section for more information.

Attributes can be assigned to one or more Attribute Sets. Each product is related to only one attribute set. For a given product, we are ableToRate all the attributes of its attribute set.

124 5.6.6 Attribute Values

I Overview Attributes should have a value for each product. Magento allows defining the scope in which a product value is shared.

I Schema Diagram

I Derivation Rules

[1] StoreViewAttributeRating :: attributeValue is the value redefined for this store view. If it is not defined, the value redefined in the website this store view belongs to. Inf it is not defined, the generic value.

Context StoreViewAttributeRating :: attributeValue : AttributeValue body: if self.redefinedAttributeValue -> notEmpty() and self.attribute.scope = Scope::StoreView then self.redefinedAttributeValue else if self.storeView.store.website.websiteAttributeRating -> select ( war | war.product = self.- product and war.attribute = self.attribute ).redefinedAttributeValue -> notEmpty() and self.attribute.- scope != Scope::Global then self.storeView.store.website.websiteAttributeRating -> select ( war | war.product = self.product and war.attribute = self.attribute ).redefinedAttributeValue

125 else self.product.globalAttributeRating -> select ( gar | gar.attribute = self.attribute).genericAttribute- Value endif

[2] LiteralValueInStoreView :: name is the specific name defined for this store view, or the generic name if it is not defined.

Context LiteralValueInStoreView :: name : String body: if self.redefinedName -> notEmpty() then self.redefinedName else self.attribute.genericName endif

I Constraints

[1] Textual values are identified by its value.

Context TextualValue :: isIdentifiedByItsValue() : Boolean body: TextualValue.allInstances() -> isUnique(value)

[2] Boolean values are identified by its value.

Context BooleanValue :: isIdentifiedByItsValue() : Boolean body: BooleanValue.allInstances() -> isUnique(value)

[3] Price values are identified by its value.

Context PriceValue :: isIdentifiedByItsValue() : Boolean body: PriceValue.allInstances() -> isUnique(value)

[4] Date values are identified by its value.

Context DateValue :: isIdentifiedByItsValue() : Boolean body: DateValue.allInstances() -> isUnique(value)

[5] Integer values are identified by its value.

Context IntegerValue :: isIdentifiedByItsValue() : Boolean body: IntegerValue.allInstances() -> isUnique(value)

[6] Decimal values are identified by its value.

Context DecimalValue :: isIdentifiedByItsValue() : Boolean body: DecimalValue.allInstances() -> isUnique(value)

[7] Image values are identified by its value.

Context ImageValue :: isIdentifiedByItsImagePath() : Boolean body: ImageValue.allInstances() -> isUnique(path)

[8] Enumeration values are identified by its generic name and the enumeration attribute it belongs to.

Context EnumerationValue :: isIdentifiedByItsGenericNameAndAttribute() : Boolean body: EnumerationValue.allInstances() -> isUnique(SetgenericName, enumerationAttribute)

[9] Multiple enumeration values are identified by its iset of enumeration values.

126 Context MultipleEnumerationValue :: isIdentifiedByItsSetOfEnumerationValues() : Boolean body: MultipleEnumerationValue.allInstances() -> isUnique(enumerationValue)

[10] A literal value is associated to all the storeviews.

Context EnumerationValue :: isSpecifiedInAllStoreViews() : Boolean body: self.storeView -> includesAll ( StoreView.allInstances() )

[11] A product and attribute can only participate in the attribute rating relationship if the attribute is an able to rate attribute of this product.

Context Product :: isOnlyRatedForItsAbleToRateProducts() : Boolean body: self.ableToRateAttribute -> includesAll ( self.globalAttributeRating.attribute ) and self.ableToRateAttribute -> includesAll ( self.websiteAttributeRating.attribute ) and self.ableToRateAttribute -> includesAll ( self.storeViewAttributeRating.attribute )

[12] All the able to rate attributes have a global attribute rating, one website rating for each website and one store view rating for each store view in the system.

Context Product :: hasAGlobalWebsiteAndStoreViewRatingForAllAbleToRateProducts() : Boolean body: self.globalAttributeRating.attribute -> includesAll ( self.ableToRateAttribute ) and self.ableToRateAttribute -> forAll ( a | self.websiteAttributeRating -> select ( gar | gar.attribute = a ).website -> includesAll (Website.- allInstances()) ) and self.ableToRateAttribute -> forAll ( a | self.storeViewAttributeRating -> select ( gar | gar.attribute = a ).storeView -> includesAll (Store- View.allInstances()) )

[13] A product has given a value to each required attribute it is related to.

Context Product :: hasARatingForAllRequiredAttributes() : Boolean body: self.ableToRateAttribute -> forall ( av | (av.isRequired.notEmpty() and av.isRequired) implies self.storeViewAttributeRating -> forall ( sar | sar.storeViewRatedAttribute=av implies sar.attributeValue.notEmpty() ) )

[14] If an attribute is set to be unique, there are no two different products that assign it the same value, even in different store views.

Context Attribute :: ratingsRespectTheUniqueConstraint() : Boolean body: (self.hasUniqueValues -> notEmpty() andself.hasUniqueValues) implies self.storeViewAttributeR- ating -> forall ( sar1, sar2 | sar1.attributeValue = sar2.attributeValue implies ar1.product = ar2.product )

[15] Only text field values are given to a text field attribute.

Context TextualAttribute :: isFilledWithTextualValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies v.oclIsTypeOf( TextualValue ) )

[16] Only price values are given to a price attribute.

127 Context PriceAttribute :: isFilledWithPriceValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies v.oclIsTypeOf( PriceValue ) )

[17] Only date values are given to a date attribute.

Context DateAttribute :: isFilledWithDateValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies v.oclIsTypeOf( DateValue ) )

[18] Only boolean values are given to a boolean attribute.

Context BooleanAttribute :: isFilledWithBooleanValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies v.oclIsTypeOf( BooleanValue ) )

[19] Only integer values are given to a integer attribute.

Context IntegerAttribute :: isFilledWithIntegerValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies v.oclIsTypeOf( IntegerValue ) )

[20] Only decimal values are given to a decimal attribute.

Context DecimalAttribute :: isFilledWithDecimalValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies v.oclIsTypeOf( DecimalValue ) )

[21] Only image values are given to an image attribute.

Context ImageAttribute :: isFilledWithImageValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies v.oclIsTypeOf( ImageValue ) )

[22] Only single enumeration values are given to a single valued enumeration attribute, and its value options are options of the attribute.

Context SingleValuedEnumerationAttribute :: isFilledWithSingleEnumerationValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies (v.oclIsTypeOf( EnumerationValue ) and self.allowedValue -> includes ( v ) ))

[23] Only multiple enumeration values are given to a multiple valued enumeration attribute, and its value options are options of the attribute.

Context MultipleValuedEnumerationAttribute :: isFilledWithMultipleEnumerationValues() : Boolean body: self.storeViewAttributeRating.attributeValue -> forall ( v | v.isDefined() implies (v.oclIsTypeOf( MultipleEnumerationValue ) and self.allowedValue -> includesAll ( v.oclAsType(MultipleEnumerationValue).enumerationValue )

128 ))

I Description In Magento, users assign a global value to each couple product - attribute. But they can redefine this value for each website or storeview. Some attribute properties restrict the way this values are assigned:

• scope: define at which level we can rate this attribute. If it is set to Global, no store view and website redefinitions are not taken into account. If it is Website, only the redefinitions at the website level are taken into account. With a Store View scope, all redefinitions are taken into account. • hasUniqueValues: if true, there can not be two products where this attribute is rated with the same value. • isRequired: if true, each product that has this attribute should give it a value. • isComparable: indicates whether the attribute can be used for customers to compare two different products.

Global, Website and Storeview Attribute Ratings are the fact that an attribute has a value in a specific scope. Each one can have a generic value or its redefinition associated. For a given product, attribute and store view, the attribute value is the more specific defined value, taking into account only the redefinitions specified by the scope property of this attribute. Depending on the type of data they represent, there are several types of Attribute Values.

• Textual Value • Boolean Value • Price Value • Date Value • Integer Value • Decimal Value • Image Value: where the file path of the image is specified. • Single Enumeration Value: represents the choose of one literal value, from those specified by the corresponding single enumeration attribute. • Multiple Enumeration Value: represents the choose of a non-empty set of literal values, from those specified by the corresponding multiple enumeration attribute.

Literal values have a generic name, that can be redefined for a given store view.

129 5.6.7 Price Rules

I Overview Magento allows modifing the final product prices depending on the quantity of products ordered or its properties.

I Schema Diagram

130 I Derivation Rules

[1] ShoppingCartPriceRuleInStoreView :: label is the redefinedLabel at the current base scope level, if it is defined, or the genericLabel of the category.

Context ShoppingCartPriceRuleInStoreView :: label : String body: if not(self.redefinedLabel.isUndefined()) then self.redefinedLabel else self.category.genericLabel endif

[2] ShoppingCartPriceRule :: timesUsed is the number of times the rule has been used by any- one.

Context ShoppingCartPriceRule :: timesUsed : String body: self.useOfShoppingCartPriceRule.timesUsed -> sum()

I Constraints

[1] A shopping cart price rule is identified by its product and quantity.

Context TierPrice :: isIdentifiedByItsProductAndQuantity() : Boolean body: TierPrice.allInstances() -> isUnique(Tuplep:product,q:quantity)

[2] A catalog price rule is identified by its name.

Context CatalogPriceRule :: isIdentifiedByItsName() : Boolean body: CatalogPriceRule.allInstances() -> isUnique(name)

[3] A catalog price rule is identified by its priority.

Context CatalogPriceRule :: isIdentifiedByItsPriority() : Boolean body: CatalogPriceRule.allInstances() -> isUnique(priority)

[4] A shopping cart price rule is identified by its name.

Context ShoppingCartPriceRule :: isIdentifiedByItsName() : Boolean body: ShoppingCartPriceRule.allInstances() -> isUnique(name)

[5] A shopping cart price rule is identified by its priority.

Context ShoppingCartPriceRule :: isIdentifiedByItsPriority() : Boolean body: ShoppingCartPriceRule.allInstances() -> isUnique(priority)

[6] A shopping cart price rule in a store view is identified by its label and store view.

Context ShoppingCartPriceRuleInStoreView :: isIdentifiedByItsLabelAndStoreView() : Boolean body: ShoppingCartPriceRuleInStoreView.allInstances() -> isUnique(Tuplel:label,s:storeView)

[7] Each shopping cart price rule should be specified in all existing store views in the system.

Context ShoppingCartPriceRule :: isSpecifiedInAllStoreViews() : Boolean body: self.storeView -> includesAll( StoreView.allInstances() )

[8] The discountQtyStep property is only defined if the apply method selected is Percentage or BuyXGetY.

131 Context ShoppingCartPriceRule :: hasTheDiscountQtyStepDefinedWhenNeeded() : Boolean body: not(self.discountQtyStep.isUndefined()) implies ( self.method = ShoppingCartRuleDiscountMethod::Percentage or self.method = ShoppingCartRuleDiscountMethod::BuyXGetY )

[9] A shopping cart price rule can not be totally used more times than the total uses defined.

Context ShoppingCartPriceRule :: isNotUsedMoreThanPermitted() : Boolean body: self.maxUses >= self.useOfShoppingCartPriceRule.timesUsed -> sum()

[10] A shopping cart price rule can not be used by a customer more times than the total uses per customer defined.

Context ShoppingCartPriceRule :: isNotUsedMoreThanPermittedToOneCustomer() : Boolean body: self.useOfShoppingCartPriceRule -> forAll ( usc | self.maxUsesPerCustomer >= usc.timesUsed)

[11] Fixed tier prices are not applied to Bundle and Grouped Products

Context FixedTierPrice :: isAppliedToProductsOfTheCorrectType() : Boolean body: not(self.product.oclIsTypeOf(BundleProduct) or self.product.oclIsTypeOf(GroupedProduct))

[12] Relative tier prices are only applied to Bundle Products

Context RelativeTierPrice :: isAppliedToProductsOfTheCorrectType() : Boolean body: self.product.oclIsTypeOf(BundleProduct)

I Description A Tier Price determines that a product will be priced differently for higher quantities. If a Customer of a selected Customer Group purchases more unities of a product than the es- tablished quantity, each unit will be priced at the new defined price (for Fixed Tier Prices) or the percentage reduction will be applied (for Relative Tier Prices). For multiple tier definition, they take precedence from the highest to lowest quantity. Catalog Price Rules are applied to a product, if it meets the defined condition. Shopping Cart Price Rules are applied to all products in a shopping cart, when the shopping cart meet the defined condition. These conditions state values for the product attributes to be selected or quantities of products to be purchased in a shopping cart. Rules will only be applied in the defined websites, as well as for customers belonging to the defined customer groups. Magento saves the following information about price rules:

• name: the name of the rule. • description: a description of the rule. • status: whether the rule is currently enabled or not. • priority: defines the order the rules will be applied. • inEffectFrom: the beggining of the data range for the promotion to be in effect. If empty, the rule will be enabled as soon as it is created.

132 • inEffectUntil: the end of the data range for the promotion to be in effect. If empty, the rule will be enabled until it is deleted. • amount: the numerical value of the discount, which can either a percentage or fixed monetary amount, depending on the apply method defined. • stopFurtherRules: will cause the system to ignore any other existing rules with a lower priority once this rule has been calculated. • condition: the condition a product or shopping cart should meet in order that the price rule can be applied .

Additional attributes should be defined for Shopping Cart Price Rules:

• couponCode: if defined, the rule will only be applied if the customer enters this code when placing an order. If no code is defined, the application of the rule depends only on its conditions. • maxUses: the maximum amount of thimes the rule can be used total. There’s no limit if it is no defined. • maxUsesPerCustomer: the maximum amount of times the Rule can be used per customer, whether or not you require a coupon code. If empty, there will be no limit.- The UseOfShoppingCartPriceRule::timesUsed property indicates the number of times the rule has been used by a customer. • timesUsed: the number of times the rule has been used by anyone. Derived Attribute. • maxQuantity: limits how many units of each line item in the shopping cart will receive this discount. • discountQtyStep: only defined if the apply method is Percentage or BuyXGetY, de- termines the quantities that your customers will have to purchase in order to receive a percentage discount or the offered free units, respectively. • freeShippingMethod: determines whether the rule can be combined with a free ship- ping offer, if it is offered. If MatchingItems is selected, only the products that match the rule’s condition will be able to ship for free. For AllItems, that will be also applied for other products in the shopping cart. • itemLevelCondition: restricts the application of the defined discounts to the products that meets it. If it is no defined, the rule is applied to all the products in the shopping cart.

For shopping cart price rules, a discount applying method should be chosen from the following:

• Percentage: substracts, for each product, the discount amount as a percentage of the original price. If a discountQtyStep is defined, the discount will be placed only to a reduced quantity of products, this quantity defined as the greatest factor of ’discoun- tQtyStep’ that is below the purchased quantity. • Fixed: substracts, for each product, the discount amount from the price of each prod- uct. • FixedForWholeCart: as the Fixed method, but it is applied only once to the total cart amount. • BuyXGetY: offers a quantity of free items, for each product, determined by the dis- count amount property. The number of given units is defined by the discountQtyStep property.

A generic label is defined to show a shopping cart price rule to customers, which can be redefined at the store view level. In catalog price rules, the following discount applying methods could be chosen:

133 • SetAsFixed: sets the final discounted price to the discount amount. • SubstractFixed: substracts the discount amount from the original price. • SetAsPercentage: sets the final discounted price to the ’discount amount’ percentage of the original price. • SubstractPercentage: substracts the discount amount as a percentage of the original price.

134 5.7 Additional Activities

5.7.1 Product Tags

I Overview Tags are one-word descriptors that customers can add to products.

I Schema Diagram

I Derivation Rules

[1] TagInStoreView :: popularity is the number of times the tag has been applied in this storeView, adding the base popularity.

Context TagInStoreView :: popularity : Integer body: self.basePopularity + self.tag.productTagging -> select ( t | t.storeView = self.storeView ) -> size()

I Constraints

[1] Tags are identified by its name.

Context Tag :: isIdentifiedByItsName() : Boolean body: Tag.allInstances() -> isUnique(name)

[2] Each tag should be related to all existing store views in the system.

135 Context Tag :: isSpecifiedInAllStoreViews() : Boolean body: self.storeView -> includesAll( StoreView.allInstances() )

I Description Tags act as keywords. Tags help Customers to organize and remember the products that they have seen. System saves the following information for tags:

• name: the description word itself. . • status: Pending tags are those recently added by costumers. Administrators can after change its status to Approved or Disabled.

A ProductTagging is the fact that a Customer has assigned a Tag to a Product in a Store View. Magento saves the time this assignation was made. Administrators can also add Tags to Products. In this case, only the Product tagged and the selectionned tag are saved. Some tag properties can be set at the store view scope:

• basePopularity: is added to the number of times the tag is assigned to calculate its popularity. • popularity: used by the system to emphasize the most used tags. Derived Attribute.

136 5.7.2 Product Reviews

I Overview Customers can write reviews so that other users may read evaluations of a product.

I Schema Diagram

I Derivation Rules

[1] PropertyInStoreView :: name is the name redefined in this store view. If it is not defined, the generic name.

Context PropertyInStoreView :: name : String body: if self.redefinedName -> notEmpty() then self.redefinedName else self.property.genericName endif

I Constraints

[1] Properties are identified by its generic name.

Context Property :: isIdentifiedByItsGenericName() : Boolean body: Property.allInstances() -> isUnique(genericName)

137 [2] Within a store view, properties should also be identified by its name.

Context PropertyInStoreView :: isIdentifiedByItsStoreViewAndName() : Boolean body: PropertyInStoreView.allInstances() -> isUnique(Setname,storeView)

[3] Reviews are identified by its author nick name and its text.

Context Review :: isIdentifiedByItsAuthorNickNameAndText() : Boolean body: Review.allInstances() -> isUnique(SetauthorNickName,text)

[4] Each property should be related to all existing store views in the system.

Context Property :: isSpecifiedInAllStoreViews() : Boolean body: self.storeView -> includesAll( StoreView.allInstances() )

I Description A Review is a customer evaluation of a product. Magento takes into account the following information about reviews:

• authorNickName: a name that the autor of the review chooses, with no restriction, to identificate himself ahead of the other users. • title: the title of the review. • text: the text of the review. • status: Pending reviews are those recently added by Costumers. Administrators can after change its status to Approved or NotApproved. • createdAt: the time the review was made. • storeViewWhereIsVisible: the store views where this review is visible. When it is created, it is only visible in the store view it was created, but administrators can change it after. • productReviewed: the product that is reviewed. • user: the costumer or administrator that has made the review. Reviews can be made by not registered costumers. In this case, no user is associated to it.

Magento allows us to define properties that the user will be able to rate for a product.- Properties have a genericName that can be redefined for each store view. We can set properties to be not visible in a store view. A Property Score assigns a value to a property for a product. When a review is placed, an score should be provided for each property which is visible in the store view it was made. As properties’ visibility can be modified later, this constraint could not be respected after editing a property.

138 5.7.3 Buying Process Information

I Overview Magento allows customers to save information to support the buying process: wished prod- ucts, product comparison and alerts, etc.

I Schema Diagram

139 I Derivation Rules

[1] ActivityInfoOfSessionInStoreView :: readyToCompareProduct are the explicit ready to com- pare products, if the catalog configuration sets its scope to StoreView, or the website’s ready to compare products otherwise.

Context ActivityInfoOfSessionInStoreView :: readyToCompareProduct : Set(Product) body: let currentWebsite : Website = self.sessionWithInfoAbout.storeView.store.website in if currentWebsite.CatalogConfigurationInWebsite.recentlyViewedComparedProductsScope = Scope::StoreView then self.explicitReadyToCompareProduct else currentWebsite.activityInfoOfSessionInWebsite -> any ( siw | siw.sessionWithInfoAbout = self ).explicitReadyToCompareProduct endif

[2] ActivityInfoOfSessionInStoreView :: recentlyComparedProduct are the explicit recently com- pared products, if the catalog configuration sets its scope to StoreView, or the website’s recently compared products otherwise.

Context ActivityInfoOfSessionInStoreView :: recentlyComparedProduct : Set(Product) body: let currentWebsite : Website = self.sessionWithInfoAbout.storeView.store.website in if currentWebsite.CatalogConfigurationInWebsite.recentlyViewedComparedProductsScope = Scope::StoreView then self.explicitRecentlyComparedProduct else currentWebsite.activityInfoOfSessionInWebsite -> any ( siw | siw.sessionWithInfoAbout = self ).explicitRecentlyComparedProduct endif

[3] ActivityInfoOfSessionInStoreView :: readyRecentlyViewedProduct are the explicit ready to compare products, if the catalog configuration sets its scope to StoreView, or the website’s recently viewed products otherwise.

Context ActivityInfoOfSessionInStoreView :: readyRecentlyViewedProduct : Set(Product) body: let currentWebsite : Website = self.sessionWithInfoAbout.storeView.store.website in if currentWebsite.CatalogConfigurationInWebsite.recentlyViewedComparedProductsScope = Scope::StoreView then self.explicitRecentlyViewedProduct else currentWebsite.activityInfoOfSessionInWebsite -> any ( siw | siw.sessionWithInfoAbout = self ).explicitRecentlyViewedProduct endif

I Constraints

[1] No wished products are registered if the wish list is not enabled for this website.

Context ActivityInfoOfCustomerInWebsite :: definesWishedProductsOnlyIfAllowed() : Boolean body: self.websiteWithInfoAbout.wishlistConfigurationInWebsite.status = Status::Disabled implies self.wishedProduct -> isEmpty()

[2] A ready to compare product can not match with any recently compared product.

Context ActivityInfoOfSessionInStoreView :: hasNotMatchingComparedAndRecentlyComparedProd- ucts() : Boolean body: self.recentlyComparedProduct -> excludesAll ( self.readyToCompareProduct )

140 [3] A ready to compare product can not match with any recently compared product.

Context ActivityInfoOfCustomerInStoreView :: hasNotMatchingComparedAndRecentlyComparedProd- ucts() : Boolean body: self.recentlyComparedProduct -> excludesAll ( self.readyToCompareProduct )

[4] A ready to compare product can not match with any recently compared product.

Context ActivityInfoOfCustomerInWebsite :: hasNotMatchingComparedAndRecentlyComparedProd- ucts() : Boolean body: self.recentlyComparedProduct -> excludesAll ( self.readyToCompareProduct )

[5] The number of uses of the ’Tell to a friend’ feature is not greater than the maximum uses allowed by the system configuration.

Context ActivityInfoOfSessionInStoreView :: hasNotUsedTellToAFriendTooMuch() : Boolean body: self.usesOfTellToFriendInLastHour <= self.storeViewWithInfoAbout.tellToAFriendConfigura- tionInStoreView.maxUsesPerHour

[6] The customer’s saved number of uses of the ’Tell to a friend’ feature is not greater than the maximum uses allowed by the system configuration.

Context ActivityInfoOfCustomerInStoreView :: hasNotUsedTellToAFriendTooMuch() : Boolean body: self.usesOfTellToFriendInLastHour <= self.storeViewWithInfoAbout.tellToAFriendConfigura- tionInStoreView.maxUsesPerHour

I Description When a customer, either registered or not, uses the system, some information about his activity is generated: it is called the activity info of the session:

• readyToCompareProducts: Magento offers a useful feature to quickly compare prod- uct attribute information in a side-by-side view. Those are the products the Customer choose to compare. • recentlyComparedProducts: the products that the customer has recently removed from the ready to compare list. • recentlyViewedProducts: the products for which the customers have recently viewed the page.

When a registered Customer goes offline, its last activity info is saved to be restored the next time he returns online. The activity info can be shared at the website or the store view level, depending on the Catalog Configuration settings. The system also keeps track of

• usesOfTellToAFriendInLastHour: the number of times the customer has used the Tell to a friend feature in the last hour. • wishedProducts: designated by registered customers to indicate that they wish to purchase them (or have someone purchase for them) at a later date. A comment can be added by the Customer, explaining why does he wish that item. • productSubscribedForPriceAlert: customers will receive an e-mail when the price of this product is changed. • productSubscribedForStockAlert: customers will receive an e-mail when this prod- uct goes back in stock.

141 Wishlist and product alerts are only allowed for registered customers, while the uses of tell to friend are resetted when the corresponding session is closed.

142 5.7.4 Newsletters

I Overview Magento allows store owners to send emails and product notifications to customers.

I Schema Diagram

I Derivation Rules

[1] NewsletterSubscription :: email is the provided email, if it is defined, or the customer’s own email, otherwise.

Context NewsletterSubscription :: email : Email body:

143 if self.oclIsTypeOf(AnonymousNewsletterSubscription) then self.oclAsType(AnonymousNewsletterSubscription).providedEMail else if not(self.providedEMail.isUndefined()) then self.oclAsType(CustomerNewsletterSubscription).providedEMail else self.oclAsType(CustomerNewsletterSubscription).customer.email endif

I Constraints

[1] Newsletter templates are identified by its name.

Context NewsletterTemplate :: isIdentifiedByItsName() : Boolean body: NewsletterTemplate.allInstances() -> isUnique (name)

[2] Newsletters are identified by its subject and message.

Context Newsletter :: isIdentifiedByItsSubjectAndMessage() : Boolean body: Newsletter.allInstances() -> isUnique (Setsubject,message)

[3] Newsletters are identified by its email and store view.

Context AnonymousNewsletterSubscription :: isIdentifiedByItsEmailAndStoreView() : Boolean body: AnonymousNewsletterSubscription.allInstances() -> isUnique (Tuplec:eMail,s:storeView)

[4] Newsletters are identified by its customer and store view.

Context CustomerNewsletterSubscription :: isIdentifiedByItsCustomerAndStoreView() : Boolean body: CustomerNewsletterSubscription.allInstances() -> isUnique (Tuplec:customer,s:storeView)

[5] A customer could not have two newsletter subscriptions from the same store view.

Context Customer :: isNotSubscribedTwiceInAStoreView() : Boolean body: self.customerNewsletterSubscription -> isUnique (storeView)

[6] A newsletter can not have been sended to more customers or visitors than the total subscrip- tions that are made to it.

Context Newsletter :: hasTheCorrectProcessedSendings() : Boolean body: self.processedSendigs <= self.storeView.newsletterSubscription -> asSet() -> size()

I Description Customers, registered or not, can subscribe themselves to the store newsletter.

• email: the email address where the newsletter will be sent. Registered customers can optionally specify a different email address, if not its own address is taken. • status: the subscription can be temporally disabled, without deleting the subscription information.

To start sending newsletters, Newsletter Templates should be created first. Those are its properties:

• name: the template’s name. • subject: the subject of the email that will be sent to your customers for a newsletter using this template.

144 • senderName: the name of the sender of the email that will be sent to your customers for a newsletter using this template. • senderEmail: the email address of the sender of the email that will be sent to your customers for a newsletter using this template. • message: the body of your email for a newsletter using this template. • createdAt: the time the template was created. • lastUpdatedAt: the last time the template was updated.

The newsletter is created from a template. The name, subject and sender name and email are generally suposed to be the same, but can be changed. Additional properties are defined:

• queueStartedAt: the time the system will start sending the newsletter. If not defined, the system starts sending the newsletter from the moment it is created. • processedSendings: the number of customers or visitors where the newsletter has been sent. • storeView: this newsletter will be sent to all the customers or visitors that have been subscribed for the newsletter at this store view. • status

A newsletter problem reports a problem that the system had sending the newsletter. Ma- gento saves the newsletter where this error has been produced and, if needed, the corre- sponding subscription. For example, a newsletter sent to an erroneous e-mail address will appear here.

• id: identifies the problem report. • code: identifies the type of error that have been succeded. • text: a human friendly explanation of the error.

145 5.8 Online Catalog

5.8.1 Shopping Carts

I Overview Customers can add or remove products from their shopping carts while they are navigating the online store.

I Schema Diagram

I Derivation Rules

[1] ShoppingCart :: total the amount the customer will pay for all products in the shopping cart.

Context ShoppingCart :: total : Integer body: self.shoppingCartItem.total -> sum()

[2] ShoppingCart :: currentStoreView Is the store view that will be used to calculate the prices and discounts that will be applied to the purchased items.

Context ShoppingCart :: currentStoreView : StoreView body: if self.oclIsTypeOf(AnonymousShoppingCart) then self.oclAsType(AnonymousShoppingCart).anonymousSession.storeView else if self.oclIsTypeOf(CustomerShoppingCart) then self.oclAsType(CustomerShoppingCart).customer.customerSession.storeView

146 else self.oclAsType(AdministrationShoppingCart).storeView endif endif

[3] ShoppingCart :: purchasingCurrency Is the currency used to buy the products in the shopping cart.

Context ShoppingCart :: purchasingCurrency : Currency body: if self.oclIsTypeOf(AnonymousShoppingCart) then self.oclAsType(AnonymousShoppingCart).anonymousSession.currentCurrency else if self.oclIsTypeOf(CustomerShoppingCart) then self.oclAsType(CustomerShoppingCart).customer.customerSession.currentCurrency else self.oclAsType(AdministrationShoppingCart).currency endif endif

[4] ShoppingCart :: customerGroup will be used to define some characteristics (as taxes) that affect the final amount that the user will pay for this products. For Anonymous Shopping Carts the "Not Logged In" group is applied, while Customer Shopping Carts use the group of its related customer. For Administration Shopping Carts, the group is explicitly selected.

Context ShoppingCart :: customerGroup : CustomerGroup body: if self.oclIsTypeOf(AnonymousShoppingCart) then CustomerGroup.allInstances() -> any ( c | c.name = ’NotLoggedIn’ ) else if self.oclIsTypeOf(CustomerShoppingCart) then self.oclAsType(CustomerShoppingCart).customer.customerGroup else if self.oclAsType(AdministrationShoppingCart).redefinedCustomerGroup -> notEmpty() then self.oclAsType(AdministrationShoppingCart).redefinedCustomerGroup else self.oclAsType(AdministrationShoppingCart).customer.customerGroup endif endif endif

[5] AdministrationShoppingCart :: customerEMail Is the redefined customer eMail, if defined, or the eMail of the associated customer.

Context AdministrationShoppingCart :: customerEMail : EMail body: if self.redefinedCustomerEMail.isDefined() then self.redefinedCustomerEMail else self.customer.eMail endif

I Constraints

[1] An anonymous shopping cart is identified by its anonymous session.

Context AnonymousShoppingCart :: isIdentifiedByItsAnonymousSession() : Boolean body: AnonymousShoppingCart.allInstances() -> isUnique(anonymousSession)

[2] A customer shopping cart is identified by its customer.

Context CustomerShoppingCart :: isIdentifiedByItsCustomer() : Boolean body: CustomerShoppingCart.allInstances() -> isUnique(customer)

[3] An administration shopping cart is identified by its customer, the store view where is placed and the administrator who places it.

Context AdministrationShoppingCart :: isIdentifiedByItsCustomerStoreViewAndAdministrator() : Boolean body: AdministrationShoppingCart.allInstances() -> isUnique(Setcustomer, storeView, administra- tor)

147 [4] The shopping cart was not created in the future.

Context CustomerShoppingCart :: hasACorrectCreatedAtDate() : Boolean body: self.createdAt <= Now()

[5] The time the user did his last update on the shopping cart should be between the time the shopping cart was created and now.

Context CustomerShoppingCart :: hasACorrectUpdatedAtDate() : Boolean body: self.updatedAt >= self.createdAt and self.updatedAt <= Now()

I Description Shopping Carts contain the products chosen by customers from the online catalog. A Shopping Cart is anonymous until the Customer logs in. At this moment, if the Customer didn’t have a previous Customer Shopping Cart, the Anonymous Shopping Cart becomes a Customer Shopping Cart. Otherwise, the anonymous Shopping Cart is removed from the system and their Products are added to the previous Customer Shopping Cart. If a Customer leaves a Session with a non-empty Customer Shopping Cart, then the cart will be automatically restored in his next session. Moreover, Anonymous Shopping Carts can only exist in the context of a Session, and they are automatically removed when the Session expires. Administration Shopping Carts are used when Administrators use the system to register an Order for a Customer, that has normally asked for it in an offline way. The prices, discounts and taxes will be calculated as if the Order was placed using the specified store view. In addition of the derived attributes, which are described by its derivation rules, Magento saves the following information about Shopping Carts:

• giftMessage: a message that will be delivered to the person that receives the products added to the cart, if an order for it is finally placed. • couponCode: a literal code that can enable the application of a Shopping Cart Price Rule that matches it.

For Customer Shopping Carts, the following information is also saved:

• createdAt: the time the cart was created. • updatedAt: the last time a product was added, edited or removed from the shopping cart.

When an order corresponding to a Shopping Cart is confirmed, the Shopping Cart is removed from the system.

148 5.8.2 Shopping Cart Items

I Overview Each different Product added to a Shopping Cart is a Shopping Cart Item.

I Schema Diagram

I Operations

[1] context ShoppingCartItem def: calculateNetPrice (currentProduct : Product) : Money =

currentProduct.productInWebsite -> any ( piw | piw.website = self.shoppingCart.currentStoreView.- store.website).netPrice

[2] context ShoppingCartItem def: calculateSpecialNetPrice (currentProduct : Product) : Money =

let productInTheCurrentWebsite:ProductInWebsite = currentProduct.productInWebsite -> any ( piw | piw.website = self.shoppingCart.currentStoreView.store.website)

149 in let productInTheCurrentStoreView:ProductInStoreView = currentProduct.productInStoreView - > any ( pisv | pisv.storeView = self.shoppingCart.currentStoreView) in if productInTheCurrentWebsite.specialNetPrice.isDefined() and productInTheCurrentStoreView.specialNetPriceFrom.isDefined() and productInTheCurrentStoreView.specialNetPriceUntil.isDefined() and productInTheCurrentStoreView.specialNetPriceFrom <= Now(); and productInTheCurrentStoreView.specialNetPriceUntil >= Now(); then productInTheCurrentWebsite.specialNetPrice endif

[3] context ShoppingCartItem def: calculateTieredPrice (currentProduct : Product) : Money =

let applicableTierPrices:Set(TierPrice) = self.shoppingCart.currentStoreView.store.website.tierPrice -> select ( tp | tp.product = currentProduct and tp.quantity <= self.quantity ) in applicableTierPrices-> any ( tp | applicableTierPrices -> forAll ( tp1 | tp.quantity >= tp1.quantity ) ).oclAsType(FixedTierPrice).price

[4] context ShoppingCartItem def: calculateCatalogRulesPrice (currentProduct : Product, applied- CatalogPriceRules : CatalogPriceRule) : Money =

let netPrice: Money = calculateNetPrice( currentProduct ) in self.calculateCatalogRulesPriceRecursively(netPrice, appliedCatalogPriceRules)

[5] context ShoppingCartItem def: calculateCatalogRulesPriceRecursively (originalPrice : Money, appliedCatalogPriceRules : CatalogPriceRule) : Money =

let morePrioritaryRule: CatalogPriceRule = appliedCatalogPriceRules -> any ( r | appliedCatalogPriceRules -> forAll ( r1 | r.priority <= r1.priority) ) in let calculatedPrice:Money = if morePrioritaryRule.method = ShoppingCartRuleDiscountMethod::SetAsFixed then morePrioritaryRule.amount else if morePrioritaryRule.method = ShoppingCartRuleDiscountMethod::SubstractFixed then originalPrice - morePrioritaryRule.amount else if morePrioritaryRule.method = ShoppingCartRuleDiscountMethod::SetAsPercentage then originalPrice * morePrioritaryRule.amount else originalPrice * (1 - morePrioritaryRule.amount) endif endif endif in if appliedCatalogPriceRules -> isEmpty() then originalPrice else else self.calculateCatalogRulesPriceRecursively(calculatedPrice,appliedCatalogPriceRules - > excluding(morePrioritaryRule)) endif

[6] context ShoppingCartItem def: calculateOptionsIncrement (basePrice : Money) : Money =

self.optionValueInOption -> collect ( ovio | if ovio.sign = Sign::Plus then if ovio.fixedIncrement.isDefined() then ovio.fixedIncrement else basePrice * ovio.relativeIn- crement endif else if ovio.fixedIncrement.isDefined() then -ovio.fixedIncrement else - (basePrice * ovio.rela- tiveIncrement) endif endif ) -> sum() + self.textOption -> union(self.dateOption) -> collect ( op | if op.sign = Sign::Plus then

150 if op.fixedIncrement.isDefined() then op.fixedIncrement else basePrice * op.relativeIncre- ment endif else if op.fixedIncrement.isDefined() then -op.fixedIncrement else - (basePrice * op.relativeIn- crement) endif endif ) -> sum()

[7] context ShoppingCartItem def: calculateDiscount (discountBasis : Money, appliedShopping- CartPriceRules : ShoppingCartPriceRule) : Money =

let morePrioritaryRule: ShoppingCartPriceRule = appliedShoppingCartPriceRules -> any ( r | appliedShoppingCartPriceRules -> forAll ( r1 | r.priority <= r1.priority) ) in let calculatedDiscount:Money = if morePrioritaryRule.method = ShoppingCartRuleDiscountMethod::Percentage then if morePrioritaryRule.discountQtyStep.isUndefined() then discountBasis * morePriori- taryRule.amount else (discountBasis / self.quantity) * ((self.quantity / morePrioritaryRule.discountQtyStep).floor() * morePrioritaryRule.discountQtyStep) * morePrioritaryRule.amount endif else if morePrioritaryRule.method = ShoppingCartRuleDiscountMethod::Fixed then morePrioritaryRule.amount * self.quantity else if morePrioritaryRule.method = ShoppingCartRuleDiscountMethod::FixedForWholeCart then if self.shoppingCart.shoppingCartItem->excluding(self).appliedShoppingCartPriceRule-> excludes(morePrioritaryRule) then morePrioritaryRule.amount else 0.0 endif else if morePrioritaryRule.method = ShoppingCartRuleDiscountMethod::BuyXGetY then (discountBasis / self.quantity) * (self.quantity/(morePrioritaryRule.discountQtyStep + morePri- oritaryRule.amount)).floor() + (self.quantity.mod((morePrioritaryRule.discountQtyStep + morePrioritaryRule.amount).floor()) - morePrioritaryRule.discountQtyStep).max(0) endif endif endif in if appliedShoppingCartPriceRules -> isEmpty() then 0.0 else else calculatedDiscount + self.calculateDiscount(discountBasis, appliedShoppingCartPriceRules -> excluding(morePrioritaryRule)) endif

I Derivation Rules

[1] ShoppingCartItem :: basePrice is the unitary price of the product in the website the Shopping Cart is placed, taking into account the special net price, tier prices and catalog price rules.

Context ShoppingCartItem :: basePrice : Money body: self.calculateNetPrice(self.product) .min( self.calculateSpecialNetPrice( self.product )) .min( self.calculateTieredPrice(self.product)) .min( self.calculateCatalogRulesPrice( self.product, self.appliedCatalogPriceRule))

[2] ShoppingCartItem :: calculatedPrice is the unitary price of the product taking into account the base price and the custom options selected

Context ShoppingCartItem :: calculatedPrice : Money body: self.basePrice + self.calculateOptionsIncrement( self.basePrice )

[3] ShoppingCartItem :: price is the price the customer pays for a unit of the product without adding taxes, shipping costs or discounts.

151 Context ShoppingCartItem :: price : Money body: if self.customPrice.isDefined() then self.customPrice else self.calculatedPrice endif

[4] ShoppingCartItem :: discount is the total amount able to be discounted from the set of prod- ucts purchased of this shopping cart item, determined by shopping cart price rules.

Context ShoppingCartItem :: discount : Money body: self.calculateDiscount( self.price * self.quantity, self.appliedShoppingCartPriceRule )

[5] ShoppingCartItem :: total is the unitary cost of this product multiplied by the quantity added to the shopping cart, minus the discount.

Context ShoppingCartItem :: total : Integer body: if self.applyDiscount then self.price * self.quantity - self.discount else self.price * self.quantity endif

[6] ShoppingCartItem :: appliedShoppingCartPriceRule are the shopping cart price rules that are applied to the current shopping cart item.

Context ShoppingCartItem :: appliedShoppingCartPriceRule : Set(ShoppingCartPriceRule) body: let applicableRules: Set(ShoppingCartPriceRule) = ShoppingCartPriceRule.allInstances() -> select ( scr | scr.status = Status::Enabled and scr.verifyForShoppingCart(self.shoppingCart) and scr.verifyItemLevelConditionForShoppingCart(self) and scr.website -> includes( currentSession.storeView.store.website ) and scr.customerGroup -> includes( currentSession().customerGroup() ) and scr.maxUses.isDefined() implies scr.timesUsed < scr.maxUses and scr.maxUsesPerCustomer.isDefined() implies scr.useOfShoppingCartPriceRule -> any ( use | use.customer = self.shoppingCart.customer).timesUsed < scr.maxUsesPerCustomer and scr.inEffectFrom.isDefined() implies scr.inEffectFrom <= Now() and scr.inEffectUntil.isDefined() implies scr.inEffectUntil >= Now() and scr.couponCode.isDefined() implies self.shoppingCart.couponCode = scr.couponCode ) in applicableRules -> select ( r | applicableRules->excluding(r) -> forAll ( r1 | r1.priority > r.priority implies r1.stopFurtherRules=false ))

[7] ShoppingCartItem :: appliedCatalogPriceRule are the catalog price rules that are applied to the current shopping cart item.

Context ShoppingCartItem :: appliedCatalogPriceRule : Set(CatalogPriceRule) body: let applicableRules: Set(ShoppingCartPriceRule) = CatalogPriceRule.allInstances() -> select ( scr | scr.status = Status::Enabled and scr.verifyConditionForShoppingCart(self.shoppingCart) and scr.website -> includes( currentSession().storeView.store.website ) and scr.customerGroup -> includes( currentSession().customerGroup() ) and scr.inEffectFrom.isDefined() implies scr.inEffectFrom <= Now() and scr.inEffectUntil.isDefined() implies scr.inEffectUntil >= Now() and ) in applicableRules -> select ( r | applicableRules->excluding(r) -> forAll ( r1 | r1.priority > r.priority implies r1.stopFurtherRules=false ))

152 I Constraints

[1] Shopping cart items are identified by its product, the cart it belongs to and the eventual options choosen.

Context ShoppingCartItem :: isIdentifiedByItsProductItsCartAndItsOptions() : Boolean body: let textOp: Bag(TupleType(o:TextOption,v:String)) = self.textOptionRating -> collect(t|Tuplet=t.textOption,v=t.value) in let dateOp: Bag(TupleType(o:DateOption,v:Date)) = self.dateOptionRating -> collect(t|Tuplet=t.dateOption,v=t.value) in ShoppingCartItem.allInstances() -> isUnique(Tuples=shoppingCart,p=product,t=textOp,d=dateOp,v=optionValueInOption)

[2] If the shopping cart item does not belong to an administration shopping cart, the custom price is undefined and the discount is always applied.

Context ShoppingCartItem :: definesCustomPriceAndApplyDiscountCorrectly() : Boolean body: (not self.shoppingCart.oclIsTypeOf(AdministrationShoppingCart)) implies (self.customPrice.isUndefined() and self.applyDiscount)

[3] The rated Text and Date Options and the selected Option Values belong to the product added in this Shopping Cart Item.

Context ShoppingCartItem :: ratesOnlyOptionsOfItsProduct() : Boolean body: self.textOption -> forAll(to|to.product = self.product) and self.dateOption -> forAll(dop|dop.product = self.product) and self.optionValueInOption -> forAll(ovio|ovio.optionWithPredefinedContent.product = self.product)

[4] If the option is required, all Shopping Cart Items that add its related Product must give a value to it.

Context Option :: isRatedWhenItIsNeeded() : Boolean body: self.isRequired implies ( if self.oclIsTypeOf(OptionWithPredefinedContent) then self.product.shoppingCartItem -> forAll ( i | i.optionValueInOption -> exists (ovio|ovio.option- WithPredefinedContent = self)) else if self.oclIsTypeOf(TextOption) then self.product.shoppingCartItem -> forAll ( i | i.textOption -> exists (to|to = self)) else self.product.shoppingCartItem -> forAll ( i | i.dateOption -> exists (dop|dop = self)) endif endif )

[5] If its section type is single, a Shopping Cart Item can not assign more than one value to the option.

Context OptionWithPredefinedContent :: isRatedWithOnlyOneValueWhenItIsNeeded() : Boolean body: self.selectionType = SelectionType::Single implies self.optionValueInOption -> forAll ( ov1, ov2 | ov1 <> ov2 implies ov1.shoppingCartItem <> ov2.shoppingCartItem )

[6] All products in the shopping cart should be enabled, in stock and available in the current web- site.

Context ShoppingCartItem :: hasOnlyAProductAbleToBeBougth() : Boolean body: self.product.productInStoreView -> any ( piw |

153 piw.storeView = self.shoppingCart.currentStoreView ).status = Status::Enabled and self.product.productInWebsite -> any ( piw | piw.website = self.shoppingCart.currentStoreView.store.website ).isAvailable and self.product.stockStatus = #InStock

I Description A Shopping Cart Item has the following attributes:

• quantity: the number of items puchased of this product. • giftMessage: a message that will be delivered to the person that receives the products added in this Shopping Cart Item. • basePrice: the unitary price of the product in the website the Shopping Cart is placed, taking into account the special net price, tier prices and catalog price rules. Derived Attribute. • calculatedPrice: the unitary price of the product taking into account the base price and the custom options selected. Derived Attribute. • customPrice: a different price that can be optionally applied for this Shopping Cart Item, instead of applying the product’s price. • price: the unitary price of the product taking into account the calculated price and the custom price. No taxes, discounts or shipping costs are added yet. Derived Attribute.

• discount: the global amount able to be discounted from the set of products added in this Shopping Cart Item, determined by shopping cart price rules. Derived Attribute. • applyDiscount: determines if discount defined above will be applied or not. • total: the total cost of this Shopping Cart Item. Derived Attribute.

If the product purchased in this Shopping Cart Item has options, its values are specified in the item itself: text and date options are rated, while an option value is choosen for Options With Predefined Content. Thus, Products with different Options configuration are added to the Shopping Cart as different Shopping Cart Items.

154 5.8.3 Shopping cart items of non-simple products

I Overview Shopping Cart Items of Downloadable, Configurated or Bundle Products need to save ad- ditional information.

I Schema Diagram

I Derivation Rules

155 [1] SCItemOfADownloadableProduct :: calculatedPrice is the unitary price of the product taking into account the base price, the custom options selected and increments due to the purchased Downloadable Items.

Context SCItemOfADownloadableProduct :: calculatedPrice : Money body: if self.downloadableProduct.purchaseItemsSeparatelly = Status::Enabled then self.basePrice + self.calculateOptionsIncrement( self.basePrice ) else self.downloadableItem -> collect ( dw | if dw.sign = Sign::Plus then if dw.fixedIncrement.isDefined() then dw.fixedIncrement else self.basePrice * dw.rela- tiveIncrement endif else if dw.fixedIncrement.isDefined() then -dw.fixedIncrement else - (self.basePrice * dw.- relativeIncrement) endif endif ) -> sum() + self.basePrice + self.calculateOptionsIncrement( self.basePrice ) endif

[2] SCItemOfAConfiguratedProduct :: basePrice the base price of the configurable product (not the configurated).

Context SCItemOfAConfiguratedProduct :: basePrice : Money body: self.calculateNetPrice( self.configurableProduct ) .min( self.calculateSpecialNetPrice( self.configurableProduct ) ) .min( self.calculateTieredPrice( self.configurableProduct ) ) .min( self.calculateCatalogRulesPrice( self.configurableProduct, self.catalogPriceRuleOfConfig- urableProduct ) )

[3] SCItemOfAConfiguratedProduct :: calculatedPrice the unitary price of the product taking into account the base price, the custom options selected and the increments due to the Configuration chosen.

Context SCItemOfAConfiguratedProduct :: calculatedPrice : Money body: self.configuratedProduct.configurationOfAConfigurableProduct -> collect ( cpa | if cpa.sign = Sign::Plus then if cpa.fixedIncrement.isDefined() then cpa.fixedIncrement else self.calculateBasePrice() * cpa.relativeIncrement endif else if cpa.fixedIncrement.isDefined() then -cpa.fixedIncrement else - (self.calculateBasePrice() * cpa.relativeIncrement) endif endif ) -> sum() + self.calculateBasePrice() + self.calculateOptionsIncrement( self.basePrice )

[4] PurchaseOfABundleProductOption :: basePriceOfBundleProductOption is the base price, in the current website, of the product purchased as a bundle option (as it was purchased alone).

Context PurchaseOfABundleProductOption :: basePriceOfBundleProductOption : Money body: self.calculateNetPrice( self.bundleProductOption.product ) -> min( self.calculateSpecialNetPrice( self.bundleProductOption.product ) ) -> min( self.calculateTieredPrice( self.bundleProductOption.product, self.quantity ) ) -> min( self.calculateCatalogRulesPrice( self.bundleProductOption.product, self.catalogPriceRule- OfBundleProduct ) )

[5] SCItemOfABundleProduct :: bundlePrice is the unitary price of the product taking into account the products purchased as Bundle Options and, if present, the custom options selected, in the current website

Context SCItemOfABundleProduct :: bundlePrice : Money body: if self.bundleProduct.priceMethod = PriceMethod::Dynamic then

156 self.purchaseOfABundleProductOption -> collect ( p | p.quantity * p.basePriceOfBundlePro- ductOption ) -> sum() else self.purchaseOfABundleProductOption -> collect ( pbpo | pbpo.quantity * ( if pbpo.bundleProductOption.sign = Sign::Plus then if pbpo.bundleProductOption.fixedIncrement.isDefined() then pbpo.bundleProductOption.- fixedIncrement else self.calculateNetPrice(self.bundleProduct) * pbpo.bundleProductOption.relativeIn- crement endif else if pbpo.bundleProductOption.fixedIncrement.isDefined() then -pbpo.bundleProductOp- tion.fixedIncrement else - (self.calculateNetPrice(self.bundleProduct) * pbpo.bundleProductOption.- relativeIncrement) endif endif ) ) -> sum() + self.calculateNetPrice( self.bundleProduct ) + self.calculateOptionsIncrement( calculateNetPrice(self.bundleProduct) ) endif

[6] SCItemOfABundleProduct :: calculatedPrice is the unitary price of the product taking into account the bundle price and the reductions due to special and tiered prices.

Context SCItemOfABundleProduct :: calculatedPrice : Money body: let specialBundlePrice: Real = let bundleProductInTheCurrentWebsite:BundleProductInWebsite = self.bundleProduct.bundlePro- ductInWebsite -> any ( piw | piw.websiteWhereIsSpecifiedAsBundleProduct = self.shoppingCart.cur- rentStoreView.store.website) in let productInTheCurrentStoreView:ProductInStoreView = self.bundleProduct.productInStore- View -> any ( pisv | pisv.storeView = self.shoppingCart.currentStoreView) in if bundleProductInTheCurrentWebsite.specialNetPriceReduction.isDefined() and productInTheCurrentStoreView.specialNetPriceFrom.isDefined() and productInTheCurrentStoreView.specialNetPriceUntil.isDefined() and productInTheCurrentStoreView.specialNetPriceFrom <= Now() and productInTheCurrentStoreView.specialNetPriceUntil >= Now() then bundlePrice * bundleProductInTheCurrentWebsite.specialNetPriceReduction else bundlePrice endif in let applicableTierPrices:Set(TierPrice) = self.shoppingCart.currentStoreView.store.website.tierPrice -> select ( tp | tp.product = cur- rentProduct and tp.quantity <= quantity) in let tieredBundlePrice: Money = bundlePrice * applicableTierPrices-> select ( tp | applica- bleTierPrices -> forAll ( tp1 | tp.quantity >= tp1.quantity ) ).percentage in bundlePrice.min( specialBundlePrice ).min( tieredBundlePrice ) .min( self.calculateCatalogRulesPrice( self.bundleProductOption.product, self.catalogPriceRule- OfBundleProduct ) )

I Constraints

[1] The downloadable items of the order line are downloadable items of the purchased download- able product.

Context SCItemOfADownloadableProduct :: hasTheCorrectDownloadItems() : Boolean body: self.downloadableProduct.downloadableItem -> includesAll( self.downloadableItem )

[2] If the downloadable product does not allow to puchase its items separatelly, all downloadable items are purchased.

Context SCItemOfADownloadableProduct :: hasAllDownloadItemsWhenNeeded() : Boolean

157 body: self.downloadableProduct.purchaseItemsSeparatelly = Status::Enabled implies self.down- loadableItem -> includesAll( self.downloadableProduct.downloadableItem )

[3] A purchase of a downloadable item has not been downloaded more times than the maximum allowed by the downloadable item configuration and the website configuration.

Context PurchaseOfADownloadableItem :: hasNotMadeMoreDownloadsThanPermitted() : Boolean body: let maximumDownloadCountOfWebsite: PositiveInteger = self.SCItemOfADownloadableProd- uct.order.storeView.store.website.catalogConfigurationInWebsite.maximumDownloadCount in self.downloadableItem.maximumDownloadCount.isDefined() implies self.downloadCount <= self.downloadableItem.maximumDownloadCount and maximumDownloadCountOfWebsite.isDefined() implies self.downloadCount <= maximumDown- loadCountOfWebsite

[4] A Shopping Cart Item is not directly associated to a configurable or grouped product.

Context ShoppingCartItem :: doesNotPurchaseConfigurableBundleOrGroupedProductsDirectly() : Boolean body: not ( self.product -> oclIsTypeOf(ConfigurableProduct) or self.product -> oclIsTypeOf(GroupedProduct) or self.product -> oclIsTypeOf(BundleProduct) )

[5] The configurated product purchased is one of the associated products of the configurable product it has been purchaset through.

Context SCItemOfAConfiguratedProduct :: hasACorrectConfigurableProduct() : Boolean body: self.configurableProduct.associatedProduct -> includes( self.configuratedProduct )

[6] The bundle product options chosen are options available of the purchased bundle product.

Context SCItemOfABundleProduct :: hasTheCorrectBundleProductOptions() : Boolean body: self.bundleProduct.bundleProductItem.bundleProductOption -> includesAll( self.bundlePro- ductOption )

I Description A Shopping Cart Item Of a Downloadable Product must specify which downloadable items have been purchased. The selected items will affect its base price. For each item, the system saves:

• downloadCount: the number of times the item has been downloaded. • calculatedPrice: an amount which includes not only the custom option price incre- ments, but also the increments due to the purchased Downloadable Items.

Shopping Cart Items of a Configurated Product must know the information about:

• configurableProduct: the Configurable Product this product has been bought through.-

• catalogPriceRuleOfConfigurableProduct: the catalog price rules applied to the con- figurable product (not the configurated). • basePrice: the base price of the configurable product (not the configurated). • calculatedPrice: an amount which includes not only the custom option price incre- ments, but also the increments due to the Configuration chosen.

158 The base price of a Shopping Cart Item Of a Configurated Product will be calculated taking into account the base price of the Configurable Product and the increments defined in the corresponding Configuration of the Configurable Product. An Shopping Cart Item Of a Bundle Product must specify:

• bundlePrice: an amount taking into account the price method of the Bundle Product and the increments due to the products purchased as Bundle Options. • calculatedPrice: an amount which includes not only the bundle price, but also the reductions due to special reductions, relative tiered prices and catalog price rules.

For each bundle product option, the following attributes must be also specified:

• quantity: the purchased quantity of this product. • basePriceOfBundleProductOption: the base price of the product associated to the selected bundle product option.

The bundle price of an Shopping Cart Item Of a Bundle Product will be calculated de- pending on the price method defined by the Bundle Product. Finally, the base price of the Shopping Cart Item will be calculated by applying to the Bundle Net Price the reductions due to specialNetPrice, tieredPrice and Catalog Price Rules.

I Price calculation process The price calculation process can be informally represented by the following schema.

159 160 5.8.4 Orders

I Overview Orders are the confirmation that a customer wants to buy the contents of its shopping cart.

I Schema Diagram

161 I Derivation Rules

[1] Order :: purchased is the Date and Time when the order was placed

Context Order :: purchased : DateTime body: Now()

[2] Order :: shippingCosts are the costs derived from the used shipping method.

Context Order :: shippingCosts : Money body: self.shippingMethod.shippingMethodInWebsite -> select ( smiw | smiw.website = self.store- View.store.website).calculateCost( self.orderLine, self.delivery )

[3] Order :: total its the total cost the user will pay for the products ordered, including its shipping costs.

Context Order :: total : Money body: self.orderLine.total -> sum() + self.shippingCosts

[4] Order :: name is the first and last name of the order’s customer.

Context Order :: name : String body: self.customer.firstName.concat( self.customer.lastName )

[5] Order :: eMail is the email of the order’s customer.

Context Order :: eMail : String body: self.customer.eMail

[6] Order :: websiteName its the name of the website where the order has been placed.

Context Order :: websiteName : Money body: self.storeView.store.website.name

[7] Order :: storeName its the name of the store where the order has been placed.

Context Order :: storeName : Money body: self.storeView.store.name

[8] Order :: storeViewName its the name of the store view where the order has been placed.

Context Order :: storeViewName : Money body: self.storeView.name

I Constraints

[1] An order is identified by the customer that has place it, the store view where it has been made and the time it was created.

Context Order :: isIdentifiedByItsCustomerStoreViewAndCreationDate() : Boolean body: Order.allInstances() -> isUnique(Setcustomer,storeView,purchased)

[2] The products that appear in the order should be available in the website the store of the order belongs to.

162 Context Order :: hasProductsFromTheCorrectStore() : Boolean body: self.storeView.store.website.product.sku -> includesAll(self.orderLine.productSku -> asSet())

[3] An order with only virtual and downloadable products should not have shipping method and address defined, an order with any other type of product should.

Context Order :: hasShippingPropertiesOnlyWhenNeeded() : Boolean body: let products : Set(Product) = Product.allInstances() -> select(p|self.orderLine.productSku -> includes(p.sku)) in products -> forAll ( p | (p.oclIsTypeOf(VirtualProduct) or p.oclIsTypeOf(DownloadableProduct) ) = (self.delivery -> isEmpty() and self.shippingMethod -> isEmpty()) ) .

[4] If an Order has been edited, so it has a newer version of itself, the older version should be cancelled.

Context Order :: statusIsCancelledIfWasEdited() : Boolean body: self.newVersion -> notEmpty() implies self.status = OrderStatus::Cancelled.

[5] If an Order is Pending, no invoices or shipments are made for it.

Context Order :: meetsPendingStatusConditions() : Boolean body: self.status = OrderStatus::Pending implies ( self.invoice -> isEmpty() and self.shipment -> isEmpty() )

[6] If an Order is Processing, some invoices are made for it, but not all possible.

Context Order :: meetsProcessingStatusConditions() : Boolean body: self.status = OrderStatus::Processing implies ( self.invoice -> notEmpty() and self.invoice.invoiceLine.quantity -> sum() < self.orderLine.quan- tity -> sum() and self.shipment -> notEmpty() and self.shipment.shipmentLine.quantity -> sum() < self.orderLine.- quantity -> sum() )

[7] If an Order is Complete, all possible invoices and shipments are made for it, but not all credit memos..

Context Order :: meetsCompleteStatusConditions() : Boolean body: self.status = OrderStatus::Complete implies ( self.invoice.invoiceLine.quantity -> sum() = self.orderLine.quantity -> sum() and self.shipment.shipmentLine.quantity -> sum() = self.orderLine.quantity -> sum() and self.refund.refundLine.quantity -> sum() < self.orderLine.quantity -> sum() )

[8] If an Order is Closed, credit memos cover all items ordered, which implies Invoices cover all items also because of ’hasNotMoreRefundedProductsThanInvoiced’ constraint.

Context Order :: meetsClosedStatusConditions() : Boolean body: self.status = OrderStatus::Closed implies ( self.refund.refundLine.quantity -> sum() = self.orderLine.quantity -> sum() )

[9] If an Order is Cancelled, not all invoices and shipments can be already made for it.

163 Context Order :: meetsCancelledAndHoldStatusConditions() : Boolean body: (self.status = OrderStatus::Cancelled or self.status = OrderStatus::Hold) implies ( self.invoice.invoiceLine.quantity -> sum() < self.orderLine.quantity -> sum() and self.shipment.shipmentLine.quantity -> sum() < self.orderLine.quantity -> sum() )

I Description When a Customer confirms that he wants to buy the contents of a Shopping Cart, the system generates an Order. Orders have the following information:

• name: the name of the customer who has placed the order. Derived Attribute. • eMail: the order’s customer eMail. Several notifications concerning this Order will be sent here. Derived Attribute. • websiteName: the name of the website where the order has been placed. Derived Attribute. • storeName: the name of the store where the order has been placed. Derived At- tribute. • storeViewName: the name of the store view where the order has been placed. De- rived Attribute. • purchased: the time the Order was placed. • couponCode: a literal code that can enable the application of a Shopping Cart Price Rule that matches it. • eMailSent: indicates if an email has been sent to the order’s costumer to notice him the invoice was recorded. • giftMessage: a message that will be delivered to the person that receives the products purchased in this order. • shippingCosts: the amount that will be paid for shipping the purchased products to the customer’s delivery address, using the selected shipping method. • total: the total amount the customer will pay for this order. Derived Attribute.

The total invoiced, non reinvoiceable, paid, shipped and refunded amounts will be detailled in the following sections. An Order has also a status attribute, that can take the following values:

• Pending: no invoices or shipments have been made for this Order. • Processing: some invoices or shipments have been made for this Order, but they do not cover all its ordered products. • Completed: the whole Order has been paid and shipped, but not all items had been refunded. • Closed: the whole Order has been refunded (so, it had been already paid before). • Cancelled: the Order has been cancelled., so all the items that had not been invoiced and/or shipped before are cancelled. Items already invoiced and/or shipped are not affected. A completed or closed Order cannot be Cancelled. • Hold: the Order is temporary holded, so no changes can be made to it until someone unholds it.

Each different Product purchased in this Order is represented as an OrderLine. Order lines will be detailed in the next section. The entity type Order participates in several relationship types:

164 • storeView: the storeView the costumer used to record this Order. If the order was created by and administrator, he should choose one store view. The website that store view belongs to determines to which product list you will have access when creating the order and to which of the customer’s Buying Process Information lists you will have access. • customer: the Customer that has made the Order. If the customer does not have an account in the website store you have chosen is in, an account will be created for the customer in that website upon completion of the order, with all the customer information duplicated. A Guest (a not registered Costumer) can also make orders without registrating themselves. In this case, no Customer will be associated to the Order. • customerGroup: some characteristics (as taxes) will be applied to the Order depend- ing on which CustomerGroup it is related. Even if normally this will be the same as the group the Customer belongs to, administrators can choose another from the back- end. Even thought, the group the costumer belongs to is not changed. • billing: the adress where the bill for this Order will be sent. It is not mandatory that they should be part of the Customer adresses. • delivery: the adress where the products purchased will be shipped. It is not manda- tory that they should be part of the Customer adresses. • rateApplicationAdress: will be used to calculate the tax rates applied to this order.

• shippingMethod: the method that will be used to ship the products to the Customers delivery adress. Each method involves a specific method for calculating the costs. • paymentMethod: the method that will be used for the Costumer to pay the order amount. If it is a credit card method, the credit card info is saved. • newVersion / previousVersion: already recorded Orders can be edited. This will simultaneously create a new order and cancel the previous order. Each version of an other is associated to its immediately newer and previous version.

Concerning currency management, an Order has the following information:

• genericBaseCurrency: the currency setted as the generic base currency when the order was placed. • websiteRedefinedBaseCurrency: the currency setted as website’s base currency when the order was placed, if defined. The conversion rate with respect to the generic base currency at the time the order was placed is saved. • purchasingCurrency: the currency in which the order was placed. If the order was placed using the genericBaseCurrency, no purchasing currency is saved. The conver- sion rate with respect to the generic base currency at the time the order was placed is saved.

Several Comments can be made to an Order, even when it is already created. For each Comment, the system saves its text, the time it was created, an status and if the customer who purchases the order had been notified for this comment.

I State Transition Diagram The state of Orders can be conceptually modeled by using a state transition diagram.

165 166 5.8.5 OrderLines

I Overview Each different Product purchased in a Order is represented by an OrderLine.

I Schema Diagram

I Constraints

[1] An order line is identified by its order, the product sku and the info about the options choosen.

Context OrderLine :: isIdentifiedByItsOrderProductSkuAndOptions() : Boolean body: OrderLine.allInstances() -> isUnique(Setorder,productSku,textOptions,dateOptions,predefinedContentOptions)

I Description An OrderLine has the following attributes:

• productSku: the sku of the purchased product. • productName: the name of the purchased product. • quantity: the number of items puchased of this product.

167 • giftMessage: a message that will be delivered to the person that receives the products purchased in this order line. • price: the unitary price the customer has finally paid for the product, taking into ac- count the calculated price and the custom price. No taxes, discounts or shipping costs are compounded. • tax: the global cost from the application of all the applicable tax rates to the set of products purchased in this order line.

• discount: the global amount that was discounted from the set of products purchased in this order line, determined at the purchasing time by shopping cart price rules. • total: the total cost of this order line.

If the product purchased in this order line has options, the values that the customer has given for them are specified in the order line.

168 5.8.6 Invoices

I Overview Magento allows administrators to generate invoices for the ordered products.

I Schema Diagram

I Derivation Rules

[1] Invoice :: shippingCosts is the shipping amount for the entire order, if it is the first Invoice for its Order; zero otherwise.

Context Invoice :: shippingCosts : Integer body: let isFirstInvoice:Boolean = equals(self, self.order.invoice -> any ( i | self.order.invoice -> forAll ( i2 | i <> i2 implies i.createdAt < i2.createdAt ) ) ) in if isFirstInvoice then self.order.shippingCosts else 0 endif

[2] Invoice :: total its the total cost the user have paid, including shipping costs and taxes.

169 Context Invoice :: total : Integer body: self.invoiceLine.total -> sum() + self.shippingCosts

[3] InvoiceLine :: total is the unitary cost paid for this product multiplied by the quantity in the Invoice.

Context InvoiceLine :: total : Integer body: self.orderLine.price * self.quantity + (self.orderLine.tax / self.orderLine.quantity) * self.quantity + (self.orderLine.discount / self.orderLine.quantity) * self.quantity

[4] Order :: notCancelledInvoice is the set of invoices made for this Order that had not been cancelled.

Context Order :: notCancelledInvoice : Set(Invoice) body: self.invoice -> select ( i | i.status <> InvoiceStatus::Cancelled )

[5] Order :: totalInvoiced its the total amount already invoiced for the products of this order.

Context Order :: totalInvoiced : Money body: self.notCancelledInvoice.total -> sum()

[6] Order :: totalPaid its the total amount that the Costumer has already paid for the products of this order.

Context Order :: totalPaid : Money body: self.invoice -> select ( i | i.status = InvoiceStatus::Paid ).total -> sum()

[7] Order :: totalNonReInvoiceable its the total amount of the cancelled orders that where already paid online througth a third-party gateway, so its products are not able to be re-invoiced again.

Context Order :: totalNonReInvoiceable : Money body: self.invoice -> select ( i | i.status = InvoiceStatus::Cancelled and i.isPaidOnline).total -> sum()

I Constraints

[1] An invoice line is identified by its invoice and order line.

Context InvoiceLine :: isIdentifiedByItsInvoiceAndOrderLine() : Boolean body: InvoiceLine.allInstances() -> isUnique(Setinvoice,orderLine)

[2] An invoice line refers to an order line of the corresponding order.

Context InvoiceLine :: refersToACorrectOrderLine() : Boolean body: self.invoice.order.orderLine -> includes(self.orderLine)

[3] An invoice is identified by the order it belongs to and the time it was created.

Context Invoice :: isIdentifiedByItsOrderAndCreationDate() : Boolean body: Invoice.allInstances() -> isUnique(Setorder,createdAt)

[4] The payment method of an invoice that has been paid automatically online should be one of the followings: Authorize.net, PayFlowPro, PayPal or GoogleCheckout.

Context Invoice :: isPaidOnlineOnlyIfHasExternalPaymentMethod() : Boolean body:

170 self.isPaidOnline implies ( self.order.paymentMethod.oclIsTypeOf( AuthorizeNet ) or self.order.paymentMethod.oclIsTypeOf( PayFlowPro ) or self.order.paymentMethod.oclIsTypeOf( PayPalMethod ) or self.order.paymentMethod.oclIsTypeOf( GoogleCheckout ) )

[5] Invoices related to this order don’t have a greater quantity of a product than the quantity ordered for this product.

Context OrderLine :: hasNotMorePaidProductsThanOrdered() : Boolean body: let notCancelledLines: Set(InvoiceLine) = self.invoiceLine -> select(li|li.invoice.status <> InvoiceStatus::Cancelled ) in notCancelledLines.quantity -> sum() <= self.quantity

I Description An Invoice is a document issued by the seller to the customer listing the goods or services purchased and the sum of money due. Multiple Invoices can be created per Order, containing as much of the purchased item quantity. However, each unit of each item can only be invoiced one time. In Magento, the Invoice for a product should be created at the latest when the customer pays the money for this product, but they can also be created before. The system saves the following information for each Invoice:

• createdAt: the time the Invoice has been recorded. • emailSent: indicates if an email has been sent, during the creation of the Invoice, to the order’s costumer, to notice him the Invoice was recorded. • capturingMethod: indicates the method used to capture the payment for this invoice: automathically online with Magento, automatically Online throught a third party method or only registering the invoice (with no payment). • isPaidOnline: indicates if the customer’s payment of the Invoice’s amount has been done automatically during Invoice creation. This can only be done throught an external online payment method: Authorize.net, PayFlowPro, PayPal or GoogleCheckout.

• shippingCosts: the shipping costs. They are only compounded for the first Invoice made for an Order, regardless of how many products are included in this first Invoice.- Derived Attribute.

• total: the total price the Costumer have should pay for this Invoice.

The status of an Invoice indicates whether:

• Pending: the Invoice’s amount has not been paid yet by the customer. • Paid: the Invoice’s amount has been already paid. • Cancelled: the administrator has made an error creating the invoice or has changed his opinion, and does not want to take this invoice into account no more.

Cancelled Invoice’s can be re-invoiced or not depending on if the invoice has been au- tomatically paid online or not. In the last, the products that where in the invoice can be re-invoiced, whether in the first they can not. Each InvoiceLine records that one kind of product has been paid in this Invoice. The product is specified by the related Order Line. The following information is also saved:

171 • quantity: records the number of items of this Product that had been paid. • total: is the total amount paid for this quantity of this product. Derived Attribute.

Several Comments can also be made to a Invoice, even when it is already created. Of course, each Invoice is also related to the Order it belongs to.

I State Transition Diagram The state of Invoices can be conceptually modeled by using a state transition diagram.

172 5.8.7 Shipments

I Overview Magento records the items in an Order which have been already shipped to the Costumer’s destination.

I Schema Diagram

I Constraints

[1] A shipment is identified by the order it belongs to and the time it was created.

Context Shipment :: isIdentifiedByItsOrderAndCreationDate() : Boolean body: Shipment.allInstances() -> isUnique(Setorder,createdAt)

[2] A refund line refers to an order line of the corresponding order.

Context ShipmentLine :: refersToACorrectOrderLine() : Boolean body: self.shipment.order.orderLine -> includes(self.orderLine)

[3] Shipments related to this order do not ship a greater quantity of a product than the quantity ordered for this product.

Context OrderLine :: hasNotMoreShippedProductsThanOrdered() : Boolean body: self.shipmentLine.quantity -> sum() <= self.quantity

173 I Description A Shipment it’s a record that some quantity of the products in that Order have been al- ready shipped to its buyer, and they were not recorded to be shipped before. Whether their physically shipment could be together or not, the fact that they appear in the same Ship- ment means that someone (normally a system administrator) has recorded their shipment together, at a determinated point of time. Multiple shipments can be created per Order, as someone has recorded at diferent mo- ments that this products have been shipped. However, each unit of each item can only be shipped only one time, so there is a finite limit of Shipments. The system saves the following information for each shipment:

• createdAt: the time the Shipment has been recorded. • emailSent: indicates if an email has been sent to the order’s costumer to notice him the shipment was recorded. • trackingNumber: can optionally be associated to the shipment in order to facilitate eventual location purposes.

Each ShipmentLine records that one kind of product has been shipped in this shipment.- The product is specified by the related Order Line. The following information is also saved:

• quantity: records the number of items of this Product that had been shipped.

Several Comments can also be made to a Shipment, even when it is already created. Of course, each shipment is related to the Order it belongs to.

174 5.8.8 Refund

I Overview Magento records the Refunds made by Customers. Magento refers them as "Credit Memos", which is the document that registers a Refund.

I Schema Diagram

I Derivation Rules

[1] Refund :: total is the total amount refunded to the costumer, including the shipping and the additional amounts refunded (which are added) and the fee (which is substracted).

Context Refund :: total : Integer body: self.refundLine.total -> sum() + self.shippingAmount + self.additionalAmount - self.fee

[2] RefundLine :: total is the unitary cost of this product multiplied by the quantity of items re- funded of this product.

Context RefundLine :: total : Boolean body: self.orderLine.price * self.quantity

[3] Order :: totalRefunded its the total amount of this order that has been refunded to the cos- tumer.

175 Context Order :: totalRefunded : Integer body: self.refund.total -> sum()

I Constraints

[1] A refund is identified by the order it belongs to and the time it was created.

Context Refund :: isIdentifiedByItsOrderAndCreationDate() : Boolean body: Refund.allInstances() -> isUnique(Setorder,createdAt)

[2] A refund line refers to an order line of the corresponding order.

Context RefundLine :: refersToACorrectOrderLine() : Boolean body: self.refund.order.orderLine -> includes(self.orderLine)

[3] A refund line refers to an order line that has been already invoiced.

Context RefundLine :: refersToAnOrderLineAlreadyInvoiced() : Boolean body: self.orderline.invoiceLine -> notEmpty()

[4] The total quantity refunded for each product should be equal or smaller than the total quantity invoiced.

Context OrderLine :: hasNotMoreRefundedProductsThanInvoiced() : Boolean body: let notCancelledLines: Set(InvoiceLine) = self.invoiceLine -> select(li|li.invoice.status <> InvoiceStatus::Cancelled ) in notCancelledLines.quantity -> sum() <= self.refundLines.quantity -> sum()

[5] The total quantity refunded for this order cannot be greater than the total quantity paid.

Context Order :: hasNotRefundedMoreThanPaid() : Boolean body: self.totalRefunded <= self.totalPaid

[6] The total amount refounded from the shipping cost of an order in all its refunds cannot be greater than the shipping costs

Context Order :: hasRefundsWithCorrectRefundShipping() : Boolean body: self.refund.shippingAmount -> sum() <= self.shippingCosts

[7] We cannot create a Refund for an Order until an Invoice has been created for this Order.

Context Order :: hasARefundOnlyIfHasInvoice() : Boolean body: self.refund -> notEmpty() implies self.invoice -> notEmpty()

I Description A Refund is placed when a Customer returns a purchased Product that is unsatisfactory for him. This will normally cause the seller to give back the amount paid for this product, althought the returned quantity can be increased or decreased. The status of the refund indicates whether:

• Pending: the customer has returned the product, but no amount money is given back to him. • Refunded: the customer has returned the product and the seller has given back him the corresponding amount.

176 • Cancelled: the customer has finally changed his opinion an does not want to refund the product no more. Refunds could not be deleted from the system.

A product can not be refunded until it is paid. Multiple Refunds can be created per Order, but each unit can only be refunded one time. The system saves the following information for each refund:

• createdAt: the time the Shipment has been recorded. • emailSent: indicates if an email has been sent, during the creation of the refund, to the order’s costumer, to notice him the refund was recorded. • shippingAmount: the amount refunded from the shipping costs. • additionalAmount: an additional refund, not related to any particular aspect of the order. . • fee: an amount charged to the customer for placing the refund. It is subtracted from the total refunded. • total: the total amount that will be refunded to the Costumer for this Refund. Derived Attribute.

Each RefundLine records that one type of product has been refunded. The product is specified by the related Order Line. The following information is also saved:

• quantity: records the number of items of this Product that had been refunded. • total: is the total amount refunded for the quantity of that product in this Refund, as it is described in its derivation rule. Derived Attribute.

Several Comments can also be made to a Refund, even when it is already created. Of course, each Refund is related to the Order it belongs to.

I State Transition Diagram The state of Refunds can be conceptually modeled by using a state transition diagram.

177 6 Behavioral Schema. Use Case Specification

In this chapter we develop the first part of the behavioral schema of the Magento information system. Section 6.1 defines the scope of the schema elicitation, while section 6.2 describes the way the schema is presented. Section 6.3 presents an overview of the described Use Cases, which are detailed in the remaining sections.

6.1 Scope

Use Cases specification covers all the functions that the system perform within the scope defined in section 5.1.

6.2 Schema presentation

The main purpose of a behavioral schema is to specify the valid changes in the domain state, as well as the actions that the described system can perform. In the following sections of this chapter, and in the next chapter, we describe Magento’s be- havioral schema. We show first a list of all the defined use cases and its primary actor, in order to give an overview of the most important funcionalities of the system and the stakeholders that interact with them. This list can be easily represented by a use case diagram. After that, each use case is specified textually, giving the following information:

I Primary Actor(s)

I Precondition

I Trigger

I Main Success Scenario

I Extensions The Main Success Scenario and Extensions are described as an interaction of activities between the primary actors and the system. Some of those activities are mapped to system Events, using the following syntaxis:

178 [-> EventName] Use Cases are organised in six groups. First five are the same as in Structural Schema. They are defined in section 5.3. In addition, Magento’s behavioral schema includes several use cases that retrieve information about the system, without modifying it’s state. They are specified here and classified in the Store Reports Use Case group.

6.3 Use Case Overview

I Store Configuration

1. Add a website. Primary Actor: Store Administrator. 2. Edit a website. Primary Actor: Store Administrator. 3. Delete a website. Primary Actor: Store Administrator. 4. Select the default website. Primary Actor: Store Administrator. 5. Add a store. Primary Actor: Store Administrator. 6. Edit a store. Primary Actor: Store Administrator. 7. Delete a store. Primary Actor: Store Administrator. 8. Select the default store of a website. Primary Actor: Store Administrator. 9. Add an store view. Primary Actor: Store Administrator. 10. Edit an store view. Primary Actor: Store Administrator. 11. Delete an store view. Primary Actor: Store Administrator. 12. Select the default store view of a store. Primary Actor: Store Administrator. 13. Select the default language. Primary Actor: Store Administrator. 14. Change the catalog configuration values. Primary Actor: Store Administrator. 15. Change the stock configuration values. Primary Actor: Store Administrator. 16. Change the wishlist configuration values. Primary Actor: Store Administrator. 17. Change the sales configuration values. Primary Actor: Store Administrator. 18. Change the customer configuration values. Primary Actor: Store Administrator. 19. Change the tax configuration values. Primary Actor: Store Administrator. 20. Change the shipping configuration values. Primary Actor: Store Administrator. 21. Change the tell to a friend configuration values. Primary Actor: Store Administra- tor. 22. Change the currency configuration values. Primary Actor: Store Administrator. 23. Change the enabled currencies. Primary Actor: Store Administrator. 24. Update currency rates. Primary Actor: Store Administrator. 25. Change the geographical configuration values. Primary Actor: Store Administrator. 26. Change shipping method values. Primary Actor: System Administrator. 27. Change payment method values. Primary Actor: System Administrator. 28. Add a customer tax class. Primary Actor: Store Administrator. 29. Edit a customer tax class. Primary Actor: Store Administrator. 30. Delete a customer tax class. Primary Actor: Store Administrator.

179 31. Add a product tax class. Primary Actor: Store Administrator. 32. Edit a product tax class. Primary Actor: Store Administrator. 33. Delete a product tax class. Primary Actor: Store Administrator. 34. Add a tax rate. Primary Actor: Store Administrator. 35. Edit a tax rate. Primary Actor: Store Administrator. 36. Delete a tax rate . Primary Actor: Store Administrator. 37. Add a tax rule. Primary Actor: Store Administrator. 38. Edit a tax rule. Primary Actor: Store Administrator. 39. Delete a tax rule . Primary Actor: Store Administrator.

I Customers

1. Create a Customer. Primary Actor: Customer, Store Administrator. 2. Change password. Primary Actor: Customer. 3. Change customer details. Primary Actor: Customer. 4. Administrate address book. Primary Actor: Customer. 5. Edit a customer. Primary Actor: Store Administrator. 6. Delete a customer. Primary Actor: Store Administrator. 7. Show account information. Primary Actor: Customer. 8. Show address book. Primary Actor: Customer. 9. Add a customer group. Primary Actor: Store Administrator. 10. Edit a customer group. Primary Actor: Store Administrator. 11. Delete a customer group. Primary Actor: Store Administrator. 12. Add an administrator. Primary Actor: Store Administrator. 13. Edit an administrator. Primary Actor: Store Administrator. 14. Delete an administrator. Primary Actor: Store Administrator. 15. Add a role. Primary Actor: Store Administrator. 16. Edit a role. Primary Actor: Store Administrator. 17. Delete a role. Primary Actor: Store Administrator. 18. Log In. Primary Actor: Customer. 19. Log Out. Primary Actor: Customer. 20. Open session. Primary Actor: Customer. 21. Finish session. Primary Actor: Customer. 22. Change the current Website. Primary Actor: Customer. 23. Change the current Store. Primary Actor: Customer. 24. Change the current Store View. Primary Actor: Customer. 25. Change the current Currency. Primary Actor: Customer.

I Store Administration

1. Add a product. Primary Actor: Store Administrator.

180 2. Edit a product. Primary Actor: Store Administrator. 3. Delete a product. Primary Actor: Store Administrator. 4. Add a product category. Primary Actor: Store Administrator. 5. Edit a product category. Primary Actor: Store Administrator. 6. Move a product category. Primary Actor: Store Administrator. 7. Delete a product category. Primary Actor: Store Administrator. 8. Add an attribute. Primary Actor: Store Administrator. 9. Edit an attribute. Primary Actor: Store Administrator. 10. Delete an attribute. Primary Actor: Store Administrator. 11. Add an attribute set. Primary Actor: Store Administrator. 12. Edit an attribute set. Primary Actor: Store Administrator. 13. Delete an attribute set. Primary Actor: Store Administrator. 14. Administrate tier prices of a product. Primary Actor: Store Administrator. 15. Add a catalog price rule. Primary Actor: Store Administrator. 16. Edit a catalog price rule. Primary Actor: Store Administrator. 17. Delete a catalog price rule. Primary Actor: Store Administrator. 18. Add a shopping cart price rule. Primary Actor: Store Administrator. 19. Edit a shopping cart price rule. Primary Actor: Store Administrator. 20. Delete a shopping cart price rule. Primary Actor: Store Administrator.

I Additional Activities

1. Tag a product. Primary Actor: Customer. 2. Add a tag. Primary Actor: Store Administrator. 3. Edit a tag. Primary Actor: Store Administrator. 4. Delete a tag. Primary Actor: Store Administrator. 5. Show tags of a customer. Primary Actor: Customer. 6. Show most popular tags. Primary Actor: Customer. 7. Show all tags. Primary Actor: Customer. 8. Add a review. Primary Actor: Customer, Store Administrator. 9. Edit a review. Primary Actor: Store Administrator. 10. Delete a review. Primary Actor: Store Administrator. 11. Add a review property. Primary Actor: Store Administrator. 12. Edit a review property. Primary Actor: Store Administrator. 13. Delete a review property. Primary Actor: Store Administrator. 14. Show reviews of a product. Primary Actor: Customer, Store Administrator. 15. Show reviews of a customer. Primary Actor: Customer. 16. Add a product to the wish list. Primary Actor: Customer. 17. Remove a product from the wish list. Primary Actor: Customer. 18. Add a product to the compare list. Primary Actor: Customer. 19. Remove a product from the compare list. Primary Actor: Customer.

181 20. Share wish list. Primary Actor: Customer. 21. Subscribe to a product price alert. Primary Actor: Customer. 22. Subscribe to a product stock alert. Primary Actor: Customer. 23. Show wish list. Primary Actor: Customer. 24. Show ready to compare products. Primary Actor: Customer. 25. Show recently compared products. Primary Actor: Customer. 26. Show recently viewed products. Primary Actor: Customer. 27. Show compare. Primary Actor: Customer. 28. Add a newsletter template. Primary Actor: Store Administrator. 29. Edit a newsletter template. Primary Actor: Store Administrator. 30. Delete a newsletter template. Primary Actor: Store Administrator. 31. Add a newsletter. Primary Actor: Store Administrator. 32. Edit a newsletter. Primary Actor: Store Administrator. 33. Subscribe to the newsletter. Primary Actor: Customer. 34. Unsubscribe from the newsletter. Primary Actor: Customer. 35. Delete a newsletter subscription. Primary Actor: Store Administrator. 36. Unsubscribe a newsletter subscription. Primary Actor: Store Administrator. 37. Show shopping cart. Primary Actor: Customer. 38. Download a product. Primary Actor: Customer. 39. Show downloadable products of a customer. Primary Actor: Customer. 40. Tell to a friend. Primary Actor: Customer. 41. View product information. Primary Actor: Customer. 42. Search products. Primary Actor: Customer. 43. Show the best purchased products. Primary Actor: Customer. 44. Reset the temporal information. Primary Actor: System.

I Online catalog

1. Place an order. Primary Actor: Customer. 2. Checkout an order with multiple addresses. Primary Actor: Customer. 3. Add an order. Primary Actor: Store Administrator. 4. Cancel an order. Primary Actor: Store Administrator. 5. Hold an order. Primary Actor: Store Administrator. 6. Unhold an order. Primary Actor: Store Administrator. 7. Reorder. Primary Actor: Customer, Store Administrator. 8. Add a gift message to a placed order. Primary Actor: Store Administrator. 9. Print an Order. Primary Actor: Customer. 10. Send order information by email. Primary Actor: Store Administrator. 11. Show previous orders of a customer. Primary Actor: . 12. Add an invoice. Primary Actor: Store Administrator. 13. Register an invoice payment. Primary Actor: Store Administrator.

182 14. Cancel an invoice. Primary Actor: Store Administrator. 15. Add a shipment. Primary Actor: Store Administrator. 16. Add a refund. Primary Actor: Store Administrator. 17. Cancel a refund. Primary Actor: Store Administrator. 18. Add a comment to an order, invoice, shipment or refund.. Primary Actor: Store Administrator.

I Store Reports

1. Show the sales report. Primary Actor: Store Administrator. 2. Show the tax report. Primary Actor: Store Administrator. 3. Show the shipping report. Primary Actor: Store Administrator. 4. Show the invoices report. Primary Actor: Store Administrator. 5. Show the refunds report. Primary Actor: Store Administrator. 6. Show the coupon codes report. Primary Actor: Store Administrator. 7. Show the products in carts report. Primary Actor: Store Administrator. 8. Show the abandoned carts report. Primary Actor: Store Administrator. 9. Show the bestseller products report. Primary Actor: Store Administrator. 10. Show the ordered products report. Primary Actor: Store Administrator. 11. Show the most viewed products report. Primary Actor: Store Administrator. 12. Show the stock of products report. Primary Actor: Store Administrator. 13. Show the downloads report. Primary Actor: Store Administrator. 14. Show the new customer accounts report. Primary Actor: Store Administrator. 15. Show the customers by orders total report. Primary Actor: Store Administrator. 16. Show the customers by number of orders report. Primary Actor: Store Administra- tor. 17. Show the customer reviews report. Primary Actor: Store Administrator. 18. Show the product reviews report. Primary Actor: Store Administrator. 19. Show the tags by customer report. Primary Actor: Store Administrator. 20. Show the tags by product report. Primary Actor: Store Administrator. 21. Show the popular tags report. Primary Actor: Store Administrator. 22. Show the search terms report. Primary Actor: Store Administrator.

183 6.4 Store Configuration

Use Case 6.4.1 Add a website

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a website

Main success scenario 1. The store administrator provides the website’s details. 2. The system saves the new website. [NewWebsite]

Use Case 6.4.2 Edit a website

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a website

Main success scenario 1. The store administrator selects the website to be edited. 2. The store administrator provides the new website’s details. 3. The system saves the new website. [EditWebsite]

Use Case 6.4.3 Delete a website

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a website

Main success scenario 1. The store administrator selects the website to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the website 4. The system deletes the website. [DeleteWebsite]

Extensions 2a.The store administrator does not want to delete the website 2a1. The use case ends

184 Use Case 6.4.4 Select the default website

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to change the default website

Main success scenario 1. The store administrator selects the website which will become the default website 2. The system updates the default website [SetDefaultWebsite]

Use Case 6.4.5 Add a store

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a store

Main success scenario 1. The store administrator provides the store’s details. 2. The system saves the new store. [NewStore]

Use Case 6.4.6 Edit a store

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a store

Main success scenario 1. The store administrator selects the store to be edited. 2. The store administrator provides the new store’s details. 3. The system saves the new store. [EditStore]

Use Case 6.4.7 Delete a store

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a store

Main success scenario 1. The store administrator selects the store to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the store

185 4. The system deletes the store. [DeleteStore]

Extensions 2a.The store administrator does not want to delete the store 2a1. The use case ends

Use Case 6.4.8 Select the default store of a website

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to change the default store of a website

Main success scenario 1. The store administrator selects the website whose default store will be changed. 2. The store administrator selects the store which will become the default store of this website. 3. The system updates the default website [SetDefaultStoreOfWebsite]

Use Case 6.4.9 Add an store view

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add an store view

Main success scenario 1. The store administrator provides the store view details. 2. The system saves the new store view. [NewStoreView]

Use Case 6.4.10 Edit an store view

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit an store view

Main success scenario 1. The store administrator selects the store view to be edited. 2. The store administrator provides the new store view details. 3. The system saves the new store view. [EditStoreView]

Use Case 6.4.11 Delete an store view

186 Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete an store view

Main success scenario 1. The store administrator selects the store view to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the store view 4. The system deletes the store view. [DeleteStoreView]

Extensions 2a.The store administrator does not want to delete the store view. 2a1. The use case ends

Use Case 6.4.12 Select the default store view of a store

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to change the default store view of a store

Main success scenario 1. The store administrator selects the store whose default store view will be changed. 2. The store administrator selects the store view which will become the default store view of this store. 3. The system updates the default store view of the store [SetDefaultStoreViewOfStore]

Use Case 6.4.13 Select the default language

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to change the default language

Main success scenario 1. The store administrator selects the language which will become the default language 2. The system updates the default language [SetDefaultLanguage]

Extensions 2a.The system administrator wants to define the default language for only a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2

187 Use Case 6.4.14 Change the catalog configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the catalog configuration.

Main success scenario 1. The system displays the current catalog configuration values. 2. The store administrator provides the new values for the catalog configuration. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeCatalogConfiguration]

Extensions 2a.The system administrator wants to change the catalog configuration values for only a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2

Use Case 6.4.15 Change the stock configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the stock configuration.

Main success scenario 1. The system displays the current stock configuration values. 2. The store administrator provides the new values for the stock configuration. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeStockConfiguration]

Use Case 6.4.16 Change the wishlist configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the wishlist configuration.

Main success scenario 1. The system displays the current wishlist configuration values. 2. The store administrator provides the new values for the wishlist configuration. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeWishListConfiguration]

188 Extensions 2a.The system administrator wants to change the wishlist configuration values for only a website 2a1. The system displays the available websites 2a2. The system administrator selects a website 2a3. The use case continues at step 2

Use Case 6.4.17 Change the sales configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the sales configuration.

Main success scenario 1. The system displays the current sales configuration values. 2. The store administrator provides the new values for the sales configuration. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeSalesConfiguration]

Extensions 2a.The system administrator wants to change the sales configuration values for only a website 2a1. The system displays the available websites 2a2. The system administrator selects a website 2a3. The use case continues at step 2

Use Case 6.4.18 Change the customer configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the customer configuration.

Main success scenario 1. The system displays the current customer configuration values. 2. The store administrator provides the new values for the sales configuration. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeCustomerConfiguration]

Extensions 2a.The system administrator wants to change the sales configuration values for only an store view. 2a1. The system displays the available store views 2a2. The system administrator selects an store view 2a3. The use case continues at step 2

189 Use Case 6.4.19 Change the tax configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the tax configuration.

Main success scenario 1. The system displays the current tax configuration values. 2. The store administrator provides the new values for the tax configuration. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeTaxConfiguration]

Extensions 2a.The system administrator wants to change the tax configuration values for only a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2

Use Case 6.4.20 Change the shipping configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the shipping configuration.

Main success scenario 1. The system displays the current shipping configuration values. 2. The store administrator provides the new values for the shipping configuration. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeShippingConfiguration]

Extensions 2a.The system administrator wants to change the shipping configuration values for only a website 2a1. The system displays the available websites 2a2. The system administrator selects a website 2a3. The use case continues at step 2

Use Case 6.4.21 Change the tell to a friend configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the tell to a friend configuration.

Main success scenario

190 1. The system displays the current tell to a friend configuration values. 2. The store administrator provides the new values for the tell to a friend configuration. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeTellToAFriendConfiguration]

Extensions 2a.The system administrator wants to change the shipping configuration values for only a store view 2a1. The system displays the available store views 2a2. The system administrator selects a store view 2a3. The use case continues at step 2

Use Case 6.4.22 Change the currency configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the currency configuration

Main success scenario 1. The system displays the enabled currencies. 2. The store administrator selects a base currency, the allowed currencies and the default cur- rency. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeCurrencyConfiguration]

Extensions 2a.The system administrator wants to change the currency configuration for only a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2 3a.A selected currency is not enabled or the selected default currency is not one of the selected allowed currencies 3a1. The system informes the store administrator 3a2. The use case continues at step 2

Use Case 6.4.23 Change the enabled currencies

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the enabled system currencies.

Main success scenario 1. The system displays all the currencies.

191 2. The store administrator selects a base currency, the allowed currencies and the default cur- rency. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeEnabledCurrencies]

Extensions 3a.Some of the selected currencies are currently a base, allowed or default currency in some store view. 3a1. The system informes the store administrator 3a2. The use case continues at step 2

Use Case 6.4.24 Update currency rates

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to updata automatically via Internet the change values for currency rates

Main success scenario 1. The system connects to the change information server. 2. The value change is automatically updated for all the currencies [UpdateCurrencyValue]

Use Case 6.4.25 Change the geographical configuration values

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants tho change the configuration related about countries.

Main success scenario 1. The system displays all the countries 2. The store administrator selects the allowed countries and a default country. 3. The system validates that the new values are correct 4. The system saves the new values [ChangeGeographicalConfiguration]

Extensions 2a.The system administrator wants to change the geographical configuration for only a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2 3a.The selected default country is not one of the selected allowed countries. 3a1. The system informes the store administrator 3a2. The use case continues at step 2

192 Use Case 6.4.26 Change shipping method values

Primary Actor: System Administrator Precondition: None Trigger: The store administrator wants to change the configuration values of a shipping method

Main success scenario 1. The system displays the shipping methods and its current values. 2. The sytem administrator provides the new values for the configurable attributes that he wants to change. 3. The system validates that the new values are correct 4. The system saves the new values [EditShippingMethods] 5. The system displays the new values of the shipping methods.

Extensions 2a.The system administrator wants to change shipping method values for only a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2

Use Case 6.4.27 Change payment method values

Primary Actor: System Administrator Precondition: None Trigger: The store administrator wants to change the configuration values of a payment method

Main success scenario 1. The system displays the payment methods and its current values. 2. The sytem administrator provides the new values for the configurable attributes that he wants to change. 3. The system validats that the new values are correct 4. The system saves the new values [EditPaymentMethods] 5. The system displays the new values of the payment methods.

Extensions 2a.The system administrator wants to change shipping method values only for a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2

Use Case 6.4.28 Add a customer tax class

Primary Actor: Store Administrator Precondition: None

193 Trigger: The store administrator wants to add a customer tax class

Main success scenario 1. The store administrator provides the customer tax class name. 2. The system saves the new customer tax class. [NewCustomerTaxClass]

Use Case 6.4.29 Edit a customer tax class

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a customer tax class

Main success scenario 1. The store administrator selects the customer tax class to be edited. 2. The store administrator provides the new customer tax class name. 3. The system saves the new customer tax class. [EditCustomerTaxClass]

Use Case 6.4.30 Delete a customer tax class

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a customer tax class

Main success scenario 1. The store administrator selects the customer tax class to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the customer tax class 4. The system deletes the customer tax class. [DeleteCustomerTaxClass]

Extensions 2a.The store administrator does not want to delete the customer tax class 2a1. The use case ends

Use Case 6.4.31 Add a product tax class

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a product tax class

Main success scenario 1. The store administrator provides the product tax class name. 2. The system saves the new product tax class.

194 [NewProductTaxClass]

Use Case 6.4.32 Edit a product tax class

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a product tax class

Main success scenario 1. The store administrator selects the product tax class to be edited. 2. The store administrator provides the new product tax class name. 3. The system saves the changes. [EditProductTaxClass]

Use Case 6.4.33 Delete a product tax class

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a product tax class

Main success scenario 1. The store administrator selects the product tax class to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the product tax class 4. The system deletes the product tax class. [DeleteProductTaxClass]

Extensions 2a.The store administrator does not want to delete the product tax class 2a1. The use case ends

Use Case 6.4.34 Add a tax rate

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a tax rate

Main success scenario 1. The store administrator provides the details of the new.tax rate 2. The system validates that the data is correct. 3. The system saves the new tax rate. [NewTaxRate]

195 Use Case 6.4.35 Edit a tax rate

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a tax rate

Main success scenario 1. The store administrator selects the tax rate to be edited. 2. The store administrator provides the new details of the tax rate. 3. The system validates that the data is correct. 4. The system saves the changes. [EditTaxRate]

Use Case 6.4.36 Delete a tax rate

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a tax rate

Main success scenario 1. The store administrator selects the tax rate to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the tax rate 4. The system deletes the tax rate. [DeleteTaxRate]

Extensions 2a.The store administrator doesn’t want to delete the tax rate 2a1. The use case ends

Use Case 6.4.37 Add a tax rule

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a tax rule

Main success scenario 1. The store administrator provides the details of the new.tax rule 2. The system validates that the data is correct. 3. The system saves the new tax rule. [NewTaxRule]

Use Case 6.4.38 Edit a tax rule

196 Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a tax rule

Main success scenario 1. The store administrator selects the tax rate to be edited. 2. The store administrator provides the new details of the tax rule. 3. The system validates that the data is correct. 4. The system saves the changes. [EditTaxRule]

Use Case 6.4.39 Delete a tax rule

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a tax rule

Main success scenario 1. The store administrator selects the tax rule to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the tax rule 4. The system deletes the tax rule. [DeleteTaxRule]

Extensions 2a.The store administrator doesn’t want to delete the tax rule 2a1. The use case ends

197 6.5 Customers

Use Case 6.5.1 Create a Customer

Primary Actor: Customer, Store Administrator Precondition: None Trigger: A customer wants to open an account in the store

Main success scenario 1. The customer or the store administrator provides the required customer data 2. The system validates the customer data 3. The system saves the new account [CreateCustomer]

Extensions 1a.The primary actor is the store administrator 1a1. The store administrator provides also the website where this customer account is associat- ed

Use Case 6.5.2 Change password

Primary Actor: Customer Precondition: The customer is logged in Trigger: A customer wants to change his password

Main success scenario 1. The customer provides the old password 2. The customer provides the new password twice 3. The system validates that the data is correct 4. The system saves the changes [PasswordChange]

Use Case 6.5.3 Change customer details

Primary Actor: Customer Precondition: The customer is logged in Trigger: A customer wants to change its customer details

Main success scenario 1. The customer provides the new customer details 2. The system validates that the data is correct 3. The system saves the changes [EditCustomerDetails]

198 Use Case 6.5.4 Administrate address book

Primary Actor: Customer Precondition: The customer is logged in Trigger: A customer wants to view or change the address book

Main success scenario 1. The system displays the current address book entries of the customer. 2. The customer selects an address book entry to be edited 3. The system validates that the data is correct 4. The system saves the changes and displays the new address book [EditCustomerAddress] The customer repeats steps 1-4 until he is done.

Extensions 2a.The customer doesn’t want tho change the address book 2a1. The use case ends. 2b.The customer wants to add a new address book entry 2b1. The user provides the required data [NewCustomerAddress] 2b2. The use case continues at step 3 2c.The customer wants to delete an address book entry 2c1. The user selects the address book entry [DeleteCustomerAddress] 2c2. The use case continues at step 3 2d.The customer wants to change the default shipping address 2d1. The customer selects the new default shipping address [DefaultDeliveryAddressChange] 2d2. The use case continues at step 3 2e.The customer wants to change the default billing address 2e1. The customer selects the new default billing address [DefaultBillingAddressChange] 2e2. The use case continues at step 3

Use Case 6.5.5 Edit a customer

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a customer

Main success scenario 1. The store administrator selects the customer to be edited. 2. The store administrator provides the new details of the customer. 3. The system validates that the data is correct. 4. The system saves the changes. [EditCustomer]

199 Use Case 6.5.6 Delete a customer

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a customer

Main success scenario 1. The store administrator selects the customer to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the customer 4. The system deletes the customer and his addresses, reviews, notification subscriptions, shop- ping carts, buying activity lists and tags. [DeleteCustomer]

Extensions 2a.The store administrator doesn’t want to delete the customer 2a1. The use case ends

Use Case 6.5.7 Show account information

Primary Actor: Customer Precondition: The customer is logged in Trigger: The customer wants to visualize his account information.

Main success scenario 1. The system shows the customer’s account information. [ShowAccountInformation]

Use Case 6.5.8 Show address book

Primary Actor: Customer Precondition: The customer is logged in Trigger: The customer wants to visualize his address book.

Main success scenario 1. The system shows the customer’s registered addresses. [ShowAddresses]

Use Case 6.5.9 Add a customer group

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a customer group

200 Main success scenario 1. The store administrator provides the details of the new customer group. 2. The system validates that the data is correct. 3. The system saves the new customer group. [NewCustomerGroup]

Use Case 6.5.10 Edit a customer group

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a customer group

Main success scenario 1. The store administrator selects the customer group to be edited. 2. The store administrator provides the new details of the customer group. 3. The system validates that the data is correct. 4. The system saves the changes. [EditCustomerGroup]

Use Case 6.5.11 Delete a customer group

Primary Actor: Store Administrator Precondition: More than three customer groups extist in the system Trigger: The store administrator wants to delete a customer group

Main success scenario 1. The store administrator selects the customer group to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the customer group. 4. The system deletes the customer group. [DeleteCustomerGroup]

Extensions 2a.The store administrator doesn’t want to delete the customer group. 2a1. The use case ends

Use Case 6.5.12 Add an administrator

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add an administrator

Main success scenario 1. The store administrator provides the details of the new administrator, including its role. 2. The system validates that the data is correct.

201 3. The system saves the new administrator. [NewAdministrator]

Use Case 6.5.13 Edit an administrator

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit an administrator

Main success scenario 1. The store administrator selects the administrator to be edited. 2. The store administrator provides the new details of the administrator, including its role. 3. The system validates that the data is correct. 4. The system saves the changes. [EditAdministrator]

Use Case 6.5.14 Delete an administrator

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a administrator

Main success scenario 1. The store administrator selects the administrator to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the administrator. 4. The system deletes the administrator. [DeleteAdministrator]

Extensions 2a.The store administrator doesn’t want to delete the administrator. 2a1. The use case ends 4a.The store administrator wants to delete himself 4a1. The system informs the store administrator that he can’t delete himself. 4a2. The use case ends.

Use Case 6.5.15 Add a role

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a role

Main success scenario 1. The store administrator provides the details of the new role. 2. The system validates that the data is correct. 3. The system saves the new role.

202 [NewRole]

Use Case 6.5.16 Edit a role

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a role

Main success scenario 1. The store administrator selects the role to be edited. 2. The store administrator provides the new details of the role. 3. The system validates that the data is correct. 4. The system saves the changes. [EditRole]

Use Case 6.5.17 Delete a role

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a role

Main success scenario 1. The store administrator selects the role to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the role. 4. The system deletes the role. [DeleteRole]

Extensions 2a.The store administrator doesn’t want to delete the role. 2a1. The use case ends 4a.The store administrator wants to delete the role he has assigned 4a1. The system informs the store administrator that he can’t delete the role he has assigned. 4a2. The use case ends.

Use Case 6.5.18 Log In

Primary Actor: Customer Precondition: The customer is not logged in Trigger: A customer logs in the system

Main success scenario 1. The customer introduces their identification data 2. The system validates the identification data 3. The customer becomes the owner of the current session [LogIn]

203 Extensions 3a. The customer has a shopping cart from a previous session 3a1. The previous shopping cart is restored [RestorePreviousShoppingCart] 3b. The customer has a non-empty compare list, a recently compared products list or a recently viewed products list from a previous session 3b1. The previous compared, recently compared a recently viewed products are restored [RestorePreviousProductLists]

Use Case 6.5.19 Log Out

Primary Actor: Customer Precondition: The customer is logged in Trigger: A customer logs out from the system

Main success scenario 1. The current session becomes anonymous [LogOut]

Extensions 1a. The customer has a non empty compare list, recently compared products list or recently viewed products list. 1a1. The compared, recently compared and recently viewed products are saved [SaveCurrentProductLists]

Use Case 6.5.20 Open session

Primary Actor: Customer Precondition: None Trigger: A customer starts using the system

Main success scenario 1. The system creates an anonymous session [NewSession]

Use Case 6.5.21 Finish session

Primary Actor: Customer Precondition: None Trigger: A customer finishes using the system

Main success scenario 1. The system deletes the current session [DeleteSession]

204 Extensions 1a.The customer is logged in and the session has a non empty shopping cart. 1b. The customer has a non empty compare list, recently compared products list or recently viewed products list. 1b1. The compared, recently compared and recently viewed products are saved [SaveCurrentProductLists]

Use Case 6.5.22 Change the current Website

Primary Actor: Customer Precondition: None Trigger: A customer wants to change the current website of the session

Main success scenario 1. The customer selects the website which will become the current website 2. The system updates the current website [SetCurrentWebsite]

Use Case 6.5.23 Change the current Store

Primary Actor: Customer Precondition: None Trigger: A customer wants to change the current store of the session

Main success scenario 1. The customer selects the store which will become the current store 2. The system updates the current store [SetCurrentStore]

Use Case 6.5.24 Change the current Store View

Primary Actor: Customer Precondition: None Trigger: A customer wants to change the current store view of the session

Main success scenario 1. The customer selects the store view which will become the current store view 2. The system updates the current store view [SetCurrentStoreView]

Use Case 6.5.25 Change the current Currency

Primary Actor: Customer

205 Precondition: None Trigger: A customer wants to change the current currency of the session

Main success scenario 1. The customer selects the currency which will become the current currency 2. The system updates the current currency [SetCurrentCurrency]

206 6.6 Store Administration

Use Case 6.6.1 Add a product

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a product to the store catalog

Main success scenario 1. The store administrator provides the product type and the attribute set associated. 2. The system displays the attributes that can be given a value for this product, including product properties and corresponding simple attributes [ShowProductPropertiesAndAbleToRateAttributes] 3. The store administrator provides the details of the new product. 4. The system validates that the data is correct. 5. The system saves the new product. [NewProduct]

Extensions 3a.The system administrator wants to redefine some details for a restricted scope 3a1. The system displays the available scopes 3a2. The system administrator selects a scope 3a3. The use case continues at step 2. 5a.The store administrator wants to continue editing the product properties 5a1. The use case continues at step 2.

Use Case 6.6.2 Edit a product

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a product

Main success scenario 1. The store administrator selects the product to be edited. 2. The store administrator provides the new details of the product. 3. The system validates that the data is correct. 4. The system saves the changes. [EditProduct]

Extensions 2a.The system administrator wants to redefine some details for a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2

207 Use Case 6.6.3 Delete a product

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a product

Main success scenario 1. The store administrator selects the product to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the product 4. The system deletes the product and the values associated to its attributes, if they are not associated to any other product. [DeleteProduct]

Extensions 2a.The store administrator doesn’t want to delete the product 2a1. The use case ends

Use Case 6.6.4 Add a product category

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a category

Main success scenario 1. The store administrator provides the details of the new category, including its parent category, if any: 2. The system validates that the data is correct. 3. The system saves the new category. [NewCategory]

Extensions 1a.The store administrator wants to redefine some details for a restricted scope 1a1. The system displays the available scopes 1a2. The store administrator selects a scope 1a3. The use case continues at step 1

Use Case 6.6.5 Edit a product category

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a category

Main success scenario 1. The store administrator selects the category to be edited. 2. The store administrator provides the new details of the category.

208 3. The system validates that the data is correct. 4. The system saves the changes. [EditCategory]

Extensions 2a.The store administrator wants to redefine some details for a restricted scope 2a1. The system displays the available store views 2a2. The store administrator selects a store view 2a3. The store administrator selects the category to be edited, within the available categories in that store view 2a4. The use case continues at step 1

Use Case 6.6.6 Move a product category

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to change the placement of a category in the category hierarchy

Main success scenario 1. The store administrator selects the category to be moved. 2. The store administrator indicates the new parent category, if any. 3. The system validates that the data is correct. 4. The system saves the new placement. [MoveCategory]

Use Case 6.6.7 Delete a product category

Primary Actor: Store Administrator Precondition: The selected category is not the root category of any store. Trigger: The store administrator wants to delete a category

Main success scenario 1. The store administrator selects the category to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the category 4. The system deletes the category and all its subcategories. Products of those categories do not belong to them no more. [DeleteCategory]

Extensions 2a.The store administrator doesn’t want to delete the category 2a1. The use case ends

Use Case 6.6.8 Add an attribute

209 Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add an attribute

Main success scenario 1. The store administrator provides the attribute type. 2. The system displays the properties that can be defined for this product [ShowAttributeProperties] 3. The store administrator provides the details of the new attribute 4. The system validates that the data is correct. 5. The system saves the new attribute. [NewAttribute]

Use Case 6.6.9 Edit an attribute

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit an attribute

Main success scenario 1. The store administrator selects the attribute to be edited. 2. The store administrator provides the new details of the attribute. 3. The system validates that the data is correct. 4. The system saves the changes. [EditAttribute]

Use Case 6.6.10 Delete an attribute

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete an attribute

Main success scenario 1. The store administrator selects the attribute to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the attribute 4. The system deletes the attribute. [DeleteAttribute]

Extensions 2a.The store administrator doesn’t want to delete the attribute 2a1. The use case ends

Use Case 6.6.11 Add an attribute set

210 Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add an attribute set

Main success scenario 1. The store administrator provides the details of the new attribute set. 2. The system validates that the data is correct. 3. The system saves the new attribute set. [NewAttributeSet]

Use Case 6.6.12 Edit an attribute set

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit an attribute set

Main success scenario 1. The store administrator selects the attribute set to be edited. 2. The system shows the attributes associated to the attribute set 3. The store administrator provides the new details of the attribute set. 4. The system validates that the data is correct. 5. The system saves the changes. [EditAttributeSet]

Extensions 3a.The store administrator wants to remove an attribute from the attribute set 3a1. The store administrator selects the attribute to be removed. 3a2. The use case continues at step 2 3b.The store administrator wants to add an attribute to the attribute set 3b1. The store administrator selects the attribute to be added. 3b2. The use case continues at step 2

Use Case 6.6.13 Delete an attribute set

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete an attribute set

Main success scenario 1. The store administrator selects the attribute set to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the attribute set. 4. The system deletes the attribute set. [DeleteAttributeSet]

Extensions 2a.The store administrator doesn’t want to delete the attribute set.

211 2a1. The use case ends

Use Case 6.6.14 Administrate tier prices of a product

Primary Actor: Store Administrator Precondition: At least one product is created in the system Trigger: A customer wants to view or change the tier prices of a product

Main success scenario 1. The store administrator selects a product 2. The system displays the current tier prices of the selected product. 3. The store administrator selects a tier price to be edited 4. The system validates that the data is correct 5. The system saves the changes and displays the new product tier prices [EditTierPrice] The customer repeats steps 2-5 until he is done.

Extensions 3a.The customer doesn’t want tho change the tier price 3a1. The use case ends. 3b.The customer wants to add a new tier price 3b1. The user provides the required data [NewTierPrice] 3b2. The use case continues at step 4 3c.The customer wants to delete a tier price 3c1. The user selects the tier price [DeleteTierPrice] 3c2. The use case continues at step 4

Use Case 6.6.15 Add a catalog price rule

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a catalog price rule

Main success scenario 1. The store administrator provides the details of the new catalog price rule and its condition. 2. The system validates that the data is correct. 3. The system saves the new catalog price rule. [NewCatalogPriceRule]

Extensions 3a.The store administrator wants to apply now the rule to the product prices 3a1. The system saves the new catalog price rule [NewCatalogPriceRule] 3a2. The system applies the new rule to applicable products in the catalog [ApplyCatalogPriceRule] 3a3. The use case ends.

212 Use Case 6.6.16 Edit a catalog price rule

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a catalog price rule

Main success scenario 1. The store administrator selects the catalog price rule to be edited. 2. The store administrator provides the new details of the catalog price rule and its condition. 3. The system validates that the data is correct. 4. The system saves the changes. [EditCatalogPriceRule]

Extensions 4a.The store administrator wants to apply now the rule to the product prices 4a1. The system saves the changes [EditCatalogPriceRule] 4a2. The system applies the new rule to applicable products in the catalog [ApplyCatalogPriceRule] 4a3. The use case ends.

Use Case 6.6.17 Delete a catalog price rule

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a catalog price rule

Main success scenario 1. The store administrator selects the catalog price rule to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the catalog price rule. 4. The system deletes the catalog price rule and its condition. [DeleteCatalogPriceRule]

Extensions 2a.The store administrator doesn’t want to delete the catalog price rule. 2a1. The use case ends

Use Case 6.6.18 Add a shopping cart price rule

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a shopping cart price rule

Main success scenario

213 1. The store administrator provides the details of the new shopping cart price rule, including its labels and its condition. 2. The system validates that the data is correct. 3. The system saves the new shopping cart price rule. [NewShoppingCartPriceRule]

Use Case 6.6.19 Edit a shopping cart price rule

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a shopping cart price rule

Main success scenario 1. The store administrator selects the shopping cart price rule to be edited. 2. The store administrator provides the new details of the shopping cart price rule, including its labels and its condition. 3. The system validates that the data is correct. 4. The system saves the changes. [EditShoppingCartPriceRule]

Use Case 6.6.20 Delete a shopping cart price rule

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a shopping cart price rule

Main success scenario 1. The store administrator selects the shopping cart price rule to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the shopping cart price rule. 4. The system deletes the shopping cart price rule and its condition. [DeleteShoppingCartPriceRule]

Extensions 2a.The store administrator doesn’t want to delete the shopping cart price rule. 2a1. The use case ends

214 6.7 Additional Activities

Use Case 6.7.1 Tag a product

Primary Actor: Customer Precondition: The customer is logged in.The product is enabled in the current website. Trigger: A customer wants to add a tag to a product

Main success scenario 1. The customer selects the product. 2. The customer gives the tag name. 3. The system saves the association of this tag to a product by the customer in the current store view

Use Case 6.7.2 Add a tag

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a tag

Main success scenario 1. The store administrator provides the details of the new tag. 2. The system validates that the data is correct. 3. The system saves the new tag. [NewTag]

Extensions 1a.The system administrator wants to change shipping method values only for a restricted scope 1a1. The system displays the available scopes 1a2. The system administrator selects a scope 1a3. The use case continues at step 1

Use Case 6.7.3 Edit a tag

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a tag

Main success scenario 1. The store administrator selects the tag to be edited. 2. The store administrator provides the new details of the tag, including the products tagged by administrators with this tag. 3. The system validates that the data is correct. 4. The system saves the changes.

215 [EditTag]

Extensions 2a.The system administrator wants to change shipping method values only for a restricted scope 2a1. The system displays the available scopes 2a2. The system administrator selects a scope 2a3. The use case continues at step 2

Use Case 6.7.4 Delete a tag

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a tag

Main success scenario 1. The store administrator selects the tag to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the tag. 4. The system deletes the tag. [DeleteTag]

Extensions 2a.The store administrator doesn’t want to delete the tag. 2a1. The use case ends

Use Case 6.7.5 Show tags of a customer

Primary Actor: Customer Precondition: The customer is logged in Trigger: The Customer wants to visualize the tags that he has added

Main success scenario 1. The system shows the tags added by the customer [ShowTagsOfCustomer]

Use Case 6.7.6 Show most popular tags

Primary Actor: Customer Precondition: None Trigger: The Customer enters to the system

Main success scenario 1. The system shows the most popular tags in the current store view, stressing those who are the most used. [ShowPopularTags]

216 Use Case 6.7.7 Show all tags

Primary Actor: Customer Precondition: None Trigger: The Customer enters to the system

Main success scenario 1. The system shows all the tags in the current store view, stressing those who are the most used. [ShowAllTags]

Use Case 6.7.8 Add a review

Primary Actor: Customer, Store Administrator Precondition: The product is enabled in the current website. Trigger: A customer or store administrator wants to write a review of a product

Main success scenario 1. The customer or store administrator selects a product 2. The customer or store administrator provides the content of review and a score for each property [NewReview] 3. The system validates that the data is correct 4. The system saves the review

Extensions 2a.The primary actor is an Store Administrator 2a1. The store administrator provides also the initial review status and the store views where it is visible [NewAdministratorReview] 2a2. The use case continues at step 3.

Use Case 6.7.9 Edit a review

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a review

Main success scenario 1. The store administrator selects the review to be edited. 2. The store administrator provides the new details of the review, including its status and rating scores. 3. The system validates that the data is correct. 4. The system saves the changes. [EditReview]

217 Use Case 6.7.10 Delete a review

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a review

Main success scenario 1. The store administrator selects the review to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the review. 4. The system deletes the review. [DeleteReview]

Extensions 2a.The store administrator doesn’t want to delete the review. 2a1. The use case ends

Use Case 6.7.11 Add a review property

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a property

Main success scenario 1. The store administrator provides the details of the new property. 2. The system validates that the data is correct. 3. The system saves the new property. [NewProperty]

Use Case 6.7.12 Edit a review property

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a property

Main success scenario 1. The store administrator selects the property to be edited. 2. The store administrator provides the new details of the property. 3. The system validates that the data is correct. 4. The system saves the changes. [EditProperty]

Use Case 6.7.13 Delete a review property

218 Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a property

Main success scenario 1. The store administrator selects the property to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the property. 4. The system deletes the property. [DeleteProperty]

Extensions 2a.The store administrator doesn’t want to delete the property. 2a1. The use case ends

Use Case 6.7.14 Show reviews of a product

Primary Actor: Customer, Store Administrator Precondition: The customer is logged in Trigger: The Customer or the Store Administrator wants to visualize the reviews of a product

Main success scenario 1. The store administrator or the customer selects a product. 2. The system shows the reviews of the selected product. [ShowReviewsOfProduct]

Use Case 6.7.15 Show reviews of a customer

Primary Actor: Customer Precondition: The customer is logged in Trigger: The Customer wants to visualize the reviews that he has made

Main success scenario 1. The system shows the reviews written by the customer [ShowReviewsOfCustomer]

Use Case 6.7.16 Add a product to the wish list

Primary Actor: Customer Precondition: The product is enabled in the current website. Trigger: A customer wants to add a product to the wish list

Main success scenario 1. The customer selects a product 2. The system adds this product to the wish list

219 [AddProductToWishlist] 3. The system shows the costumer’s wish list.

Extensions 1a.The product is already in the wish list 1a1. The use case continues at step 3 3a.The customer wants to add or edit a comment to a product in the wish list. 3a1. The customer provides the comment text. 3a2. The system saves the comment. [AddCommentToWishlistItem] 3b.The customer wants to remove a comment from a product in the wish list. 3b1. The system deletes the comment. [RemoveCommentFromWishlistItem]

Use Case 6.7.17 Remove a product from the wish list

Primary Actor: Customer Precondition: The customer’s wish list contains one product at least. Trigger: A customer wants to remove a product from the wish list

Main success scenario 1. The customer selects a product from the wish list 2. The system removes this product from the wish list [RemoveProductFromWishlist]

Use Case 6.7.18 Add a product to the compare list

Primary Actor: Customer Precondition: The product is enabled in the current website Trigger: A customer wants to add a product to the compare list

Main success scenario 1. The customer selects a product 2. The system adds this product to the compare list [AddProductToCompareList]

Extensions 1a.The product is already in the compare list 1a1. End of the use case.

Use Case 6.7.19 Remove a product from the compare list

Primary Actor: Customer Precondition: The customer’s compare list contains one product at least. Trigger: A customer wants to remove a product from the compare list

220 Main success scenario 1. The customer selects a product from the compare list 2. The system removes this product from the compare list [RemoveProductFromCompareList]

Use Case 6.7.20 Share wish list

Primary Actor: Customer Precondition: The wish list has at least one product Trigger: A customer wants to share its wish list information

Main success scenario 1. The customer provides multiple email addresses and a message. 2. The system sends an email to each address with the message and the customer’s wish list information.

Use Case 6.7.21 Subscribe to a product price alert

Primary Actor: Customer Precondition: The customer is logged in. Product alerts are enabled for the current website.The product is enabled in the current website. Trigger: A customer wants to subscribe himself to a product price alert

Main success scenario 1. The customer selects the product 2. The system signs up the customer for the price alerts of that product. [SingUpForPriceAlert]

Use Case 6.7.22 Subscribe to a product stock alert

Primary Actor: Customer Precondition: The customer is logged in. Product alerts are enabled for the current website and the product is out of stock.The product is enabled in the current website. Trigger: A customer wants to subscribe himself to a product stock alert

Main success scenario 1. The customer selects the product 2. The system signs up the customer for the stock alerts of that product. [SingUpForStockAlert]

Use Case 6.7.23 Show wish list

Primary Actor: Customer

221 Precondition: The customer is logged in Trigger: The customer wants to visualize the products in his wish list.

Main success scenario 1. The system shows the products in the customer’s wish list. [ShowWishList]

Use Case 6.7.24 Show ready to compare products

Primary Actor: Customer Precondition: None Trigger: The customer wants to visualize the products in his ready to compare list.

Main success scenario 1. The system shows the products in the customer’s ready to compare list. [ShowReadyToCompareProducts]

Use Case 6.7.25 Show recently compared products

Primary Actor: Customer Precondition: None Trigger: The customer wants to visualize the products on his recently compared list.

Main success scenario 1. The system shows the products in the customer’s recently compared list. [ShowRecentlyComparedProducts]

Use Case 6.7.26 Show recently viewed products

Primary Actor: Customer Precondition: None Trigger: The customer wants to visualize the products on his recently viewed list.

Main success scenario 1. The system shows the products in the customer’s recently viewed list. [ShowRecentlyViewedProducts]

Use Case 6.7.27 Show compare

Primary Actor: Customer Precondition: There is at least one product in the customer’s ready to compare list Trigger: The customer wants to visualize the comparable attributes of products in his ready to compare list.

222 Main success scenario 1. The system shows the comparable attributes of products in the customer’s ready to compare list. [ShowCompare]

Use Case 6.7.28 Add a newsletter template

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a newsletter template

Main success scenario 1. The store administrator provides the details of the new newsletter template. 2. The system validates that the data is correct. 3. The system saves the new newsletter template. [NewNewsletterTemplate]

Extensions 2a.The store administrator wants to see a preview of the newsletter template. 2a1. The system displays a preview of the newsletter template. 2a2. The use case continues at step 2

Use Case 6.7.29 Edit a newsletter template

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a newsletter template

Main success scenario 1. The store administrator selects the newsletter template to be edited. 2. The store administrator provides the new details of the newsletter template. 3. The system validates that the data is correct. 4. The system saves the changes. [EditNewsletterTemplate]

Use Case 6.7.30 Delete a newsletter template

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a newsletter template

Main success scenario 1. The store administrator selects the newsletter template to be deleted. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to delete the newsletter template

223 4. The system deletes the newsletter template and all the newsletters created with this template. [DeleteNewsletterTemplate]

Extensions 2a.The store administrator doesn’t want to delete the newsletter template 2a1. The use case ends

Use Case 6.7.31 Add a newsletter

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add a newsletter

Main success scenario 1. The store administrator selects the newsletter template that will be used to create this template. 2. The store administrator provides the details of the new newsletter, including optionally redefi- nitions of the details taken from the newsletter template. 3. The system validates that the data is correct. 4. The system saves the new newsletter. [NewNewsletter]

Use Case 6.7.32 Edit a newsletter

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to edit a newsletter

Main success scenario 1. The store administrator selects the newsletter to be edited. 2. The store administrator provides the new details of the newsletter. 3. The system validates that the data is correct. 4. The system saves the changes. [EditNewsletter]

Use Case 6.7.33 Subscribe to the newsletter

Primary Actor: Customer Precondition: None Trigger: A customer wants to subscribe himself to the newsletter

Main success scenario 1. The customer provides an email 2. The system subscribes the costumer to the newsletter of the current store view. [SignUpForNewsletter]

Extensions

224 1a.The customer does not provide an email, but it is a registered customer. 1a1. The use case continues at step 2 1b.The customer does not provide an email and it is not a registered customer. 1b1. The system informs the customer that providing an email is mandatory. 1b2. The use case continues at step 1

Use Case 6.7.34 Unsubscribe from the newsletter

Primary Actor: Customer Precondition: The customer is not subscribed to the newsletter in the current store view Trigger: A customer wants to unsubscribe himself from the newsletter

Main success scenario 1. The system unsubscribes the costumer from the newsletter in this store view. [SignDownFromNewsletter]

Use Case 6.7.35 Delete a newsletter subscription

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to delete a newsletter subscription.

Main success scenario 1. The store administrator selects the subscription to delete. 2. The system deletes the newsletter subscription [DeleteNewsletterSubscription]

Use Case 6.7.36 Unsubscribe a newsletter subscription

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to unsubscribe a newsletter subscription.

Main success scenario 1. The store administrator selects the subscription to delete. 2. The system changes the newsletter subscription status to disabled. [UnsubscribeNewsletterSubscription]

Use Case 6.7.37 Show shopping cart

Primary Actor: Customer Precondition: None Trigger: The customer wants to visualize the products in his shopping cart.

225 Main success scenario 1. The system shows the products in the costumer’s shopping cart and the quantity chosen for each item [ShowShoppingCart]

Use Case 6.7.38 Download a product

Primary Actor: Customer Precondition: The customer is logged in. The customer purchased the product. Download is enabled by the system. The download is not expired and the number of download has not been exceeded. Trigger: A customer wants to download an item of a downloadable product that he has pur- chased.

Main success scenario 1. The customer selects the item to be downloaded [ProductDownload] 2. The system allows the customer downloading the downloadable item

Use Case 6.7.39 Show downloadable products of a customer

Primary Actor: Customer Precondition: None Trigger: The Customer wants to visualize the downloadable products that he has already pur- chased.

Main success scenario 1. The system shows the downloadable products purchased by the Customer. [ShowDownloadableProductsOfCustomer]

Use Case 6.7.40 Tell to a friend

Primary Actor: Customer Precondition: Tell to a friend is enabled by the system. If the customer is not logged in, tell to a friend is also enabled for guests. The number of maximum sents allowed has not been exceeded in the current hour. Trigger: A customer wants to send information about a product to a friend with a comment by email

Main success scenario 1. The customer selects the product to be sent 2. The customer provides his name, his friend’s name, his email, his friend’s email and the message about the product. 3. The system saves that the customer has used the "tell to a friend"feature. [TellToAFriendUsed]

226 4. The system sends the email with a link to the current product information.

Use Case 6.7.41 View product information

Primary Actor: Customer Precondition: The product is enabled in the current website. Trigger: A customer wants to see detailed information of a product

Main success scenario 1. The customer selects a product 2. The system adds the product to the customer’s recently viewed products list [ProductViewed] 3. The system shows the detailled information about this product [ShowProductInfo]

Use Case 6.7.42 Search products

Primary Actor: Customer Precondition: None Trigger: A customer wants to search products by one or more keywords

Main success scenario 1. The customer enters one or more keywords to search 2. The system shows the products that fit the keywords. [ShowFoundProducts]

Use Case 6.7.43 Show the best purchased products

Primary Actor: Customer Precondition: None Trigger: The Customer enters to the system

Main success scenario 1. The system shows the products in stock that have been purchased more times by the cus- tomers, ordered by the number of times purchased. [ShowBestPurchasedProducts]

Use Case 6.7.44 Reset the temporal information

Primary Actor: System Precondition: None Trigger: An hour has passed since the last time the use case has been applied.

Main success scenario

227 1. The system resets to its initial values all the temporal information that should be erased after an hour. [ResetTemporalInfo]

228 6.8 Online catalog

Use Case 6.8.1 Place an order

Primary Actor: Customer Precondition: None Trigger: A customer wants to place and order.

Main success scenario 1. At any time before step 11 the customer, may log in: [LogIn] 2. The system adds the contents of the anonymous shopping cart to the customer shopping cart. 3. The system displays the contents of the shopping cart. 4. The system calculates the applicable discounts to this shopping cart 5. The customer browses the product catalog [ShowProductInfo] [ProductViewed] 6. The customer selects a product to buy [AddProductToShoppingCart] 7. The system displays the contents of the shopping cart 8. The customer changes the contents of the shopping cart [UpdateShoppingCart] 9. The system updates the shopping cart 10. The system displays the contents of the updated shopping cart. The customer repeats steps from 3 to 1 as necessary to build his order. 11. The customer checks out the order 12. The system shows the default customer’s shipping address. 13. The customer accepts the shipping address. 14. The system shows the default customer’s billing address. 15. The customer accepts the billing address. 16. The system shows the available shipping methods with their cost 17. The customer selects the preferred shipping method 18. The system shows the available payment methods 19. The customer selects the preferred payment method 20. The system displays a summary of the order. 21. The customer confirms the order [OrderConfirmation] 22. The system saves the order 23. The system sends an email to the customer with the information about the order.

Extensions 1a.The customer is new 1a1. 5a.The customer wants to add a product from his wish list 5a1. The system displays the contents of the wish list 5a2. The use case continues at step 6 5b.The customer wants to add a product from his ready to compare list 5b1. The system displays the contents of the ready to compare list 5b2. The use case continues at step 6

229 5c.The customer wants to add a product from his recently viewed products list 5c1. The system displays the contents of the recently viewed products list 5c2. The use case continues at step 6 5d.The customer wants to add a product from his recently compared products list 5d1. The system displays the contents of the recently compared products list 5d2. The use case continues at step 6 8a.The customer wants to use a coupon code 8a1. The customer provides the coupon code. 8a2. The system checks that the coupon code is applicable. 8a3. The system applies the corresponding discount. [ApplyCouponCode] 8a4. The use case continues at step 9. 8a2a.The coupon code is not applicable for this shopping cart 8a2a1. The use case continues at step 9. 8b.The customer wants to estimate the shipping and tax costs of the products in the shopping cart 8b1. The customer provides the Country, Zone or Postal Area where it is located. 8b2. The system shows the tax amount associated and adds it to the total order cost 8b3. The system shows the available shipping methods with their cost. 8b4. The customer selects the preferred shipping method 8b5. The system shows the estimated shipping and order’s total cost. [ShowShippingCost] 11a.The customer wants to Checkout with multiple Adresses 11a1. 11b.The customer is not yet logged in 11b1. The system asks the customer to log in himself 11b2. The customer logs in. [LogIn] 11b3. The use case continues at step 12 11b1a.The customer wants to check out the order without logging in (as guest) and the configu- ration allows it. 11b1a1. The customer checks out without logging in. 11b1a2. The use case continues at step 12 12a, 20b.The customer wants to change the shipping address. 12a1. The system shows the know adresses of the customer. 12a2. The customer selects a different shipping addres. 12a3. The system shows the selected address. 12a4. The customer continues with the checkout procedure at step 13 12a2a, 14a2a.The customer wants to define a new address 12a2a1. The customer gives the new address: [NewCustomerAddress] 12a2a2. The system saves the adress. 12a2a3. The system shows the new address. 12a2a4. The customer continues with the checkout procedure at step 13 12b.All products in the shopping car are virtual or downloadable 12b1. The customer continues with the checkout procedure at step 13 13a.The customer wants to choose the same billing and shipping address 13a1. The customer continues with the checkout procedure at step 16 14a, 20b.The customer wants to change the billing address. 14a1. The system shows the know adresses of the customer. 14a2. The customer selects a different billing addres. 14a3. The system shows the selected address 14a4. The customer continues with the checkout procedure at step 16 16a.The customer wants to add a gift message

230 16a1. The customer enters the gift message information 16a2. The system saves the gift message [AddGiftMessage] 16a3. The customer continues with the checkout procedure at step 17 20a.The customer wants to change the contents of the shopping cart 20a1. The customer continues with the checkout procedure at step 3

Use Case 6.8.2 Checkout an order with multiple addresses

Primary Actor: Customer Precondition: The customer’s shopping cart is not empty Trigger: A customer wants to ship the order’s products to multiple adresses

Main success scenario 1. The customer selects the quantity of each product that will be shipped to each address. 2. The system shows the available shipping methods. 3. The customer chooses a shipping method for each address. 4. The system shows the available payment methods and the default billing address 5. The customer chooses a payment method for all addresses 6. The system displays a summary of the order. 7. The customer confirms the order [OrderConfirmationWithMultipleCheckout] 8. The system saves an order for each shipping address. 9. For each shipping address, the system sends an email to the customer with the information about the order.

Extensions 1a, 4a2a.The customer wants to define a new address 1a1. The customer gives the new address: [NewCustomerAddress] 1a2. The system saves the adress. 1a3. The system shows the new address. 1a4. The customer continues with the checkout procedure at step 1 2a.The customer wants to add a gift message to any of the addresses 2a1. The customer enters the gift message information 2a2. The system saves the gift message 2a3. The customer continues with the checkout procedure at step 2 4a.The customer wants to change the billing address. 4a1. The system shows all the customer’s addresses 4a2. The customer chooses an address. 4a3. The customer returns to step 4 4a2a.The customer wants to edit an Address 4a2a1. 4a2a2. The customer returns to step 4 6a.The customer wants to change the quantity of each product that will be shipped to each address 6a1. The customer returns to step 1 6b.The customer wants to change one or more shipping methods 6b1. The customer returns to step 2 6c.The customer wants to change the billing address.

231 6c1. The customer returns to step 4 6d.The customer wants to change the payment method. 6d1. The customer returns to step 4

Use Case 6.8.3 Add an order

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to add an order for a registered customer

Main success scenario 1. The store administrator selects the customer. 2. The store administrator selects the website where the order will be placed 3. The system creates the administration shopping cart that will be used to choose the purchased products [NewAdministrationShoppingCart] 4. The system shows the order settings (currency the order will be placed with, customer group, customer email and shipping and billing addresses) and the customer’s buying activity lists. [ShowPredefinedOrderSettings] [ShowShoppingCart] [ShowWishList] [ShowReadyToCompareProducts] [ShowRecentlyComparedProducts] [ShowRecentlyViewedProducts] 5. The system shows the available payment methods and its associated cost 6. The store administrator selects a payment method 7. The system shows the available shipping methods and its associated cost 8. The store administrator selects a shipping method The store administrator repeats steps 4 - 8 until he is done. 9. The store administrator confirms the current order settings 10. The system saves the order [OrderConfirmation] 11. The system deletes the administration shopping cart that has been used. [DeleteAdministrationShoppingCart]

Extensions 1a.The store administrator wants to add an order for a customer that doesn’t have an account in the system yet. 1a1. 2a.The customer’s account is not visible in the selected website 2a1. The system creates an account for the registered customer in the selected website [DuplicateAccount] 4a.The store administrator wants to add products to the order 4a1. The system shows all the available products 4a2. The store administrator selects the products he want to purchase, and defines its quantity. 4a3. The system updates the selected products to be purchased [AddProductToShoppingCart] 4a4. The use case continues at step 4 4b.The store administrator wants to change the currency the order will be placed with 4b1. The system displays the available currencies

232 4b2. The store administrator selects a currency 4b3. The system changes the order’s currency [ChangeCurrencyOfAdministrationShoppingCart] 4b4. The use case continues at step 4 4c.The store administrator wants to change the address details 4c1. The store administrator provides the new address details. 4c2. The system changes the new address details of the order. 4c3. The use case continues at step 4 4c1a.The store administrator wants to use a saved customer’s address 4c1a1. The system shows the customer’s addresses 4c1a2. The store administrator selects an address 4c1a3. The system changes the new address details of the order. 4c1a4. The use case continues at step 4 4d.The store administrator wants to change the customer’s group and email 4d1. The store administrator provides the new customer’s group and email. 4d2. The system updates the changes. [ChangeEMailAndGroupOfAdministrationShoppingCart] 4d3. The use case continues at step 4 4e.The store administrator wants to apply a shopping cart rule that works with coupon code 4e1. The store administrator provides the coupon code. 4e2. The system applies the shopping cart rule. [ApplyCouponCode] 4e3. The use case continues at step 4 4e2a.The coupon code does not match with any applicable rule 4e2a1. The system informs the store administrator that coupon code does not match 4e2a2. The use case continues at step 4 4f.The store administrator wants to add to the order a product that appears in the customer’s buying activity lists. 4f1. The store administrator selects the product 4f2. The system updates the changes [AddProductToShoppingCart] 4f3. The use case continues at step 4 4g.The store administrator wants to add a gift message for an item or the entire order 4g1. The store administrator selects an item or the whole order. 4g2. The store administrator provides the gift messsage’s details. 4g3. The system saves the changes. [AddGiftMessage] [AddGiftMessageToItem] 4g4. The use case continues at step 4 4h.The store administrator wants to add a comment to the order 4h1. The store administrator provides the comment’s details. 4h2. The system saves the changes. 4h3. The use case continues at step 4 10a.The order has no products added, no payment or shipping method selected or missing ad- dress and customer information. 10a1. The system shows to the customer the information that is missing. 10a2. The use case continues at step 4 10b.The store administrator wants to save the billing or shipping address in the address book 10b1. The system saves the new address [NewCustomerAddress] 10b2. The system saves the order [OrderConfirmation]

233 Use Case 6.8.4 Cancel an order

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to cancel an order

Main success scenario 1. The store administrator selects the order to be cancelled. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to cancel the order. 4. The system sets the order status to cancelled. [CancelOrder]

Extensions 2a.The store administrator doesn’t want to cancel the order. 2a1. The use case ends

Use Case 6.8.5 Hold an order

Primary Actor: Store Administrator Precondition: The order is not already hold. Trigger: The store administrator wants to put an order on hold.

Main success scenario 1. The store administrator selects the order to be on hold. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to put the order on hold. 4. The system sets the order status to hold. [HoldOrder]

Extensions 2a.The store administrator doesn’t want to puth the order on hold. 2a1. The use case ends

Use Case 6.8.6 Unhold an order

Primary Actor: Store Administrator Precondition: The order is not already unhold. Trigger: The store administrator wants to release an order from holding status.

Main success scenario 1. The store administrator selects the order to be released from holding status. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to release an order from holding status. 4. The system sets the order status to its corresponding status. [UnholdOrder]

234 Extensions 2a.The store administrator doesn’t want to release an order from holding status. 2a1. The use case ends

Use Case 6.8.7 Reorder

Primary Actor: Customer, Store Administrator Precondition: An order is already placed by the customer Trigger: A customer or the store administrator wants to reorder the items purchased in a previous order.

Main success scenario 1. The customer or store administrator selects the order to be reordered. 2. The system adds to the customer shopping cart all the products in the selected order [Reorder] 3. The customer starts a checkout procedure

Extensions 2a.The primary actor is the store administrator 2a1. The use case continues at step 3

Use Case 6.8.8 Add a gift message to a placed order

Primary Actor: Store Administrator Precondition: At least one order is placed in the system Trigger: The customer asks the store administrator to add a gift message to an entire order or to a order line.

Main success scenario 1. The store administrator selects the order or the order line. 2. The store administrator provides the information about the gift message. 3. The system saves the gift message. [AddGiftMessageToExistingOrder]

Use Case 6.8.9 Print an Order

Primary Actor: Customer Precondition: The order is placed. Trigger: A customer wants to print a summary of an order.

Main success scenario 1. The customer selects the order to be printed 2. The system allows the customer printing the order summary

235 Use Case 6.8.10 Send order information by email

Primary Actor: Store Administrator Precondition: At least one order is placed in the system Trigger: The store administrator wants to send and email to a customer with the details of an order he has purchased.

Main success scenario 1. The store administrator selects the order. 2. The system asks the store administrator a confirmation 3. The store administrator confirms that he wants to send the email. 4. The system sends an email to the customer with the order details.

Extensions 2a.The store administrator doesn’t want to send the email. 2a1. The use case ends

Use Case 6.8.11 Show previous orders of a customer

Primary Actor: Precondition: The customer is logged in Trigger: The Customer wants to visualize his orders

Main success scenario 1. The system shows the previous orders made by the customer [ShowOrdersOfCustomer]

Use Case 6.8.12 Add an invoice

Primary Actor: Store Administrator Precondition: At least one order is placed in the system, with not all products invoiced. Trigger: The store administrator wants to generate an invoice for a saved order.

Main success scenario 1. The store administrator selects the order 2. The system shows the information about the invoice it is going to create 3. The store administrator confirms he wants to create the invoice. 4. The system creates the invoice [AddInvoice]

Extensions 3a.The store administrator wants to change the quantity invoiced for each product 3a1. The store administrator defines the quantity he wants to invoice for each product. 3a2. The system validates that the data is correct. 3a3. The use case continues at step 2 3b.The store administrator wants to add a comment to the invoice

236 3b1. The store administrator provides the comment 3b2. The use case continues at step 2 4a.The store administrator wants to send an email confirmation to the customer 4a1. The system sends an email confirmation to the customer’s address. 4a2. The system creates the invoice [AddInvoice] 4a3. The use case ends. 4b.The store administrator wants to create a shipment with the same products of the invoice 4b1. The system creates both the shipment and the invoice [AddInvoice] [AddShipment] 4b2. The use case ends. 4b1a.The store administrator wants to add a tracking number to the shipment 4b1a1. The store administrator provides the tracking number details 4b1a2. The use case continues at step 1

Use Case 6.8.13 Register an invoice payment

Primary Actor: Store Administrator Precondition: At least one pending invoice is placed in the system. Trigger: The store administrator wants to register the payment of a saved invoice.

Main success scenario 1. The store administrator selects the invoice to be paid. 2. The system registers that the payment has been made. [PayInvoice]

Use Case 6.8.14 Cancel an invoice

Primary Actor: Store Administrator Precondition: At least one not cancelled invoice is placed in the system. Trigger: The store administrator wants to cancel an invoice.

Main success scenario 1. The store administrator selects the invoice. 2. The system cancels the invoice. [CancelInvoice]

Use Case 6.8.15 Add a shipment

Primary Actor: Store Administrator Precondition: At least one order is placed in the system, with not all products shipped. Trigger: The store administrator wants to generate a refund for a saved order.

Main success scenario 1. The store administrator selects the order

237 2. The system shows the information about the shipment it is going to create 3. The store administrator confirms he wants to create the shipment. 4. The system creates the shipment [AddShipment]

Extensions 3a.The store administrator wants to change the quantity shipped for each product 3a1. The store administrator defines the quantity he wants to ship for each product. 3a2. The system validates that the data is correct. 3a3. The use case continues at step 2 3b.The store administrator wants to add a tracking number to the shipment 3b1. The store administrator provides the tracking number details 3b2. The use case continues at step 2 3c.The store administrator wants to add a comment to the shipment 3c1. The store administrator provides the comment 3c2. The use case continues at step 2 4a.The store administrator wants to send an email confirmation to the customer 4a1. The system sends an email confirmation to the customer’s address. 4a2. The system creates the shipment [AddShipment] 4a3. The use case ends.

Use Case 6.8.16 Add a refund

Primary Actor: Store Administrator Precondition: At least one order is placed in the system, with at least one product invoiced. Trigger: The store administrator wants to generate a refund for a saved order.

Main success scenario 1. The store administrator selects the order 2. The system shows the information about the refund it is going to create 3. The store administrator confirms he wants to create the refund. 4. The system creates the refund [AddRefund]

Extensions 3a.The store administrator wants to change the quantity refunded for each product 3a1. The store administrator defines the quantity he wants to refund for each product. 3a2. The system validates that the data is correct. 3a3. The use case continues at step 2 3b.The store administrator wants to add a comment to the refund 3b1. The store administrator provides the comment 3b2. The use case continues at step 2 4a.The store administrator wants to send an email confirmation to the customer 4a1. The system sends an email confirmation to the customer’s address. 4a2. The system creates the refund [AddRefund] 4a3. The use case ends.

238 Use Case 6.8.17 Cancel a refund

Primary Actor: Store Administrator Precondition: At least one non cancelled refund is placed in the system. Trigger: The store administrator wants to cancel a refund.

Main success scenario 1. The store administrator selects the refund. 2. The system cancels the refund. [CancelRefund]

Use Case 6.8.18 Add a comment to an order, invoice, shipment or refund.

Primary Actor: Store Administrator Precondition: At least one invoice is placed in the system Trigger: The store administrator wants to add a comment to a saved order, invoice, shipment or refund.

Main success scenario 1. The store administrator selects the order, invoice, shipment or refund 2. The system shows the information about the order, invoice, shipment or refund. 3. The store administrator provides the comment 4. The system saves the comment. [AddComment]

Extensions 3a.The store administrator wants to send an email notification to the customer 3a1. The system sends an email confirmation to the customer’s address. 3a2. The use case continues at step 1.

239 6.9 Store Reports

Use Case 6.9.1 Show the sales report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the sales in the system.

Main success scenario 1. The store administrator provides the details to delimit the orders that will appear in the report, including the time period and scope. 2. The system shows the orders that meet the provided details. [ShowSalesReport]

Use Case 6.9.2 Show the tax report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the tax information of the system.

Main success scenario 1. The store administrator provides the details to delimit the tax information that will appear in the report, including the time period and scope. 2. The system shows the tax information that meet the provided details. [ShowTaxInformationReport]

Use Case 6.9.3 Show the shipping report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the shipping information of the system.

Main success scenario 1. The store administrator provides the details to delimit the shipping information that will appear in the report, including the time period and scope. 2. The system shows the shipping information that meet the provided details. [ShowShippingInformationReport]

Use Case 6.9.4 Show the invoices report

240 Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the invoices in the system.

Main success scenario 1. The store administrator provides the details to delimit the invoices that will appear in the report, including the time period and scope. 2. The system shows the invoices that meet the provided details. [ShowInvoicesReport]

Use Case 6.9.5 Show the refunds report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the refunds in the system.

Main success scenario 1. The store administrator provides the details to delimit the refunds that will appear in the report, including the time period and scope. 2. The system shows the refunds that meet the provided details. [ShowRefundsReport]

Use Case 6.9.6 Show the coupon codes report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the coupon codes used in the system.

Main success scenario 1. The store administrator provides the details to delimit the coupon codes that will appear in the report, including the time period and scope. 2. The system shows the coupon codes that meet the provided details and the orders where it have been used. [ShowCouponCodeReport]

Use Case 6.9.7 Show the products in carts report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize the products that appear in any shopping cart in the system.

Main success scenario 1. The system shows the products that appear in any shopping cart in the system.

241 [ShowProductsInCartsReport]

Extensions 1a.The store administrator wants to visualize the information for only a restricted scope 1a1. The system displays the available scopes 1a2. The system administrator selects a scope 1a3. The use case continues at step 1

Use Case 6.9.8 Show the abandoned carts report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize the carts of customers who added products to their shopping cart and then logged out.

Main success scenario 1. The system shows he carts of customers who added products to their shopping cart and then logged out. [ShowAbandonedCartsReport]

Extensions 1a.The store administrator wants to visualize the information for only a restricted scope 1a1. The system displays the available scopes 1a2. The system administrator selects a scope 1a3. The use case continues at step 1

Use Case 6.9.9 Show the bestseller products report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the bestseller products in the system.

Main success scenario 1. The store administrator provides the time period and scope to delimit the besteller products that will appear in the report. 2. The system shows the best seller products in the system. [ShowBestsellersReport]

Use Case 6.9.10 Show the ordered products report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of all the ordered products in the system.

242 Main success scenario 1. The store administrator provides the time period and scope to delimit the besteller products that will appear in the report. 2. The system shows the ordered products products in the system. [ShowOrderedProductsReport]

Use Case 6.9.11 Show the most viewed products report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the most viewed products in the system.

Main success scenario 1. The store administrator provides the time period and scope to delimit the most viewed products that will appear in the report. 2. The system shows the most viewed products in the system. [ShowMostViewedProductsReport]

Use Case 6.9.12 Show the stock of products report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the stock properties of the products in the system.

Main success scenario 1. The store administrator provides the scope to delimit the products that will appear in the report. 2. The system shows the products in the system, ordered by its remaining quantity in stock. [ShowStockOfProductsReport]

Use Case 6.9.13 Show the downloads report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the downloads the customers have made in the system.

Main success scenario 1. The store administrator provides the scope to delimit the downloads that will appear in the report. 2. The system shows the downloads the customers have made in the system. [ShowDownloadsReport]

243 Use Case 6.9.14 Show the new customer accounts report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the customer accounts that have been created in the system.

Main success scenario 1. The store administrator provides the time period and scope to delimit the accounts that will appear in the report. 2. The system shows the customer accounts that have been created in the system.. [ShowNewCustomerAccountsReport]

Use Case 6.9.15 Show the customers by orders total report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the orders that each customer has placed, ordering customers by the total amount of its orders.

Main success scenario 1. The store administrator provides the time period and scope to delimit the orders that will appear in the report. 2. The system shows the orders that each customer has placed, ordering customers by the total amount of its orders [ShowCustomersByOrdersTotalReport]

Use Case 6.9.16 Show the customers by number of orders report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the orders that each customer has placed, ordering customers by the number of orders they have placed.

Main success scenario 1. The store administrator provides the time period and scope to delimit the orders that will appear in the report. 2. The system shows the orders that each customer has placed, ordering customers by the number of orders they have placed [ShowCustomersByNumberOfOrdersReport]

Use Case 6.9.17 Show the customer reviews report

Primary Actor: Store Administrator

244 Precondition: None Trigger: The store administrator wants to visualize a summary of the registered customers that have placed a review.

Main success scenario 1. The system shows the customers that have placed a review and the number of total reviews that they have placed. [ShowCustomerReviewsReport]

Use Case 6.9.18 Show the product reviews report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the products that have received a review.

Main success scenario 1. The system shows the products that have received one or more reviews. [ShowProductsReviewsReport]

Use Case 6.9.19 Show the tags by customer report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the customers who have sub- mitted approved tags.

Main success scenario 1. The system shows the customers who have submitted approved tags, and the number of approved tags they have submitted. [ShowTagsByCustomerReport]

Use Case 6.9.20 Show the tags by product report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of the products for which tags have been submitted.

Main success scenario 1. The system shows the products for which tags have been submitted, and the number of approved tags. [ShowTagsByProductReport]

245 Use Case 6.9.21 Show the popular tags report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of all the approved tags in the system, ordered by the times it have been used.

Main success scenario 1. The store administrator provides the scope to delimit the tags that will appear in the report. 2. The system shows all the approved tags in the system, ordered by the times it have been used. [ShowPopularTagsReport]

Use Case 6.9.22 Show the search terms report

Primary Actor: Store Administrator Precondition: None Trigger: The store administrator wants to visualize a summary of all the search terms used by customers.

Main success scenario 1. The system shows all the search terms used by customers and the number of results that displayed the last time the search term was used. [ShowPopularTagsReport]

246 7 Behavioral Schema. Events Specification

In this chapter we develop the second part of the behavioral schema of the Magento information system: the specification of the Events defined in chapter 6. Section 7.1 defines the scope of the events elicitation, while section 7.2 describes the way the schema is presented. Section 7.3 presents an overview of the described Events, which are detailed in the remaining sections.

7.1 Scope

Event’s specification assumes that a set of events can be implicitly defined without explicitly specifying them. The whole schema and information base is assumed to be visible to the actors that interact with the system. Therefore, we assume that Queries, which only retrieve information from the IB, can be infered by its definition. Secondly, for each entity type three basic Events1are assumed to exist: New, Edit and Delete events. They are known as Structural Events (see [8]). In this chapter, they will be specified only if their effect or initial integrity constraints are relevant. For the remaining events, the following behaviour is assumed:

New: creates a new object as an instance of the class specified in the event’s name. The event class has the same attributes and relationship types as the created object’s class, so its values are copied from one object to another.

Edit: replaces the values of an existing object. As New, event and edited classes have the same attributes and relationship types. A relationship is also defined between the two classes, to identify the existing object whose values will be replaced.

Delete: deletes an object. If other objects are related to it with a multiplicity of 1, it may be deleted too.

Some entities in the schema mantain information about the system’s configuration. In those cases, no “New” or “Delete” Structural Event is defined. They are assumed to have the same behaviour as “Edit” Structural Events, excepting that the event can not replace the relationship

1In permanent entities, no “Delete” Event can be defined. In constant entities, there can’t be “New” or “Delete” Events defined.

247 with an store view or a website (it represents the scope where this configuration is applied). Thirdly, events specification only concerns about Simple Products. Thus, the following events are specified by supposing that the related product is simple:

I NewProduct

I EditProduct

I AddProductToShoppingCart

I UpdateShoppingCart

I OrderConfirmation Other events that concern specifically on Non-Simple Products are listed but not specified. A second iteration on the current project work should extend this scope (see section 12.3).

7.2 Schema presentation

First of all, we describe a complete list of the events, detailing those which are specified or only listed, and the Use Case group they belong to. In the next sections, the following information is given for each specified event:

I Event Specification: following the approach presented in [8], events are modelized as entity types. It’s attributes and relationship types (and those inherited from other events through generalization/specialization) represent the information needed for its execution.

I Initial Integrity Constraints: used as preconditions, they specify in OCL the state of the information base in which the event can be instantiated.

I Effect: all events use an effect operation, whose OCL postconditions define the changes made made in the information base when the event occurs.

Events specification is organized in the same six groups as Use Cases, described in sections 5.3 and 6.2. Event Specification sections define textually the event’s attributes and relationships. We use here an example to show the way they are described. First, we show the way it will be described by a classic UML Diagram.

248 In the following sections, this event would be textually specified as follows: Attributes attribute2 : Type2 [multiplicity4]

Relationships role2 : Class2 [multiplicity2]

Generalizations Attributes inherited from AbstractEvent1 attribute1 : Type1 [multiplicity3] Relationships inherited from AbstractEvent1 role1 : Class1 [multiplicity1]

The correspondance between textual and visual specifications can be easily established. Note that the multiplicity of an event role in all its relationships is assumed to be [*].

7.3 Events Overview

First of all, we present a summary of all the events specified in the use case specification. As some events are not specified (see scope restrictions in section 7.1), the criteria for not specifying them is listed below the event name. The specified events are highlighted in bold.

I Store Configuration

– NewWebsite. This event is a non-specified Structural Event. – EditWebsite. This event is a non-specified Structural Event. – DeleteWebsite. This event is a non-specified Structural Event. – SetDefaultWebsite – NewStore. This event is a non-specified Structural Event. – EditStore. This event is a non-specified Structural Event. – DeleteStore. This event is a non-specified Structural Event. – SetDefaultStoreOfWebsite – NewStoreView. This event is a non-specified Structural Event. – EditStoreView. This event is a non-specified Structural Event. – DeleteStoreView. This event is a non-specified Structural Event. – SetDefaultStoreViewOfStore – SetDefaultLanguage – ChangeCatalogConfiguration. This event changes system configuration parameters. – ChangeStockConfiguration. This event changes system configuration parameters. – ChangeWishListConfiguration. This event changes system configuration parameters. – ChangeSalesConfiguration. This event changes system configuration parameters. – ChangeCustomerConfiguration. This event changes system configuration parameters. – ChangeTaxConfiguration. This event changes system configuration parameters. – ChangeShippingConfiguration. This event changes system configuration parameters. – ChangeTellToAFriendConfiguration. This event changes system configuration parameters. – ChangeCurrencyConfiguration. This event changes system configuration parameters.

249 – ChangeEnabledCurrencies. This event changes system configuration parameters. – UpdateCurrencyValue. This event changes system configuration parameters. – ChangeGeographicalConfiguration. This event changes system configuration parameters. – EditShippingMethods. This event changes system configuration parameters. – EditPaymentMethods. This event changes system configuration parameters. – NewCustomerTaxClass. This event is a non-specified Structural Event. – EditCustomerTaxClass. This event is a non-specified Structural Event. – DeleteCustomerTaxClass. This event is a non-specified Structural Event. – NewProductTaxClass. This event is a non-specified Structural Event. – EditProductTaxClass. This event is a non-specified Structural Event. – DeleteProductTaxClass. This event is a non-specified Structural Event. – NewTaxRate. This event is a non-specified Structural Event. – EditTaxRate. This event is a non-specified Structural Event. – DeleteTaxRate. This event is a non-specified Structural Event. – NewTaxRule. This event is a non-specified Structural Event. – EditTaxRule. This event is a non-specified Structural Event. – DeleteTaxRule. This event is a non-specified Structural Event.

I Customers

– CreateCustomer – PasswordChange – EditCustomerDetails – NewCustomerAddress – DeleteCustomerAddress – DefaultDeliveryAddressChange – DefaultBillingAddressChange – EditCustomerAddress – EditCustomer. This event is a non-specified Structural Event. – DeleteCustomer. This event is a non-specified Structural Event. – ShowAccountInformation. This event is a query. – ShowAddresses. This event is a query. – NewCustomerGroup. This event is a non-specified Structural Event. – EditCustomerGroup. This event is a non-specified Structural Event. – DeleteCustomerGroup. This event is a non-specified Structural Event. – NewAdministrator. This event is a non-specified Structural Event. – EditAdministrator. This event is a non-specified Structural Event. – DeleteAdministrator. This event is a non-specified Structural Event. – NewRole. This event is a non-specified Structural Event. – EditRole. This event is a non-specified Structural Event. – DeleteRole. This event is a non-specified Structural Event. – LogIn – RestorePreviousShoppingCart – RestorePreviousProductLists

250 – LogOut – SaveCurrentProductLists – NewSession – DeleteSession – SetCurrentWebsite – SetCurrentStore – SetCurrentStoreView – SetCurrentCurrency

I Store Administration

– ShowProductPropertiesAndAbleToRateAttributes. This event is a query. – NewProduct – EditProduct – DeleteProduct – NewCategory. This event is a non-specified Structural Event. – EditCategory. This event is a non-specified Structural Event. – MoveCategory – DeleteCategory. This event is a non-specified Structural Event. – ShowAttributeProperties. This event is a query. – NewAttribute – EditAttribute – DeleteAttribute – NewAttributeSet. This event is a non-specified Structural Event. – EditAttributeSet. This event is a non-specified Structural Event. – DeleteAttributeSet. This event is a non-specified Structural Event. – NewTierPrice. This event is a non-specified Structural Event. – EditTierPrice. This event is a non-specified Structural Event. – DeleteTierPrice. This event is a non-specified Structural Event. – NewCatalogPriceRule. This event is a non-specified Structural Event. – EditCatalogPriceRule. This event is a non-specified Structural Event. – ApplyCatalogPriceRule. This event . – DeleteCatalogPriceRule. This event is a non-specified Structural Event. – ApplyAllCatalogPriceRules. This event . – NewShoppingCartPriceRule. This event is a non-specified Structural Event. – EditShoppingCartPriceRule. This event is a non-specified Structural Event. – DeleteShoppingCartPriceRule. This event is a non-specified Structural Event.

I Additional Activities

– NewTag. This event is a non-specified Structural Event. – EditTag. This event is a non-specified Structural Event. – DeleteTag. This event is a non-specified Structural Event. – ShowTagsOfCustomer. This event is a query. – ShowPopularTags. This event is a query.

251 – ShowAllTags. This event is a query. – NewReview – NewAdministratorReview – EditReview. This event is a non-specified Structural Event. – DeleteReview. This event is a non-specified Structural Event. – NewProperty. This event is a non-specified Structural Event. – EditProperty. This event is a non-specified Structural Event. – DeleteProperty. This event is a non-specified Structural Event. – ShowReviewsOfProduct. This event is a query. – ShowReviewsOfCustomer. This event is a query. – AddProductToWishlist – AddCommentToWishlistItem – RemoveCommentFromWishlistItem – RemoveProductFromWishlist – AddProductToCompareList – RemoveProductFromCompareList – SingUpForPriceAlert – SingUpForStockAlert – ShowWishList. This event is a query. – ShowReadyToCompareProducts. This event is a query. – ShowRecentlyComparedProducts. This event is a query. – ShowRecentlyViewedProducts. This event is a query. – ShowCompare. This event is a query. – NewNewsletterTemplate. This event is a non-specified Structural Event. – EditNewsletterTemplate. This event is a non-specified Structural Event. – DeleteNewsletterTemplate. This event is a non-specified Structural Event. – NewNewsletter. This event is a non-specified Structural Event. – EditNewsletter. This event is a non-specified Structural Event. – SignUpForNewsletter – SignDownFromNewsletter – DeleteNewsletterSubscription. This event is a non-specified Structural Event. – UnsubscribeNewsletterSubscription. This event . – ShowShoppingCart. This event is a query. – ProductDownload. This event concerns specifically on Non-Simple Products. – ShowDownloadableProductsOfCustomer. This event concerns specifically on Non-Simple Prod- ucts. – TellToAFriendUsed – ProductViewed – ResetTemporalInfo

I Online catalog

– ShowProductInfo. This event is a query. – ProductViewed

252 – AddProductToShoppingCart – UpdateShoppingCart – ApplyCouponCode – ShowShippingCost. This event is a query. – OrderConfirmation – OrderConfirmationWithMultipleCheckout. This event concerns specifically on Non-Simple Prod- ucts. – DuplicateAccount – NewAdministrationShoppingCart – ShowPredefinedOrderSettings. This event is a query. – ShowShoppingCart. This event is a query. – ShowWishList. This event is a query. – ShowReadyToCompareProducts. This event is a query. – ShowRecentlyComparedProducts. This event is a query. – ShowRecentlyViewedProducts. This event is a query. – ShowFoundProducts. This event is a query. – ShowBestPurchasedProducts. This event is a query. – AddProductToShoppingCart – ChangeCurrencyOfAdministrationShoppingCart – ChangeEMailAndGroupOfAdministrationShoppingCart – ApplyCouponCode – AddProductToShoppingCart – AddGiftMessage – AddGiftMessageToItem – AddGiftMessageToExistingOrder. This event . – OrderConfirmation – DeleteAdministrationShoppingCart – CancelOrder – HoldOrder – UnholdOrder – Reorder – ShowOrdersOfCustomer. This event is a query. – AddInvoice – PayInvoice – CancelInvoice – AddShipment – AddRefund – CancelRefund – AddComment

I Store Reports

– ShowSalesReport. This event is a query. – ShowTaxInformationReport. This event is a query.

253 – ShowShippingInformationReport. This event is a query. – ShowInvoicesReport. This event is a query. – ShowRefundsReport. This event is a query. – ShowCouponCodeReport. This event is a query. – ShowProductsInCartsReport. This event is a query. – ShowAbandonedCartsReport. This event is a query. – ShowBestsellersReport. This event is a query. – ShowOrderedProductsReport. This event is a query. – ShowMostViewedProductsReport. This event is a query. – ShowStockOfProductsReport. This event is a query. – ShowDownloadsReport. This event is a query. – ShowNewCustomerAccountsReport. This event is a query. – ShowCustomersByOrdersTotalReport. This event is a query. – ShowCustomersByNumberOfOrdersReport. This event is a query. – ShowCustomerReviewsReport. This event is a query. – ShowProductsReviewsReport. This event is a query. – ShowTagsByCustomerReport. This event is a query. – ShowTagsByProductReport. This event is a query. – ShowPopularTagsReport. This event is a query. – ShowPopularTagsReport. This event is a query.

Several events can be modelised as specializations of an abstract event, as structural entities do (see section 4.2). The abstract events specified here do not define initial integrity constraints or effect postconditions, but attributes and relationships that will be inherited by concrete events. Accordingly, they are not defined in any Use Case and represent any valid change on the IB. We present here the specification of those Abstract Events:

I ScopeDefinedEvent Relationships editForWebsiteScope : Website [0..1] editForStoreViewScope : StoreView [0..1]

I ExistingOrderEvent

Relationships order : Order [1]

I ExistingCustomerEvent

Relationships customer : Customer [1]

254 I ExistingAdministratorEvent

Relationships administrator : Administrator [1]

I StoreViewPlacedEvent

Relationships storeView : StoreView [1]

I ExistingAdministrationShoppingCartEvent

Relationships shoppingCart : AdministrationShoppingCart [1]

I ExistingShoppingCartEvent

Relationships shoppingCart : ShoppingCart [1]

I ExistingSessionEvent

Relationships session : Session [1]

I ExistingCustomerSessionEvent

Relationships customerSession : CustomerSession [1]

I ExistingWishListItemEvent

Relationships wishListItem : WishListItem [1]

I ExistingProductEvent

Relationships product : Product [1]

255 7.4 Store Configuration

Event 7.4.1 SetDefaultWebsite

From use case: Select the default website

I Event Specification Relationships website : Website [1]

I Effect

context SetDefaultWebsite::effect()

post:

System.allInstances->any(true).defaultWebsite = self.website

Event 7.4.2 SetDefaultStoreOfWebsite

From use case: Select the default store of a website

I Event Specification Relationships store : Store [1] website : Website [1]

I Initial Integrity Constraints

context SetDefaultStoreOfWebsite :: storeBelongsToWebsite() : Boolean body:

self.website.store->includes(self.store)

I Effect

context SetDefaultStoreOfWebsite::effect()

post:

self.website.defaultStore = self.store

256 Event 7.4.3 SetDefaultStoreViewOfStore

From use case: Select the default store view of a store

I Event Specification Relationships storeView : StoreView [1] store : Store [1]

I Initial Integrity Constraints

context SetDefaultStoreViewOfStore :: storeBelongsToWebsite() : Boolean body:

self.store.storeView->includes(self.storeView)

I Effect

context SetDefaultStoreViewOfStore::effect()

post:

self.store.defaultStoreView = self.storeView

Event 7.4.4 SetDefaultLanguage

From use case: Select the default language

I Event Specification Relationships language : Language [1]

Generalizations Relationships inherited from ScopeDefinedEvent editForWebsiteScope : Website [0..1] editForStoreViewScope : StoreView [0..1]

I Initial Integrity Constraints

context SetDefaultLanguage :: notTwoScopesDefined() : Boolean body:

self.editForWebsiteScope.isEmpty() or self.editForStoreViewScope.isEmpty()

257 I Effect

context SetDefaultLanguage::effect()

post: if self.editForWebsiteScope.notEmpty() then self.editForWebsiteScope.store.storeView -> forAll(stv|stv.language = self.language) else if self.editForStoreViewScope.notEmpty() then self.editForStoreViewScope.language = self.language else StoreView.allInstances -> forAll(stv|stv.language = self.language) endif endif

258 7.5 Customers

Event 7.5.1 CreateCustomer

From use case: Create a Customer

I Event Specification Attributes eMail : EMail [1] firstName : String [1] lastName : String [1] password : String [1] passwordConfirmation : String [1] namePrefix : String [0..1] middleName : String [0..1] nameSuffix : String [0..1] dateOfBirth : Date [0..1] taxVatNumber : String [0..1]

Generalizations Relationships inherited from StoreViewPlacedEvent storeView : StoreView [1]

I Initial Integrity Constraints

context CreateCustomer :: customerDoesNotExist() : Boolean body: not Customer.allInstances() -> exists (c | c.eMail = self.eMail and c.websiteWhereIsVisible -> includes ( self.storeView.store.website ) )

context CreateCustomer :: passwordCorrect() : Boolean body:

self.password = self.passwordConfirmation

I Effect

context CreateCustomer::effect()

post: c.oclIsNew() and c.oclIsTypeOf(Customer) and c.eMail = self.eMail and c.firstName = self.firstName and c.lastName = self.lastName and c.password = self.password and c.namePrefix = self.namePrefix and c.middleName = self.middleName and c.nameSuffix = self.nameSuffix and c.dateOfBirth = self.dateOfBirth and c.taxVatNumber = self.taxVatNumber and c.createdAt = Now() and c.storeViewWhereIsCreated = self.storeView and c.websiteWhereIsAssociated = self.storeView.store.website and

259 c.customerGroup = self.storeView.customerConfigurationInStoreView.defaultCustomerGroup

Event 7.5.2 PasswordChange

From use case: Change password

I Event Specification Attributes oldPassword : String [1] newPassword : String [1] newPasswordConfirmation : String [1]

Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context PasswordChange :: oldPasswordIsCorrect() : Boolean body:

customer.password = self.oldPassword

context PasswordChange :: passwordCorrect() : Boolean body:

self.newPassword = self.newPasswordConfirmation

I Effect

context PasswordChange::effect()

post:

self.customer.password = self.newPassword

Event 7.5.3 EditCustomerDetails

From use case: Change customer details

I Event Specification Attributes newFirstName : String [1] newLastName : String [1] newEMail : EMail [1]

260 Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1] Relationships inherited from StoreViewPlacedEvent storeView : StoreView [1]

I Initial Integrity Constraints

context EditCustomerDetails :: customerDoesNotExist() : Boolean body: not Customer.allInstances() -> exists (c | c.eMail = self.newEMail and c.websiteWhereIsVisible -> includes ( self.storeView.store.website ) )

I Effect

context EditCustomerDetails::effect()

post: self.customer.firstName = self.newFirstName and self.customer.lastName = self.newLastName and self.customer.eMail = self.newEMail

Event 7.5.4 NewCustomerAddress

From use case: Administrate address book, Place an Order, Checkout with multiple addresses, Add an order

I Event Specification Attributes newFirstName : String [1] newMiddleName : String [0..1] newLastName : String [1] newNamePrefix : String [0..1] newNameSuffix : String [0..1] newCompany : String [0..1] newStreetAddress : String [1] newTelephone : String [1] newFax : String [0..1]

Relationships country : Country [1] zone : Zone [0..1] municipality : Municipality [1] postalArea : PostalArea [0..1]

Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context NewCustomerAddress :: hasACorrectZoneIfItIsNeeded() : Boolean

261 body:

self.country.zone -> notEmpty() = (self.zone -> notEmpty() and self.country = self.zone.country)

context NewCustomerAddress :: municipalityBelongsToCorrectZoneIfItIsNeeded() : Boolean body:

self.municipality.country.zone -> notEmpty() = (self.municipality.zone -> notEmpty() and self.municipality.country = self.municipality.zone.country)

context NewCustomerAddress :: hasAPostalAreaIfItIsNeeded() : Boolean body:

self.country.postalCodeIsMandatory implies self.postalArea -> notEmpty()

context NewCustomerAddress :: hasACorrectPostalArea() : Boolean body:

self.municipality.postalArea -> includes( self.postalArea )

context NewCustomerAddress :: hasACorrectMunicipality() : Boolean body:

self.country.municipality -> includes( self.municipality ) and self.zone -> notEmpty() implies self.zone.municipality -> includes ( self.municipality )

context NewCustomerAddress :: hasACorrectZone() : Boolean body:

self.country.zone -> includes( self.zone )

I Effect

context NewCustomerAddress::effect()

post: Address.allInstances() -> exists(a| a.firstName = self.newFirstName and a.middleName = self.newMiddleName and a.lastName = self.newLastName and a.namePrefix = self.newNamePrefix and a.nameSuffix = self.newNameSuffix and a.company = self.newCompany and a.streetAddress = self.newStreetAddress and a.telephone = self.newTelephone and a.fax = self.newFax and a.country = self.country and a.zone = self.zone and a.municipality = self.municipality and a.postalArea = self.postalArea and self.customer.address -> includes(a))

Event 7.5.5 DeleteCustomerAddress

From use case: Administrate address book

I Event Specification Relationships

262 address : Address [1]

Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context DeleteCustomerAddress :: addressOfCustomer() : Boolean body:

self.customer.address -> includes(self.address)

I Effect

context DeleteCustomerAddress::effect()

post:

self.customer.address -> excludes(self.address)

Event 7.5.6 DefaultDeliveryAddressChange

From use case: Administrate address book

I Event Specification Relationships address : Address [1]

Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context DefaultDeliveryAddressChange :: addressOfCustomer() : Boolean body:

self.customer.address -> includes(self.address)

I Effect

context DefaultDeliveryAddressChange::effect()

post:

self.customer.defaultDelivery = self.address

263 Event 7.5.7 DefaultBillingAddressChange

From use case: Administrate address book

I Event Specification Relationships address : Address [1]

Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context DefaultBillingAddressChange :: addressOfCustomer() : Boolean body:

self.customer.address -> includes(self.address)

I Effect

context DefaultBillingAddressChange::effect()

post:

self.customer.defaultBilling = self.address

Event 7.5.8 EditCustomerAddress

From use case: Administrate address book

I Event Specification Attributes newAddress : Address [1]

Relationships address : Address [1]

Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context EditCustomerAddress :: addressOfCustomer() : Boolean body:

264 self.customer.address -> includes(self.address)

context EditCustomerAddress :: hasACorrectZoneIfItIsNeeded() : Boolean body:

self.newAddress.country.zone -> notEmpty() implies (self.newAddress.zone -> notEmpty() and self.newAddress.- country = self.newAddress.zone.country)

context EditCustomerAddress :: hasAMunicipalityOfTheCorrectZoneIfItIsNeeded() : Boolean body:

self.newAddress.municipality.country.zone -> notEmpty() implies (self.newAddress.municipality.zone -> notEmp- ty() and self.newAddress.municipality.country = self.newAddress.municipality.zone.country)

context EditCustomerAddress :: hasAPostalAreaIfItIsNeeded() : Boolean body:

self.newAddress.newAddress.country.postalCodeIsMandatory implies self.newAddress.postalArea -> notEmp- ty()

context EditCustomerAddress :: hasACorrectPostalArea() : Boolean body:

self.newAddress.municipality.postalArea -> includes( self.newAddress.postalArea )

context EditCustomerAddress :: hasACorrectMunicipality() : Boolean body:

self.newAddress.country.municipality -> includes( self.newAddress.municipality ) and self.newAddress.zone -> notEmpty() implies self.newAddress.zone.municipality -> includes ( self.newAddress.- municipality )

context EditCustomerAddress :: hasACorrectZone() : Boolean body:

self.newAddress.country.zone -> includes( self.newAddress.zone )

I Effect

context EditCustomerAddress::effect()

post:

self.customer.address -> excludes(self.address) and self.customer.address ->includes(self.newAddress)

Event 7.5.9 LogIn

From use case: Log In, Place an Order

I Event Specification Relationships anonymousSession : AnonymousSession [1]

Generalizations

265 Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context LogIn :: CustomerIsNotLoggedIn() : Boolean body:

self.customer.customerSession -> isEmpty()

context LogIn :: CustomerIsVisibleInWebsite() : Boolean body:

self.customer.websiteWhereIsVisible -> includes(self.anonymousSession.storeView.store.website)

I Effect

context LogIn::effect()

post: s.oclIsNew() and s.oclIsTypeOf(CustomerSession) and s.ipAddress = self.anonymousSession.ipAddress and s.createdAt = self.anonymousSession.createdAt and s.lastActivityAt = Now() and s.customer = self.customer and s.storeView = self.anonymousSession.storeView and if self.customer.lastCurrentCurreny.isDefined() then s.currentCurrency = self.customer.lastCurrentCurrency else s.currentCurrency = self.anonymousSession.currentCurrency endif

Event 7.5.10 RestorePreviousShoppingCart

From use case: Log In

I Event Specification Relationships anonymousShoppingCart : AnonymousShoppingCart [1]

Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Effect

context RestorePreviousShoppingCart::effect()

post createCustomerSC: [email protected]() implies (sc.oclIsNew and sc.oclIsTypeOf(CustomerShoppingCart) and sc.customer = self.customer and sc.createdAt = Now() and sc.updatedAt = Now())

266 post updateCustomerSC: [email protected] -> notEmpty() implies self.customer.customerShoppingCart.updatedAt = Now()

post addProductsToCustomerSC: self.customer.customerShoppingCart.shoppingCartItem -> forAll ( i | ([email protected] -> includes(i) or self.anonymousShopping- [email protected] -> includes(i)) and self.customer.customerShoppingCart.shoppingCartItem -> any( i2 | i.product=i2.product ).quantity = [email protected] -> any( i2 | i.product=i2.product ).quantity + [email protected] -> any( i2 | i.product=i2.product ).quantity ) and self.customer.customerShoppingCart.couponCode = self.anonymousShoppingCart.couponCode

post removeProductsFromAnonymousSC:

self.anonymousShoppingCart.shoppingCartItem -> isEmpty()

Event 7.5.11 RestorePreviousProductLists

From use case: Log In

I Event Specification Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context RestorePreviousProductLists :: customerIsLoggedIn() : Boolean body:

self.customer.customerSession -> notEmpty()

I Effect

context RestorePreviousProductLists::effect()

post: let infoCuSt: ActivityInfoOfCustomerInStoreView = self.customer.activityInfoOfCustomerInStoreView->any(ai|ai.storeViewWithInfoAbout=self.customer.custom- erSession.storeView) in let infoSeSt: ActivityInfoOfSessionInStoreView = self.customer.customerSession.activityInfoOfSessionInStoreView->any(ai|ai.storeViewWithInfoAbout=self.- customer.customerSession.storeView) in let numProdsLeft : Integer = [email protected] -> size() + infoCuSt.readyToCompareProducts -> size() - self.customer.customerSession.storeView.catalogConfigurationInStoreView.numberOfRecentlyCompared- ProductsSaved in infoSeSt.explicitReadyToCompareProducts = [email protected] -> union ( infoCuSt.readyToCompareProducts ) and infoSeSt.explicitRecentlyComparedProducts = [email protected] -> union (info- CuSt.recentlyComparedProducts.subOrderedSet(1, infoCuSt.recentlyComparedProducts->size() - numProdsLeft.- max(0))) and

267 infoSeSt.explicitRecentlyViewedProducts = [email protected] -> union (infoCuSt.re- centlyViewedProducts.subOrderedSet(1, infoCuSt.recentlyViewedProducts->size() - numProdsLeft.max(0)))

post: let infoCuWe: ActivityInfoOfCustomerInWebsite = self.customer.activityInfoOfCustomerInWebsite->any(ai|ai.websiteWithInfoAbout=self.customer.customerS- ession.storeView.store.website) in let infoSeWe: ActivityInfoOfSessionInWebsite = self.customer.customerSession.activityInfoOfSessionInWebsite->any(ai|ai.websiteWithInfoAbout=self.- customer.customerSession.storeView.store.website) in let numProdsLeft : Integer = [email protected] -> size() + infoCuWe.readyToCompareProducts -> size() - self.customer.customerSession.storeView.catalogConfigurationInStoreView.numberOfRecentlyCompared- ProductsSaved in infoSeWe.explicitReadyToCompareProducts = [email protected] -> union ( infoCuWe.readyToCompareProducts ) and infoSeWe.explicitRecentlyComparedProducts = [email protected] -> union (in- foCuWe.recentlyComparedProducts.subOrderedSet(1, infoCuWe.recentlyComparedProducts->size() - numProd- sLeft.max(0))) and infoSeWe.explicitRecentlyViewedProducts = [email protected] -> union (infoCuWe.- recentlyViewedProducts.subOrderedSet(1, infoCuWe.recentlyViewedProducts->size() - numProdsLeft.max(0)))

Event 7.5.12 LogOut

From use case: Log Out

I Event Specification Generalizations Relationships inherited from ExistingCustomerSessionEvent customerSession : CustomerSession [1]

I Effect

context LogOut::effect()

post createAnonymousSession: s.oclIsNew() and s.oclIsTypeOf(AnonymousSession) and s.ipAddress = self.customerSession.ipAddress and s.createdAt = self.customerSession.createdAt and s.lastActivityAt = Now() and s.lastUrlVisited = self.anonymousSession.lastUrlVisited and s.storeView = self.customerSession.storeView and s.currentCurrency = self.customerSession.currentCurrency

post saveCustomersCurrentCurrency:

self.customerSession.customer.lastCurrentCurrency = self.customerSession.currentCurrency

268 Event 7.5.13 SaveCurrentProductLists

From use case: Log Out, Finish Session

I Event Specification Generalizations Relationships inherited from ExistingCustomerSessionEvent customerSession : CustomerSession [1]

I Effect

context SaveCurrentProductLists::effect()

post: let infoCuSt: ActivityInfoOfCustomerInStoreView = self.customer.activityInfoOfCustomerInStoreView->any(ai|ai.storeViewWithInfoAbout=self.customerSession.- storeView) in let infoSeSt: ActivityInfoOfSessionInStoreView = self.customer.customerSession.activityInfoOfSessionInStoreView->any(ai|ai.storeViewWithInfoAbout=self.- customerSession.storeView) in infoCuSt.readyToCompareProducts = [email protected] and infoCuSt.recentlyComparedProducts = [email protected] and infoCuSt.recentlyViewedProducts = [email protected]

post: let infoCuWe: ActivityInfoOfCustomerInWebsite = self.customer.activityInfoOfCustomerInWebsite->any(ai|ai.websiteWithInfoAbout=self.customerSession.store- View.store.website) in let infoSeWe: ActivityInfoOfSessionInWebsite = self.customer.customerSession.activityInfoOfSessionInWebsite->any(ai|ai.websiteWithInfoAbout=self.- customerSession.storeView.store.website) in infoCuWe.readyToCompareProducts = [email protected] and infoCuWe.recentlyComparedProducts = [email protected] and infoCuWe.recentlyViewedProducts = [email protected]

Event 7.5.14 NewSession

From use case: Open session

I Event Specification Attributes ipAddress : String [1]

Relationships website : Website [1]

269 I Initial Integrity Constraints

context NewSession :: ipAddressDoesNotExist() : Boolean body:

not AnonymousSession.allInstances() -> exists(s|s.ipAddress=self.ipAddress)

I Effect

context NewSession::effect()

post: s.oclIsNew() and s.oclIsTypeOf(AnonymousSession) and s.ipAddress = self.ipAddress and s.createdAt = Now() and s.lastActivityAt = Now() and s.storeView = self.website.defaultStore.defaultStoreView and s.currentCurrency = s.storeView.currencyConfigurationInStoreView.defaultCurrency and cart.oclIsNew() and cart.oclIsTypeOf(AnonymousShoppingCart) and s.oclAsType(AnonymousSession).anonymousShoppingCart = cart

Event 7.5.15 DeleteSession

From use case: Finish session

I Event Specification Generalizations Relationships inherited from ExistingSessionEvent session : Session [1]

I Effect

context DeleteSession::effect()

post:

not [email protected](OclAny)

post:

[email protected](AnonymousSession) implies [email protected] -> isEmp- ty()

Event 7.5.16 SetCurrentWebsite

From use case: Change the current Website

I Event Specification

270 Relationships website : Website [1]

Generalizations Relationships inherited from ExistingSessionEvent session : Session [1]

I Effect

context SetCurrentWebsite::effect()

post:

self.session.storeView = self.website.defaultStore.defaultStoreView

Event 7.5.17 SetCurrentStore

From use case: Change the current Store

I Event Specification Relationships store : Store [1]

Generalizations Relationships inherited from ExistingSessionEvent session : Session [1]

I Effect

context SetCurrentStore::effect()

post:

self.session.storeView = self.store.defaultStoreView

Event 7.5.18 SetCurrentStoreView

From use case: Change the current Store View

I Event Specification Relationships storeView : StoreView [1]

Generalizations Relationships inherited from ExistingSessionEvent session : Session [1]

271 I Effect

context SetCurrentStoreView::effect()

post:

self.session.storeView = self.storeView

Event 7.5.19 SetCurrentCurrency

From use case: Change the current Currency

I Event Specification Relationships currency : Currency [1]

Generalizations Relationships inherited from ExistingSessionEvent session : Session [1]

I Initial Integrity Constraints

context SetCurrentCurrency :: currencyIsAllowed() : Boolean body:

self.session.storeView.currencyConfigurationInStoreView.allowedCurrency -> includes(self.currency)

I Effect

context SetCurrentCurrency::effect()

post:

self.session.currentCurrency = self.currency

272 7.6 Store Administration

Event 7.6.1 NewProduct

From use case: Add a product

I Event Specification Attributes values : TupleType (a: Attribute, v: AttributeValue) [1..*] productType : ProductType [1] sku : String [1] name : String [1] netPrice : Money [1] weight : Decimal [0..1] status : Status [1] isNewFrom : Date [0..1] isNewUntil : Date [0..1] specialNetPrice : Money [0..1] specialNetPriceFrom : Date [0..1] specialNetPriceUntil : Date [0..1] description : String [0..1] shortDescription : String [0..1] metaDescription : String [0..1] metaKeyword : String [*] metaTitle : String [0..1] imageGalleryPath : String [0..1] baseImagePath : String [0..1] smallImagePath : String [0..1] thumbnailPath : String [0..1] urlKey : String [0..1] isAvailableForGoogleCheckout : Boolean [0..1] giftMessageAllowed : Boolean [0..1] visibleOnCatalog : Boolean [0..1] visibleOnSearch : Boolean [0..1] stockStatus : ProductStatus [1] quantity : PositiveInteger [0..1] qtyToBecomeOutOfStock : PositiveInteger [0..1] minQtyAllowedInShoppingCart : PositiveInteger [0..1] maxQtyAllowedInShoppingCart : PositiveInteger [0..1] notifyForQuantityBelow : PositiveInteger [0..1] backOrderPolicy : BackOrderPolicy [0..1]

Relationships crossSellProduct : Product [*] upSellProduct : Product [*] relatedProduct : Product [*] website : Website [1..*] productTaxClass : ProductTaxClass [0..1] fixedTaxRate : TaxRate [*] attributeSet : AttributeSet [1]

273 I Initial Integrity Constraints

context NewProduct :: skuDoesNotExist() : Boolean body:

not Product.allInstances() -> exists ( p | p.sku = self.sku )

context NewProduct :: attributesHaveOnlyOneValue() : Boolean body:

self.values -> isUnique(attribute)

context NewProduct :: attributeValuesHaveTheCorrectType() : Boolean body: self.values -> forAll ( v | (v.a.oclIsTypeOf ( TextualAttribute ) implies v.v.oclIsTypeOf ( TextualValue )) and (v.a.oclIsTypeOf ( PriceAttribute ) implies v.v.oclIsTypeOf ( PriceValue )) and (v.a.oclIsTypeOf ( DateAttribute ) implies v.v.oclIsTypeOf ( DateValue )) and (v.a.oclIsTypeOf ( BooleanAttribute ) implies v.v.oclIsTypeOf ( BooleanValue )) and (v.a.oclIsTypeOf ( IntegerAttribute ) implies v.v.oclIsTypeOf ( IntegerValue )) and (v.a.oclIsTypeOf ( DecimalAttribute ) implies v.v.oclIsTypeOf ( DecimalValue )) and (v.a.oclIsTypeOf ( ImageAttribute ) implies v.v.oclIsTypeOf ( ImageValue )) and (v.a.oclIsTypeOf ( SingleValuedEnumerationAttribute ) implies (v.v.oclIsTypeOf ( EnumerationValue ) and v.a.oclAsType(SingleValuedEnumerationAttribute).allowedValue- >includes(v.v) )) and (v.a.oclIsTypeOf ( MultipleValuedEnumerationAttribute ) implies (v.v.oclIsTypeOf ( MultipleEnumerationValue ) and v.a.oclAsType(EnumerationValue).allowedValue- >includesAll(v.v.oclAsType(MultipleValuedEnumerationAttribute).enumerationValue) )) )

context NewProduct :: onlyAbleToRateAttributesHaveValues() : Boolean body: let ableToRateAttributes : Set(Attribute) = self.attributeSet.attribute -> select(a|a.associatedProductType -> includes(self.productType)) in self.values->collect(a) -> forAll ( a | ableToRateAttributes -> includes(a) )

context NewProduct :: requiredAttributesHaveAValue() : Boolean body: let ableToRateAttributes : Set(Attribute) = self.attributeSet.attribute -> select(a|a.associatedProductType -> includes(self.productType)) in ableToRateAttributes -> forAll ( a | a.isRequired implies self.values->collect(a) -> includes(a) )

context NewProduct :: uniqueValuedAttributesHaveANonUsedValue() : Boolean body: self.values -> forAll ( v | v.a.hasUniqueValues implies not v.a.storeViewAttributeRating.attributeValue -> exists(val | val=v.v) )

context NewProduct :: onlyAbleToRatePropertiesHaveValues() : Boolean body: let conf : ProductPropertiesTypeAssociationConfiguration = ProductPropertiesTypeAssociationConfiguration.allInstances() -> any(true) in (self.name.isDefined() implies conf.nameAssociatedToProductType->includes(self.productType)) and (self.netPrice.isDefined() implies conf.netPriceAssociatedToProductType->includes(self.productType)) and (self.weight.isDefined() implies conf.weightAssociatedToProductType->includes(self.productType)) and (self.status.isDefined() implies conf.statusAssociatedToProductType->includes(self.productType)) and (self.isNewFrom.isDefined() implies conf.isNewFromAssociatedToProductType->includes(self.productType)) and (self.isNewUntil.isDefined() implies conf.isNewUntilAssociatedToProductType->includes(self.productType)) and (self.specialNetPrice.isDefined() implies conf.specialNetPriceAssociatedToProductType->includes(self.productType)) and (self.specialNetPriceFrom.isDefined() implies conf.specialNetPriceFromAssociatedToProductType->includes(self.productType)) and (self.specialNetPriceUntil.isDefined() implies conf.specialNetPriceUntilAssociatedToProductType->includes(self.productType)) and (self.description.isDefined() implies

274 conf.descriptionAssociatedToProductType->includes(self.productType)) and (self.shortDescription.isDefined() implies conf.shortDescriptionAssociatedToProductType->includes(self.productType)) and (self.metaDescription.isDefined() implies conf.metaDescriptionAssociatedToProductType->includes(self.productType)) and (self.metaKeyword -> forAll(k|k.isDefined()) implies conf.metaKeywordAssociatedToProductType->includes(self.productType)) and (self.metaTitle.isDefined() implies conf.metaTitleAssociatedToProductType->includes(self.productType)) and (self.imageGalleryPath.isDefined() implies conf.imageGalleryPathAssociatedToProductType->includes(self.productType)) and (self.baseImagePath.isDefined() implies conf.baseImagePathAssociatedToProductType->includes(self.productType)) and (self.smallImagePath.isDefined() implies conf.smallImagePathAssociatedToProductType->includes(self.productType)) and (self.thumbnailPath.isDefined() implies conf.thumbnailPathAssociatedToProductType->includes(self.productType)) and (self.urlKey.isDefined() implies conf.urlKeyAssociatedToProductType->includes(self.productType)) and (self.isAvailableForGoogleCheckout.isDefined() implies conf.isAvailableForGoogleCheckoutAssociatedToProductType->includes(self.productType)) and (self.giftMessageAllowed.isDefined() implies conf.giftMessageAllowedAssociatedToProductType->includes(self.productType)) and (self.visibleOnCatalog.isDefined() implies conf.visibleOnCatalogAssociatedToProductType->includes(self.productType)) and (self.visibleOnSearch.isDefined() implies conf.visibleOnSearchAssociatedToProductType->includes(self.productType)) context NewProduct :: mandatoryPropertiesAreRated() : Boolean body: let confType : ProductPropertiesTypeAssociationConfiguration = ProductPropertiesTypeAssociationConfiguration.allInstances() -> any(true) in let confOblig : ProductPropertiesObligatorinessConfiguration = ProductPropertiesObligatorinessConfiguration.allInstances() -> any(true) in ((confOblig.isNewFromIsMandatory and confType.isNewFromAssociatedToProductType->includes(self.productType)) implies self.isNewFrom.isDefined()) and ((confOblig.isNewUntilIsMandatory and confType.isNewUntilAssociatedToProductType->includes(self.productType)) implies self.isNewUntil.isDefined()) and ((confOblig.specialNetPriceIsMandatory and confType.specialNetPriceAssociatedToProductType->includes(self.productType)) implies self.specialNetPrice.isDefined()) and ((confOblig.specialNetPriceFromIsMandatory and confType.specialNetPriceFromAssociatedToProductType->includes(self.productType)) implies self.specialNetPriceFrom.isDefined()) and ((confOblig.specialNetPriceUntilIsMandatory and confType.specialNetPriceUntilAssociatedToProductType->includes(self.productType)) implies self.specialNetPriceUntil.isDefined()) and ((confOblig.descriptionIsMandatory and confType.descriptionAssociatedToProductType->includes(self.productType)) implies self.description.isDefined()) and ((confOblig.shortDescriptionIsMandatory and confType.shortDescriptionAssociatedToProductType->includes(self.productType)) implies self.shortDescription.isDefined()) and ((confOblig.metaDescriptionIsMandatory and confType.metaDescriptionAssociatedToProductType->includes(self.productType)) implies self.metaDescription.isDefined()) and ((confOblig.metaKeywordIsMandatory and confType.metaKeywordAssociatedToProductType->includes(self.productType)) implies self.metaKeyword -> forAll(k|k.isDefined())) and ((confOblig.metaTitleIsMandatory and confType.metaTitleAssociatedToProductType->includes(self.productType)) implies self.metaTitle.isDefined()) and ((confOblig.imageGalleryPathIsMandatory and confType.imageGalleryPathAssociatedToProductType->includes(self.productType)) implies self.imageGalleryPath.isDefined()) and ((confOblig.baseImagePathIsMandatory and confType.baseImagePathAssociatedToProductType->includes(self.productType)) implies self.baseImagePath.isDefined()) and ((confOblig.smallImagePathIsMandatory and confType.smallImagePathAssociatedToProductType->includes(self.productType)) implies self.smallImagePath.isDefined()) and ((confOblig.thumbnailPathIsMandatory and

275 confType.thumbnailPathAssociatedToProductType->includes(self.productType)) implies self.thumbnailPath.isDefined()) and ((confOblig.urlKeyIsMandatory and confType.urlKeyAssociatedToProductType->includes(self.productType)) implies self.urlKey.isDefined()) and ((confOblig.isAvailableForGoogleCheckoutIsMandatory and confType.isAvailableForGoogleCheckoutAssociatedToProductType->includes(self.productType)) implies self.isAvailableForGoogleCheckout.isDefined()) and ((confOblig.giftMessageAllowedIsMandatory and confType.giftMessageAllowedAssociatedToProductType->includes(self.productType)) implies self.giftMessageAllowed.isDefined()) and ((confOblig.visibleOnCatalogIsMandatory and confType.visibleOnCatalogAssociatedToProductType->includes(self.productType)) implies self.visibleOnCatalog.isDefined()) and ((confOblig.visibleOnSearchIsMandatory and confType.visibleOnSearchAssociatedToProductType->includes(self.productType)) implies self.visibleOnSearch.isDefined())

context NewProduct :: stockOptionsAreRatedOnlyForTheCorrectProductTypes() : Boolean body: ( self.productType=ProductType::Grouped or self.productType=ProductType::Configurable or self.productType=ProductType::Bundle ) implies ( self.quantity.isUndefined() and self.qtyToBecomeOutOfStock.isUndefined() and self.minQtyAllowedInShoppingCart.isUndefined() and self.maxQtyAllowedInShoppingCart.isUndefined() and self.notifyForQuantityBelow.isUndefined()and self.backOrderPolicy.isUndefined() )

I Effect

context NewProduct::effect()

post productIsCreated: p.oclIsNew() and p.oclIsKindOf(Product) and self.productType = ProductType::Simple implies p.oclIsTypeOf(SimpleProduct) and self.productType = ProductType::Grouped implies p.oclIsTypeOf(GroupedProduct) and self.productType = ProductType::Configurable implies p.oclIsTypeOf(ConfigurableProduct) and self.productType = ProductType::Virtual implies p.oclIsTypeOf(VirtualProduct) and self.productType = ProductType::Bundle implies p.oclIsTypeOf(BundleProduct) and self.productType = ProductType::Downloadable implies p.oclIsTypeOf(DownloadableProduct) and p.productTaxClass = self.productTaxClass and p.fixedTaxRate = self.fixedTaxRate and p.attributeSet = self.attributeSet and p.crossSellProduct = self.crossSellProduct and p.upSellProduct = self.upSellProduct and p.relatedProduct = self.relatedProduct and Website.allInstances() -> forAll ( w | piw.oclIsNew() and piw.oclIsTypeOf(ProductInWebsite) and piw.product = p and piw.website = w and piw.isAvailable = (self.website -> includes(w)) ) and StoreView.allInstances() -> forAll ( stw | pis.oclIsNew() and pis.oclIsTypeOf(ProductInStoreView) and pis.product = p and pis.storeView = stw ) and let ableToRateAttributes : Set(Attribute) = self.attributeSet.attribute -> select(a|a.associatedProductType -> includes(self.productType)) in ableToRateAttributes -> forAll ( a | gar.oclIsNew() and gar.oclIsTypeOf(GlobalAttributeRating) and gar.productOfGloballyRatedAttribute = p and gar.globallyRatedAttribute = a and Website.allInstances() -> forAll ( w | war.oclIsNew() and war.oclIsTypeOf(WebsiteAttributeRating) and war.websiteRatedProduct = p and war.websiteRatedAttribute = a and war.websiteWhereIsRated = w ) and StoreView.allInstances() -> forAll ( s | sar.oclIsNew() and sar.oclIsTypeOf(StoreViewAttributeRating) and

276 sar.storeViewRatedProduct = p and sar.storeViewRatedAttribute = a and sar.storeViewWhereIsRated = s ))

post attributesAreRated: self.values -> forAll ( v | v.a.globalAttributeRating->any(gar|gar.productOfGloballyRatedAttribute=p).genericAttributeValue=v.v )

post propertiesAreRated: p.sku = self.sku and p.genericName = self.name and p.genericNetPrice = self.netPrice and p.genericWeight = self.weight and p.genericStatus = self.status and p.genericIsNewFrom = self.isNewFrom and p.genericIsNewUntil = self.isNewUntil and p.genericSpecialNetPrice = self.specialNetPrice and p.genericSpecialNetPriceFrom = self.specialNetPriceFrom and p.genericSpecialNetPriceUntil = self.specialNetPriceUntil and p.genericDescription = self.description and p.genericShortDescription = self.shortDescription and p.genericMetaDescription = self.metaDescription and p.genericMetaKeyword = self.metaKeyword and p.genericMetaTitle = self.metaTitle and p.genericImageGalleryPath = self.imageGalleryPath and p.genericBaseImagePath = self.baseImagePath and p.genericSmallImagePath = self.smallImagePath and p.genericThumbnailPath = self.thumbnailPath and p.genericUrlKey = self.urlKey and p.genericIsAvailableForGoogleCheckout = self.isAvailableForGoogleCheckout and p.genericIsGiftMessageAllowed = self.giftMessageAllowed and p.genericIsVisibleOnCatalog = self.visibleOnCatalog and p.genericIsVisibleOnSearch = self.visibleOnSearch and p.stockStatus = self.stockStatus and p.quantity = self.quantity and p.qtyToBecomeOutOfStock = self.qtyToBecomeOutOfStock and p.minQtyAllowedInShoppingCart = self.minQtyAllowedInShoppingCart and p.maxQtyAllowedInShoppingCart = self.maxQtyAllowedInShoppingCart and p.notifyForQuantityBelow = self.notifyForQuantityBelow and p.backOrderPolicy = self.backOrderPolicy

Event 7.6.2 EditProduct

From use case: Edit a product

I Event Specification Attributes values : TupleType (a: Attribute, v: AttributeValue) [1..*] sku : String [1] name : String [1] netPrice : Money [1] weight : Decimal [0..1] status : Status [1] isNewFrom : Date [0..1] isNewUntil : Date [0..1] specialNetPrice : Money [0..1] specialNetPriceFrom : Date [0..1] specialNetPriceUntil : Date [0..1] description : String [0..1] shortDescription : String [0..1]

277 metaDescription : String [0..1] metaKeyword : String [0..1] metaTitle : String [0..1] imageGalleryPath : String [0..1] baseImagePath : String [0..1] smallImagePath : String [0..1] thumbnailPath : String [0..1] urlKey : String [0..1] isAvailableForGoogleCheckout : Boolean [0..1] giftMessageAllowed : Boolean [0..1] visibleOnCatalog : Boolean [0..1] visibleOnSearch : Boolean [0..1] stockStatus : ProductStatus [1] quantity : PositiveInteger [0..1] qtyToBecomeOutOfStock : PositiveInteger [0..1] minQtyAllowedInShoppingCart : PositiveInteger [0..1] maxQtyAllowedInShoppingCart : PositiveInteger [0..1] notifyForQuantityBelow : PositiveInteger [0..1] backOrderPolicy : BackOrderPolicy [0..1] productType : ProductType [1]

Relationships crossSellProduct : Product [*] upSellProduct : Product [*] relatedProduct : Product [*] website : Website [1..*] productTaxClass : ProductTaxClass [0..1] fixedTaxRate : TaxRate [*]

Generalizations Relationships inherited from ExistingProductEvent product : Product [1] Relationships inherited from ScopeDefinedEvent editForWebsiteScope : Website [0..1] editForStoreViewScope : StoreView [0..1]

I Initial Integrity Constraints

context EditProduct :: notTwoScopesDefined() : Boolean body:

self.editForWebsiteScope.isEmpty() or self.editForStoreViewScope.isEmpty()

context EditProduct :: onlyAbleToRateAttributesHaveValues() : Boolean body: let ableToRateAttributes : Set(Attribute) = self.product.attributeSet.attribute -> select(a|a.associatedProductType -> includes(self.productType)) in self.values->collect(a) -> forAll ( a | ableToRateAttributes -> includes(a) )

context EditProduct :: requiredAttributesHaveAValue() : Boolean body: let ableToRateAttributes : Set(Attribute) = self.product.attributeSet.attribute -> select(a|a.associatedProductType -> includes(self.productType)) in ableToRateAttributes -> forAll ( a | a.isRequired implies self.values->collect(a) -> includes(a) )

context EditProduct :: attributesHaveOnlyOneValue() : Boolean body:

self.values -> isUnique(attribute)

278 context EditProduct :: attributeValuesHaveTheCorrectType() : Boolean body: self.values -> forAll ( v | (v.a.oclIsTypeOf ( TextualAttribute ) implies v.v.oclIsTypeOf ( TextualValue )) and (v.a.oclIsTypeOf ( PriceAttribute ) implies v.v.oclIsTypeOf ( PriceValue )) and (v.a.oclIsTypeOf ( DateAttribute ) implies v.v.oclIsTypeOf ( DateValue )) and (v.a.oclIsTypeOf ( BooleanAttribute ) implies v.v.oclIsTypeOf ( BooleanValue )) and (v.a.oclIsTypeOf ( IntegerAttribute ) implies v.v.oclIsTypeOf ( IntegerValue )) and (v.a.oclIsTypeOf ( DecimalAttribute ) implies v.v.oclIsTypeOf ( DecimalValue )) and (v.a.oclIsTypeOf ( ImageAttribute ) implies v.v.oclIsTypeOf ( ImageValue )) and (v.a.oclIsTypeOf ( SingleValuedEnumerationAttribute ) implies (v.v.oclIsTypeOf ( EnumerationValue ) and v.a.oclAsType(SingleValuedEnumerationAttribute).allowedValue- >includes(v.v) )) and (v.a.oclIsTypeOf ( MultipleValuedEnumerationAttribute ) implies (v.v.oclIsTypeOf ( MultipleEnumerationValue ) and v.a.oclAsType(EnumerationValue).allowedValue- >includesAll(v.v.oclAsType(MultipleValuedEnumerationAttribute).enumerationValue) )) ) context EditProduct :: uniqueValuedAttributesHaveANonUsedValue() : Boolean body: self.values -> forAll ( v | v.a.hasUniqueValues implies not v.a.storeViewAttributeRating.attributeValue -> exists(val | val=v.v) ) context EditProduct :: onlyAbleToRatePropertiesHaveValues() : Boolean body: let conf : ProductPropertiesTypeAssociationConfiguration = ProductPropertiesTypeAssociationConfiguration.allInstances() -> any(true) in (self.name.isDefined() implies conf.nameAssociatedToProductType->includes(self.productType)) and (self.netPrice.isDefined() implies conf.netPriceAssociatedToProductType->includes(self.productType)) and (self.weight.isDefined() implies conf.weightAssociatedToProductType->includes(self.productType)) and (self.status.isDefined() implies conf.statusAssociatedToProductType->includes(self.productType)) and (self.isNewFrom.isDefined() implies conf.isNewFromAssociatedToProductType->includes(self.productType)) and (self.isNewUntil.isDefined() implies conf.isNewUntilAssociatedToProductType->includes(self.productType)) and (self.specialNetPrice.isDefined() implies conf.specialNetPriceAssociatedToProductType->includes(self.productType)) and (self.specialNetPriceFrom.isDefined() implies conf.specialNetPriceFromAssociatedToProductType->includes(self.productType)) and (self.specialNetPriceUntil.isDefined() implies conf.specialNetPriceUntilAssociatedToProductType->includes(self.productType)) and (self.description.isDefined() implies conf.descriptionAssociatedToProductType->includes(self.productType)) and (self.shortDescription.isDefined() implies conf.shortDescriptionAssociatedToProductType->includes(self.productType)) and (self.metaDescription.isDefined() implies conf.metaDescriptionAssociatedToProductType->includes(self.productType)) and (self.metaKeyword -> forAll(k|k.isDefined()) implies conf.metaKeywordAssociatedToProductType->includes(self.productType)) and (self.metaTitle.isDefined() implies conf.metaTitleAssociatedToProductType->includes(self.productType)) and (self.imageGalleryPath.isDefined() implies conf.imageGalleryPathAssociatedToProductType->includes(self.productType)) and (self.baseImagePath.isDefined() implies conf.baseImagePathAssociatedToProductType->includes(self.productType)) and (self.smallImagePath.isDefined() implies conf.smallImagePathAssociatedToProductType->includes(self.productType)) and (self.thumbnailPath.isDefined() implies conf.thumbnailPathAssociatedToProductType->includes(self.productType)) and (self.urlKey.isDefined() implies conf.urlKeyAssociatedToProductType->includes(self.productType)) and (self.isAvailableForGoogleCheckout.isDefined() implies conf.isAvailableForGoogleCheckoutAssociatedToProductType->includes(self.productType)) and (self.giftMessageAllowed.isDefined() implies conf.giftMessageAllowedAssociatedToProductType->includes(self.productType)) and (self.visibleOnCatalog.isDefined() implies conf.visibleOnCatalogAssociatedToProductType->includes(self.productType)) and (self.visibleOnSearch.isDefined() implies conf.visibleOnSearchAssociatedToProductType->includes(self.productType))

279 context EditProduct :: mandatoryPropertiesAreRated() : Boolean body: let confType : ProductPropertiesTypeAssociationConfiguration = ProductPropertiesTypeAssociationConfiguration.allInstances() -> any(true) in let confOblig : ProductPropertiesObligatorinessConfiguration = ProductPropertiesObligatorinessConfiguration.allInstances() -> any(true) in ((confOblig.isNewFromIsMandatory and confType.isNewFromAssociatedToProductType->includes(self.productType)) implies self.isNewFrom.isDefined()) and ((confOblig.isNewUntilIsMandatory and confType.isNewUntilAssociatedToProductType->includes(self.productType)) implies self.isNewUntil.isDefined()) and ((confOblig.specialNetPriceIsMandatory and confType.specialNetPriceAssociatedToProductType->includes(self.productType)) implies self.specialNetPrice.isDefined()) and ((confOblig.specialNetPriceFromIsMandatory and confType.specialNetPriceFromAssociatedToProductType->includes(self.productType)) implies self.specialNetPriceFrom.isDefined()) and ((confOblig.specialNetPriceUntilIsMandatory and confType.specialNetPriceUntilAssociatedToProductType->includes(self.productType)) implies self.specialNetPriceUntil.isDefined()) and ((confOblig.descriptionIsMandatory and confType.descriptionAssociatedToProductType->includes(self.productType)) implies self.description.isDefined()) and ((confOblig.shortDescriptionIsMandatory and confType.shortDescriptionAssociatedToProductType->includes(self.productType)) implies self.shortDescription.isDefined()) and ((confOblig.metaDescriptionIsMandatory and confType.metaDescriptionAssociatedToProductType->includes(self.productType)) implies self.metaDescription.isDefined()) and ((confOblig.metaKeywordIsMandatory and confType.metaKeywordAssociatedToProductType->includes(self.productType)) implies self.metaKeyword -> forAll(k|k.isDefined())) and ((confOblig.metaTitleIsMandatory and confType.metaTitleAssociatedToProductType->includes(self.productType)) implies self.metaTitle.isDefined()) and ((confOblig.imageGalleryPathIsMandatory and confType.imageGalleryPathAssociatedToProductType->includes(self.productType)) implies self.imageGalleryPath.isDefined()) and ((confOblig.baseImagePathIsMandatory and confType.baseImagePathAssociatedToProductType->includes(self.productType)) implies self.baseImagePath.isDefined()) and ((confOblig.smallImagePathIsMandatory and confType.smallImagePathAssociatedToProductType->includes(self.productType)) implies self.smallImagePath.isDefined()) and ((confOblig.thumbnailPathIsMandatory and confType.thumbnailPathAssociatedToProductType->includes(self.productType)) implies self.thumbnailPath.isDefined()) and ((confOblig.urlKeyIsMandatory and confType.urlKeyAssociatedToProductType->includes(self.productType)) implies self.urlKey.isDefined()) and ((confOblig.isAvailableForGoogleCheckoutIsMandatory and confType.isAvailableForGoogleCheckoutAssociatedToProductType->includes(self.productType)) implies self.isAvailableForGoogleCheckout.isDefined()) and ((confOblig.giftMessageAllowedIsMandatory and confType.giftMessageAllowedAssociatedToProductType->includes(self.productType)) implies self.giftMessageAllowed.isDefined()) and ((confOblig.visibleOnCatalogIsMandatory and confType.visibleOnCatalogAssociatedToProductType->includes(self.productType)) implies self.visibleOnCatalog.isDefined()) and ((confOblig.visibleOnSearchIsMandatory and confType.visibleOnSearchAssociatedToProductType->includes(self.productType)) implies self.visibleOnSearch.isDefined()) context EditProduct :: stockOptionsAreRatedOnlyForTheCorrectProductTypes() : Boolean body: ( self.productType=ProductType::Grouped or self.productType=ProductType::Configurable or self.productType=ProductType::Bundle ) implies ( self.quantity.isUndefined() and self.qtyToBecomeOutOfStock.isUndefined() and self.minQtyAllowedInShoppingCart.isUndefined() and self.maxQtyAllowedInShoppingCart.isUndefined() and self.notifyForQuantityBelow.isUndefined()and

280 self.backOrderPolicy.isUndefined() )

I Effect

context EditProduct::effect()

post productIsEdited: self.product.crossSellProduct = self.crossSellProduct and self.product.upSellProduct = self.upSellProduct and self.product.relatedProduct = self.relatedProduct and self.product.productTaxClass = self.productTaxClass and self.product.fixedTaxRate = self.fixedTaxRate and self.product.productInWebsite -> forAll (piw|piw.isAvailable = self.website->includes(piw.website))

post attributeRatingsAreEdited: self.values -> forAll ( v | if self.editForWebsiteScope.isDefined() then v.a.websiteAttributeRating->any(war| war.websiteRatedProduct=self.product and war.websiteWhereIsRated=self.editForWebsiteScope ).redefinedAttributeValue=v.v else if self.editForStoreViewScope.isDefined() then v.a.storeViewAttributeRating->any(sar| sar.product=self.storeViewRatedProduct and sar.storeViewWhereIsRated=self.editForStoreViewS- cope ).redefinedAttributeValue=v.v else v.a.globalAttributeRating->any(gar|gar.productOfGloballyRatedAttribute=self.product).genericAttribute- Value=v.v endif endif )

post propertyRatingsAreEdited: if self.editForWebsiteScope.isDefined() then let p : ProductInWebsite = self.product.productInWebsite->any(piw|piw.website=self.editForWebsiteScope) in p.redefinedName = self.name and p.redefinedNetPrice = self.netPrice and p.redefinedWeight = self.weight and p.redefinedStatus = self.status and p.redefinedIsNewFrom = self.isNewFrom and p.redefinedIsNewUntil = self.isNewUntil and p.redefinedSpecialNetPrice = self.specialNetPrice and p.redefinedSpecialNetPriceFrom = self.specialNetPriceFrom and p.redefinedSpecialNetPriceUntil = self.specialNetPriceUntil and p.redefinedDescription = self.description and p.redefinedShortDescription = self.shortDescription and p.redefinedMetaDescription = self.metaDescription and p.redefinedMetaKeyword = self.metaKeyword and p.redefinedMetaTitle = self.metaTitle and p.redefinedImageGalleryPath = self.imageGalleryPath and p.redefinedBaseImagePath = self.baseImagePath and p.redefinedSmallImagePath = self.smallImagePath and p.redefinedThumbnailPath = self.thumbnailPath and p.redefinedUrlKey = self.urlKey and p.redefinedIsAvailableForGoogleCheckout = self.isAvailableForGoogleCheckout and p.redefinedGiftMessageAllowed = self.giftMessageAllowed and p.redefinedVisibleOnCatalog = self.visibleOnCatalog and p.redefinedVisibleOnSearch = self.visibleOnSearch else if self.editForStoreViewScope.isDefined() then let p : ProductInStoreView = self.product.productInStoreView->any(pis|pis.storeView=self.editForStore- ViewScope) in p.redefinedName = self.name and p.redefinedWeight = self.weight and p.redefinedStatus = self.status and p.redefinedIsNewFrom = self.isNewFrom and p.redefinedIsNewUntil = self.isNewUntil and p.redefinedSpecialNetPriceFrom = self.specialNetPriceFrom and p.redefinedSpecialNetPriceUntil = self.specialNetPriceUntil and p.redefinedDescription = self.description and p.redefinedShortDescription = self.shortDescription and p.redefinedMetaDescription = self.metaDescription and

281 p.redefinedMetaKeyword = self.metaKeyword and p.redefinedMetaTitle = self.metaTitle and p.redefinedImageGalleryPath = self.imageGalleryPath and p.redefinedBaseImagePath = self.baseImagePath and p.redefinedSmallImagePath = self.smallImagePath and p.redefinedThumbnailPath = self.thumbnailPath and p.redefinedUrlKey = self.urlKey and p.redefinedIsAvailableForGoogleCheckout = self.isAvailableForGoogleCheckout and p.redefinedGiftMessageAllowed = self.giftMessageAllowed and p.redefinedVisibleOnCatalog = self.visibleOnCatalog and p.redefinedVisibleOnSearch = self.visibleOnSearch and let piw:ProductInWebsite = self.product.productInWebsite->any(piw|piw.website=self.editForStoreViewScope.store.- website) in piw.redefinedNetPrice = self.netPrice and piw.redefinedSpecialNetPrice = self.specialNetPrice else self.product.genericName = self.name and self.product.genericNetPrice = self.netPrice and self.product.genericWeight = self.weight and self.product.genericStatus = self.status and self.product.genericIsNewFrom = self.isNewFrom and self.product.genericIsNewUntil = self.isNewUntil and self.product.genericSpecialNetPrice = self.specialNetPrice and self.product.genericSpecialNetPriceFrom = self.specialNetPriceFrom and self.product.genericSpecialNetPriceUntil = self.specialNetPriceUntil and self.product.genericDescription = self.description and self.product.genericShortDescription = self.shortDescription and self.product.genericMetaDescription = self.metaDescription and self.product.genericMetaKeyword = self.metaKeyword and self.product.genericMetaTitle = self.metaTitle and self.product.genericImageGalleryPath = self.imageGalleryPath and self.product.genericBaseImagePath = self.baseImagePath and self.product.genericSmallImagePath = self.smallImagePath and self.product.genericThumbnailPath = self.thumbnailPath and self.product.genericUrlKey = self.urlKey and self.product.genericIsAvailableForGoogleCheckout = self.isAvailableForGoogleCheckout and self.product.genericGiftMessageAllowed = self.giftMessageAllowed and self.product.genericVisibleOnCatalog = self.visibleOnCatalog and self.product.genericVisibleOnSearch = self.visibleOnSearch endif endif and self.product.sku = self.sku and self.product.stockStatus = self.stockStatus and self.product.quantity = self.quantity and self.product.qtyToBecomeOutOfStock = self.qtyToBecomeOutOfStock and self.product.minQtyAllowedInShoppingCart = self.minQtyAllowedInShoppingCart and self.product.maxQtyAllowedInShoppingCart = self.maxQtyAllowedInShoppingCart and self.product.notifyForQuantityBelow = self.notifyForQuantityBelow and self.product.backOrderPolicy = self.backOrderPolicy

Event 7.6.3 DeleteProduct

From use case: Delete a product

I Event Specification Generalizations Relationships inherited from ExistingProductEvent product : Product [1]

I Effect

context DeleteProduct::effect()

282 post: not [email protected](OclAny) and [email protected] -> forAll ( t | not [email protected](OclAny) ) and [email protected] -> forAll ( t | not [email protected](OclAny) ) and [email protected] -> forAll ( o | not [email protected](OclAny) ) and ([email protected](DownloadableProduct) implies [email protected](DownloadableProduct).downloadableItem -> forAll ( i | not [email protected](OclAny) )) and ([email protected](BundleProduct) implies [email protected](BundleProduct).bundleItem -> forAll ( i | not [email protected](OclAny) )) and

Event 7.6.4 MoveCategory

From use case: Move a product category

I Event Specification Relationships categoryToMove : Category [1] newParentCategory : Category [0..1]

I Initial Integrity Constraints

context MoveCategory :: categoriesAreNotTheSame() : Boolean body:

not self.categoryToMove = self.newParentCategory

I Effect

context MoveCategory::effect()

post:

if self.newParentCategory.isDefined() then self.categoryToMove.parent = self.newParentCategory else self.cate- goryToMove.parent->isEmpty() endif

Event 7.6.5 NewAttribute

From use case: Add an attribute

I Event Specification Attributes attributeType : AttributeType [1] code : String [1]

283 name : String [1] scope : Scope [1] isComparable : Boolean [1] hasUniqueValues : Boolean [0..1] isRequired : Boolean [0..1] associatedProductType : ProductType [1..*] inputValidation : InputValidation [0..1] isConfigurable : Boolean [0..1]

Relationships attributeSet : AttributeSet [1] allowedValue : EnumerationValue [*] defaultValue : AttributeValue [0..1]

I Initial Integrity Constraints

context NewAttribute :: allowedEnumerationValuesAreOnlyDefinedForEnumerationAttributes() : Boolean body: self.allowedValue -> notEmpty() = (self.attributeType = AttributeType::SingleValuedEnumeration or self.attributeType = AttributeType::MultipleValuedEnumeration)

context NewAttribute :: isConfigurablePropertyIsOnlyDefinedForSingleValuedEnumerationAttributes() : Boolean body:

self.allowedValue -> notEmpty() = self.attributeType = AttributeType::SingleValuedEnumeration

context NewAttribute :: inputValidationIsOnlyDefinedForTextualAttributes() : Boolean body:

self.inputValidation.isDefined() = self.attributeType = AttributeType::Textual

context NewAttribute :: requiredAndUniqueAreOnlyDefinedForNonImageAttributes() : Boolean body: (self.isRequired.isDefined() and self.hasUniqueValues.isDefined()) = (self.attributeType <> AttributeType::Image)

context NewAttribute :: defaultValueIsNotDefinedForImageAttributes() : Boolean body:

self.attributeType = AttributeType::Image implies self.defaultValue.isDefined()

context NewAttribute :: defaultValueHaveTheCorrectType() : Boolean body: self.attributeType = AttributeType::Textual implies self.defaultValue.oclIsTypeOf ( TextualValue ) and self.attributeType = AttributeType::Price implies self.defaultValue.oclIsTypeOf ( PriceValue ) and self.attributeType = AttributeType::Date implies self.defaultValue.oclIsTypeOf ( DateValue ) and self.attributeType = AttributeType::Boolean implies self.defaultValue.oclIsTypeOf ( BooleanValue ) and self.attributeType = AttributeType::Integer implies self.defaultValue.oclIsTypeOf ( IntegerValue ) and self.attributeType = AttributeType::Decimal implies self.defaultValue.oclIsTypeOf ( DecimalValue ) and self.attributeType = AttributeType::SingleValuedEnumeration implies self.defaultValue.oclIsTypeOf ( EnumerationVa- lue ) and self.attributeType = AttributeType::MultipleValuedEnumeration implies self.defaultValue.oclIsTypeOf ( MultipleEnu- merationValue )

I Effect

context NewAttribute::effect()

post: a.oclIsNew() and a.oclIsKindOf(Attribute) and self.attributeType = AttributeType::Textual implies a.oclIsType- Of(TextualAttribute) and

284 self.attributeType = AttributeType::Price implies a.oclIsTypeOf(PriceAttribute) and self.attributeType = AttributeType::Date implies a.oclIsTypeOf(DateAttribute) and self.attributeType = AttributeType::Boolean implies a.oclIsTypeOf(BooleanAttribute) and self.attributeType = AttributeType::Integer implies a.oclIsTypeOf(IntegerAttribute) and self.attributeType = AttributeType::Decimal implies a.oclIsTypeOf(DecimalAttribute) and self.attributeType = AttributeType::Image implies a.oclIsTypeOf(ImageAttribute) and self.attributeType = AttributeType::SingleValuedEnumeration implies a.oclIsTypeOf(SingleValuedEnumerationAttribute) and self.attributeType = AttributeType::MultipleValuedEnumeration implies a.oclIsTypeOf(MultipleValuedEnumerationAttribute) and a.code = self.code and a.genericName = self.name and a.scope = self.scope and a.isComparable = self.isComparable and a.hasUniqueValues = self.hasUniqueValues and a.isRequired = self.isRequired and a.associatedProductType = self.associatedProductType and (self.attributeType = AttributeType::Textual implies a.oclAsType(TextualAttribute).inputValidation = self.inputVali- dation) and (self.attributeType = AttributeType::SingleValuedEnumeration implies a.oclAsType(SingleValuedEnumerationAttribute).- isConfigurable = self.isConfigurable) and ((self.attributeType = AttributeType::SingleValuedEnumeration or self.attributeType = AttributeType::SingleValuedEnumeration) implies a.oclAsType(EnumerationAttribute).allowedValue = self.allowedValue) and a.defaultValue = self.defaultValue and StoreView.allInstances() -> forAll ( stw | ais.oclIsNew() and ais.oclIsTypeOf(AttributeInStoreView) and ais.attribute = a and ais.storeView = stw ) and let productAbleToBeRated : Set(Product) = self.attributeSet.product -> select(p|a.associatedProductType -> includes(p.productType)) in productAbleToBeRated -> forAll ( p | gar.oclIsNew() and gar.oclIsTypeOf(GlobalAttributeRating) and gar.productOfGloballyRatedAttribute = p and gar.globallyRatedAttribute = a and Website.allInstances() -> forAll ( w | war.oclIsNew() and war.oclIsTypeOf(WebsiteAttributeRating) and war.websiteRatedProduct = p and war.websiteRatedAttribute = a and war.websiteWhereIsRated = w ) and StoreView.allInstances() -> forAll ( s | sar.oclIsNew() and sar.oclIsTypeOf(StoreViewAttributeRating) and sar.storeViewRatedProduct = p and sar.storeViewRatedAttribute = a and sar.storeViewWhereIsRated = s ))

Event 7.6.6 EditAttribute

From use case: Edit an attribute

I Event Specification Attributes attributeType : AttributeType [1] code : String [1] name : String [1] scope : Scope [1] isComparable : Boolean [1] hasUniqueValues : Boolean [0..1] isRequired : Boolean [0..1] associatedProductType : ProductType [1..*] inputValidation : InputValidation [0..1] isConfigurable : Boolean [0..1]

Relationships

285 attribute : Attribute [1] allowedValue : EnumerationValue [*] defaultValue : AttributeValue [0..1]

Generalizations Relationships inherited from ScopeDefinedEvent editForWebsiteScope : Website [0..1] editForStoreViewScope : StoreView [0..1]

I Initial Integrity Constraints

context EditAttribute :: allowedEnumerationValuesAreOnlyDefinedForEnumerationAttributes() : Boolean body: (self.attribute.oclIsTypeOf(SingleValuedEnumerationAttribute) or self.attribute.oclIsTypeOf(MultipleValuedEnumerationAttribute)) = self.attribute.oclAsType(EnumerationAttribute).allowedValue -> notEmpty()

context EditAttribute :: isConfigurablePropertyIsOnlyDefinedForSingleValuedEnumerationAttributes() : Boolean body:

self.allowedValue -> notEmpty() = self.attribute.oclIsTypeOf(SingleValuedEnumerationAttribute)

context EditAttribute :: inputValidationIsOnlyDefinedForTextualAttributes() : Boolean body:

self.inputValidation.isDefined() = self.attribute.oclIsTypeOf(TextualAttribute)

context EditAttribute :: requiredAndUniqueAreOnlyDefinedForNonImageAttributes() : Boolean body: (self.isRequired.isDefined() and self.hasUniqueValues.isDefined()) = (not self.attribute.oclIsTypeOf(ImageAttribute))

context EditAttribute :: defaultValueIsNotDefinedForImageAttributes() : Boolean body:

self.attribute.oclIsTypeOf(ImageAttribute) implies self.defaultValue.isDefined()

context EditAttribute :: defaultValueHaveTheCorrectType() : Boolean body: self.attribute.oclIsTypeOf(TextualAttribute) implies self.defaultValue.oclIsTypeOf ( TextualValue ) and self.attribute.oclIsTypeOf(PriceAttribute) implies self.defaultValue.oclIsTypeOf ( PriceValue ) and self.attribute.oclIsTypeOf(DateAttribute) implies self.defaultValue.oclIsTypeOf ( DateValue ) and self.attribute.oclIsTypeOf(BooleanAttribute) implies self.defaultValue.oclIsTypeOf ( BooleanValue ) and self.attribute.oclIsTypeOf(IntegerAttribute) implies self.defaultValue.oclIsTypeOf ( IntegerValue ) and self.attribute.oclIsTypeOf(DecimalAttribute) implies self.defaultValue.oclIsTypeOf ( DecimalValue ) and self.attribute.oclIsTypeOf(SingleValuedEnumerationAttribute) implies self.defaultValue.oclIsTypeOf ( EnumerationVa- lue )) and self.attribute.oclIsTypeOf(MultipleValuedEnumerationAttribute) implies self.defaultValue.oclIsTypeOf ( MultipleEnu- merationValue ))

I Effect

context EditAttribute::effect()

post: self.attribute.code = self.code and self.attribute.scope = self.scope and self.attribute.isComparable = self.isComparable and self.attribute.hasUniqueValues = self.hasUniqueValues and self.attribute.isRequired = self.isRequired and self.attribute.associatedProductType = self.associatedProductType and (self.attributeType = AttributeType::Textual implies self.attribute.oclAsType(TextualAttribute).inputValidation = self.- inputValidation) and (self.attributeType = AttributeType::SingleValuedEnumeration implies self.attribute.oclAsType(SingleValuedEnumerationAttribute).-

286 isConfigurable = self.isConfigurable) and ((self.attributeType = AttributeType::SingleValuedEnumeration or self.attributeType = AttributeType::SingleValuedEnumeration) implies self.attribute.oclAsType(EnumerationAttribute).allowedValue = self.allowedValue) and self.attribute.defaultValue = self.defaultValue and if self.editForStoreViewScope.isDefined() then self.attribute.attributeInStoreView->any( as|as.storeView=self.editForStoreViewScope ).redefinedName = self.name else self.attribute.genericName = self.name endif

Event 7.6.7 DeleteAttribute

From use case: Delete an attribute

I Event Specification Relationships attribute : Attribute [1]

I Effect

context DeleteAttribute::effect()

post:

not [email protected](OclAny) and

287 7.7 Additional Activities

Event 7.7.1 NewReview

From use case: Add a review

I Event Specification Attributes authorNickName : String [1] title : String [1] text : String [1] scores : TupleType (value: Rating, property: Property) [1..*]

Relationships product : Product [1]

Generalizations Relationships inherited from ExistingSessionEvent session : Session [1]

I Initial Integrity Constraints

context NewReview :: customerIsLoggedInIfGuestsAreNotAllowed() : Boolean body: self.session.storeView.store.website.CatalogConfigurationInWebsite.allowGuestsToWriteReviews or self.session.oclIsTypeOf(CustomerSession)

context NewReview :: scoresAllVisibleProperties() : Boolean body:

self.scores.property -> includesAll( self.session.storeView.propertyInStoreView->select(piw|piw.isVisible).proper- ty )

context NewReview :: reviewDoesNotExist() : Boolean body:

not Review.allInstances()->exists ( r | r.authorNickName = self.authorNickName and r.text = self.text )

I Effect

context NewReview::effect()

post: r.oclIsNew() and r.oclIsTypeOf(Review) and r.authorNickName = self.authorNickName and r.title = self.title and r.text = self.text and r.status = ReviewStatus::Pending and r.createdAt = Now() and r.product = self.product and self.session.oclIsTypeOf(CustomerSession) implies r.user = self.session.oclAsType(CustomerSession).customer and r.storeViewWhereIsVisible -> includes(self.session.storeView) and self.scores -> forAll ( scoreTuple |

288 s.oclIsNew() and s.oclIsTypeOf(PropertyScore) and s.property = scoreTuple.property and s.review = r and s.value = scoreTuple.value )

Event 7.7.2 NewAdministratorReview

From use case: Add a review

I Event Specification Attributes authorNickName : String [1] title : String [1] text : String [1] status : ReviewStatus [1] scores : TupleType (value: Rating, property: Property) [1..*]

Relationships product : Product [1] storeView : StoreView [1..*]

Generalizations Relationships inherited from ExistingAdministratorEvent administrator : Administrator [1]

I Initial Integrity Constraints

context NewAdministratorReview :: scoresAllVisibleProperties() : Boolean body:

self.scores.property -> includesAll( self.storeView.propertyInStoreView->select(piw|piw.isVisible).property -> as- Set() )

context NewAdministratorReview :: reviewDoesNotExist() : Boolean body:

not Review.allInstances()->exists ( r | r.authorNickName = self.authorNickName and r.text = self.text )

I Effect

context NewAdministratorReview::effect()

post: r.oclIsNew() and r.oclIsTypeOf(Review) and r.authorNickName = self.authorNickName and r.title = self.title and r.text = self.text and r.status = self.status and r.createdAt = Now() and r.product = self.product and r.user = self.administrator and r.storeViewWhereIsVisible = self.storeView and r.scores -> forAll ( scoreTuple | s.oclIsNew() and s.oclIsTypeOf(PropertyScore) and s.property = scoreTuple.property and

289 s.review = r and s.value = scoreTuple.value )

Event 7.7.3 AddProductToWishlist

From use case: Add a product to the wish list

I Event Specification Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1] Relationships inherited from ExistingProductEvent product : Product [1]

I Initial Integrity Constraints

context AddProductToWishlist :: customerIsLoggedIn() : Boolean body:

self.customer.customerSession -> notEmpty()

context AddProductToWishlist :: wishListIsEnabledInTheCurrentWebsite() : Boolean body:

self.customer.customerSession.storeView.store.website.wishlistConfigurationInWebsite.status = Status::Enabled

context AddProductToWishlist :: productIsAvailableAtTheCurrentWebsite() : Boolean body: self.customer.customerSession.storeView.store.website.productInWebsite->any( piw | piw.product = self.product ).isAvailable

context AddProductToWishlist :: productIsEnabledAtTheCurrentStoreView() : Boolean body: self.customer.customerSession.storeView.productInStoreView->any( pisw | pisw.product = self.product ).status = #Enabled

context AddProductToWishlist :: productIsNotAlreadyInWishList() : Boolean body: let currentWebsite : Website = self.customer.customerSession.storeView.store.website in self.customer.activityInfoOfCustomerInWebsite -> any ( ai | ai.websiteWithInfoAbout = currentWebsite ).- wishedProduct -> excludes(self.product)

I Effect

context AddProductToWishlist::effect()

post: w.oclIsNew and w.oclIsTypeOf(WishListItem) and w.addedOn = Now() and w.wishedProduct = self.product and w.activityInfoOfCustomerInWebsite = self.customer.activityInfoOfCustomerInWebsite

290 ->any( ai | ai.websiteWithInfoAbout = self.customer.customerSession.storeView.store.website )

Event 7.7.4 AddCommentToWishlistItem

From use case: Add a product to the wish list

I Event Specification Attributes newComment : String [1]

Generalizations Relationships inherited from ExistingWishListItemEvent wishListItem : WishListItem [1] Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context AddCommentToWishlistItem :: wishListItemBelongsToCustomer() : Boolean body:

self.wishListItem.activityInfoOfCustomerInWebsite.customerWithInfoAbout = self.customer

I Effect

context AddCommentToWishlistItem::effect()

post:

self.wishListItem.comment = self.newComment

Event 7.7.5 RemoveCommentFromWishlistItem

From use case: Add a product to the wish list

I Event Specification Generalizations Relationships inherited from ExistingWishListItemEvent wishListItem : WishListItem [1] Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

291 context RemoveCommentFromWishlistItem :: wishListItemBelongsToCustomer() : Boolean body:

self.wishListItem.activityInfoOfCustomerInWebsite.customerWithInfoAbout = self.customer

I Effect

context RemoveCommentFromWishlistItem::effect()

post:

self.wishListItem.comment -> isEmpty()

Event 7.7.6 RemoveProductFromWishlist

From use case: Remove a product from the wish list

I Event Specification Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1] Relationships inherited from ExistingProductEvent product : Product [1]

I Initial Integrity Constraints

context RemoveProductFromWishlist :: customerIsLoggedIn() : Boolean body:

self.customer.customerSession -> notEmpty()

context RemoveProductFromWishlist :: productIsInTheWishList() : Boolean body: let currentWebsite : Website = self.customer.customerSession.storeView.store.website in self.customer.activityInfoOfCustomerInWebsite->any( ai | ai.websiteWithInfoAbout = currentWebsite ).- wishedProduct -> includes(self.product)

I Effect

context RemoveProductFromWishlist::effect()

post: let currentWebsite : Website = self.customer.customerSession.storeView.store.website in self.customer.activityInfoOfCustomerInWebsite( ai | ai.websiteWithInfoAbout = currentWebsite ).wished- Product -> excludes(self.product)

292 Event 7.7.7 AddProductToCompareList

From use case: Add a product to the compare list

I Event Specification Generalizations Relationships inherited from ExistingSessionEvent session : Session [1] Relationships inherited from ExistingProductEvent product : Product [1]

I Initial Integrity Constraints

context AddProductToCompareList :: productIsAvailableAtTheCurrentWebsite() : Boolean body:

self.session.storeView.store.website.productInWebsite->any(piw|piw.product = self.product).isAvailable

context AddProductToCompareList :: productIsEnabledAtTheCurrentStoreView() : Boolean body:

self.session.storeView.productInStoreView->any(pisw|pisw.product = self.product).status = Status::Enabled

self.session.storeView.productInStoreView->any(pisw|pisw.product = self.product).status = Status::Enabled

context AddProductToCompareList :: productIsNotAlreadyInCompareList() : Boolean body: self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.storeView) .readyToCompareProduct -> excludes(self.product)

I Effect

context AddProductToCompareList::effect()

post: let listScope : Scope = self.session.storeView.store.website.catalogConfigurationInWebsite.recentlyViewedAndComparedProductsS- cope in if listScope = Scope::StoreView then self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.store- View) .explicitReadyToCompareProduct -> includes(self.product) else self.session.activityInfoOfSessionInWebsite->any( ai | ai.websiteWithInfoAbout = self.session.storeView.- store.website) .explicitReadyToCompareProduct -> includes(self.product) endif

293 Event 7.7.8 RemoveProductFromCompareList

From use case: Remove a product from the compare list

I Event Specification Generalizations Relationships inherited from ExistingSessionEvent session : Session [1] Relationships inherited from ExistingProductEvent product : Product [1]

I Initial Integrity Constraints

context RemoveProductFromCompareList :: productIsInCompareList() : Boolean body: self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.storeView) .readyToCompareProduct -> includes(self.product)

I Effect

context RemoveProductFromCompareList::effect()

post: self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.storeView) .readyToCompareProduct -> excludes(self.product)

post: let listScope : Scope = self.session.storeView.store.website.catalogConfigurationInWebsite.recentlyViewedAndComparedProductsS- cope in let comparedProducts : OrderedSet(Product) = if listScope = Scope::StoreView then self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.- storeView) .explicitRecentlyComparedProduct else self.session.activityInfoOfSessionInWebsite->any( ai | ai.websiteWithInfoAbout = self.session.store- View.store.website) .explicitRecentlyComparedProduct endif in comparedProducts -> first() = self.product and ( comparedProducts@pre -> size() = self.session.storeView.catalogConfigurationInStoreView.numberOfRecently- ComparedProductsSaved implies comparedProducts -> excludes(comparedProducts@pre -> last()) )

Event 7.7.9 SingUpForPriceAlert

From use case: Subscribe to a product price alert

I Event Specification

294 Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1] Relationships inherited from ExistingProductEvent product : Product [1]

I Initial Integrity Constraints

context SingUpForPriceAlert :: customerIsLoggedIn() : Boolean body:

self.customer.customerSession -> notEmpty()

context SingUpForPriceAlert :: productIsAvailableAtTheCurrentWebsite() : Boolean body: self.customer.customerSession.storeView.store.website.productInWebsite->any( piw | piw.product = self.product ).isAvailable

context SingUpForPriceAlert :: productIsEnabledAtTheCurrentStoreView() : Boolean body: self.customer.customerSession.storeView.productInStoreView->any( pisw | pisw.product = self.product ).status = #Enabled

context SingUpForPriceAlert :: priceAlertIsAllowed() : Boolean body:

self.customer.customerSession.storeView.catalogConfigurationInStoreView.allowPriceAlert

I Effect

context SingUpForPriceAlert::effect()

post: self.customer.activityInfoOfCustomerInWebsite->any( ai | ai.websiteWithInfoAbout = self.customer.custom- erSession.storeView.store.website) .productSubscribedForPriceAlert -> includes(self.product)

Event 7.7.10 SingUpForStockAlert

From use case: Subscribe to a product stock alert

I Event Specification Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1] Relationships inherited from ExistingProductEvent product : Product [1]

I Initial Integrity Constraints

295 context SingUpForStockAlert :: customerIsLoggedIn() : Boolean body:

self.customer.customerSession -> notEmpty()

context SingUpForStockAlert :: productIsAvailableAtTheCurrentWebsite() : Boolean body: self.customer.customerSession.storeView.store.website.productInWebsite->any( piw | piw.product = self.product ).isAvailable

context SingUpForStockAlert :: productIsEnabledAtTheCurrentStoreView() : Boolean body: self.customer.customerSession.storeView.productInStoreView->any( pisw | pisw.product = self.product ).status = #Enabled

context SingUpForStockAlert :: stockAlertIsAllowed() : Boolean body:

self.customer.customerSession.storeView.store.website.catalogConfigurationInWebsite.allowStockAlert

I Effect

context SingUpForStockAlert::effect()

post: self.customer.activityInfoOfCustomerInWebsite->any( ai | ai.websiteWithInfoAbout = self.customer.custom- erSession.storeView.store.website) .productSubscribedForStockAlert -> includes(self.product)

Event 7.7.11 SignUpForNewsletter

From use case: Subscribe to the newsletter

I Event Specification Attributes eMail : EMail [0..1]

Generalizations Relationships inherited from ExistingSessionEvent session : Session [1]

I Initial Integrity Constraints

context SignUpForNewsletter :: sessionIsFromCustomerOrAnEmailIsProvided() : Boolean body:

self.session.oclIsTypeOf(CustomerSession) or self.eMail.isDefined()

context SignUpForNewsletter :: anonymousSubscriptionDoesNotExistIfCurrentSessionIsAnonymous() : Boolean body:

self.session.oclIsTypeOf(AnonymousSession) implies not AnonymousNewsletterSubscription.allInstances() -> ex- ists ( s | s.storeView = self.session.storeView and s.providedEMail = self.eMail )

296 I Effect

context SignUpForNewsletter::effect()

post: if self.session.oclIsTypeOf(CustomerSession) then if self.session.oclAsType(CustomerSession)[email protected] -> exists( s | s.storeView = self.session.storeView ) then let subs : CustomerNewsletterSubscription = self.session.oclAsType(CustomerSession).customer.customerNewsletterSubscription -> any( s | s.- storeView = self.session.storeView ) in subs.status = Status::Enabled and subs.providedEMail = self.eMail else if [email protected]() -> exists ( s | s.storeView = self.session.storeView and s.providedEMail = self.eMail ) then let subs : AnonymousNewsletterSubscription = [email protected]() -> any ( s | s.storeView = self.session.store- View and s.providedEMail = self.eMail ) in subs.oclIsTypeOf(CustomerNewsletterSubscription) and subs.customer = self.session.oclAsType(CustomerSession).customer else s.oclIsNew() and s.oclIsTypeOf(CustomerNewsletterSubscription) and s.customer = self.session.oclAsType(CustomerSession).customer and s.providedEMail = self.eMail s.status = Status::Enabled and s.storeView = self.session.storeView endif endif else s.oclIsNew() and s.oclIsTypeOf(AnonymousNewsletterSubscription) and s.providedEMail = self.eMail and s.status = Status::Enabled and s.storeView = self.session.storeView endif

Event 7.7.12 SignDownFromNewsletter

From use case: Unsubscribe from the newsletter

I Event Specification Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context SignDownFromNewsletter :: customerIsLoggedIn() : Boolean body:

self.customer.customerSession -> notEmpty()

context SignDownFromNewsletter :: customerHasAnEnabledSubscription() : Boolean body: self.customer.customerNewsletterSubscription -> exists ( s | s.storeView = self.customer.customerSession.storeView and s.status = Status::Enabled )

I Effect

297 context SignDownFromNewsletter::effect()

post: self.customer.customerNewsletterSubscription -> any ( s | s.storeView = self.customer.customerSession.storeView ).status = Status::Disabled

Event 7.7.13 TellToAFriendUsed

From use case: Tell to a friend

I Event Specification Generalizations Relationships inherited from ExistingSessionEvent session : Session [1]

I Initial Integrity Constraints

context TellToAFriendUsed :: tellToAFriendIsEnabled() : Boolean body:

self.session.storeView.tellToAFriendConfigurationInStoreView.status = Status::Enabled

context TellToAFriendUsed :: sessionBelongsToCustomerOrGuestIsEnabled() : Boolean body:

self.session.storeView.tellToAFriendConfigurationInStoreView.guestStatus = Status::Enabled or self.session.oclIsType- Of(CustomerSession)

context TellToAFriendUsed :: maxUsesNotReached() : Boolean body: let uses:Integer= self.session.activityInfoOfSessionInStoreView->any(ai|ai.storeViewWithInfoAbout=self.session.storeView) .usesOfTellToAFriendInLastHour in uses < self.session.storeView.tellToAFriendConfigurationInStoreView.maxUsesPerHour

I Effect

context TellToAFriendUsed::effect()

post: let uses:Integer= self.session.activityInfoOfSessionInStoreView->any(ai|ai.storeViewWithInfoAbout = self.session.storeView).- usesOfTellToAFriendInLastHour in uses = uses@pre +1

298 Event 7.7.14 ProductViewed

From use case: View product information, Place an Order

I Event Specification Generalizations Relationships inherited from ExistingSessionEvent session : Session [1] Relationships inherited from ExistingProductEvent product : Product [1]

I Initial Integrity Constraints

context ProductViewed :: productIsAvailableAtTheCurrentWebsite() : Boolean body:

self.session.storeView.store.website.productInWebsite->any(piw|piw.product = self.product).isAvailable

context ProductViewed :: productIsEnabledAtTheCurrentStoreView() : Boolean body:

self.session.storeView.productInStoreView->any(pisw|pisw.product = self.product).status = Status::Enabled

context ProductViewed :: productIsNotAlreadyInRecentlyViewedList() : Boolean body: self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.storeView) .recentlyViewedProduct -> excludes(self.product)

I Effect

context ProductViewed::effect()

post: let listScope : Scope = self.session.storeView.store.website.catalogConfigurationInWebsite.recentlyViewedAndComparedProductsS- cope in let viewedProducts : OrderedSet(Product) = if listScope = Scope::StoreView then self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.- storeView) .explicitRecentlyViewedProduct else self.session.activityInfoOfSessionInWebsite->any( ai | ai.websiteWithInfoAbout = self.session.store- View.store.website) .explicitRecentlyViewedProduct endif in viewedProducts -> first() = self.product and ( viewedProducts@pre -> size() = self.session.storeView.catalogConfigurationInStoreView.numberOfRecentlyViewed- ProductsSaved implies viewedProducts -> excludes(viewedProducts@pre -> last()) )

299 Event 7.7.15 ResetTemporalInfo

From use case: Reset the temporal information

I Event Specification

I Effect context ResetTemporalInfo::effect() post: ActivityInfoOfSessionInStoreView.usesOfTellToAFriendInLastHour = 0 and ActivityInfoOfCustomerInStoreView.usesOfTellToAFriendInLastHour = 0

300 7.8 Online catalog

Event 7.8.1 ProductViewed

From use case: View product information, Place an Order

I Event Specification Generalizations Relationships inherited from ExistingSessionEvent session : Session [1] Relationships inherited from ExistingProductEvent product : Product [1]

I Initial Integrity Constraints

context ProductViewed :: productIsAvailableAtTheCurrentWebsite() : Boolean body:

self.session.storeView.store.website.productInWebsite->any(piw|piw.product = self.product).isAvailable

context ProductViewed :: productIsEnabledAtTheCurrentStoreView() : Boolean body:

self.session.storeView.productInStoreView->any(pisw|pisw.product = self.product).status = Status::Enabled

context ProductViewed :: productIsNotAlreadyInRecentlyViewedList() : Boolean body: self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.storeView) .recentlyViewedProduct -> excludes(self.product)

I Effect

context ProductViewed::effect()

post: let listScope : Scope = self.session.storeView.store.website.catalogConfigurationInWebsite.recentlyViewedAndComparedProductsS- cope in let viewedProducts : OrderedSet(Product) = if listScope = Scope::StoreView then self.session.activityInfoOfSessionInStoreView->any( ai | ai.storeViewWithInfoAbout = self.session.- storeView) .explicitRecentlyViewedProduct else self.session.activityInfoOfSessionInWebsite->any( ai | ai.websiteWithInfoAbout = self.session.store- View.store.website) .explicitRecentlyViewedProduct endif in viewedProducts -> first() = self.product and ( viewedProducts@pre -> size() = self.session.storeView.catalogConfigurationInStoreView.numberOfRecentlyViewed- ProductsSaved implies viewedProducts -> excludes(viewedProducts@pre -> last()) )

301 Event 7.8.2 AddProductToShoppingCart

From use case: Place an order

I Event Specification Attributes quantity : PositiveInteger [1] textOption : TupleType(o:TextOption,text:String) [*] dateOption : TupleType(o:DateOption,date:Date) [*]

Relationships optionValue : OptionValueInOption [*]

Generalizations Relationships inherited from ExistingProductEvent product : Product [1] Relationships inherited from ExistingShoppingCartEvent shoppingCart : ShoppingCart [1]

I Initial Integrity Constraints

context AddProductToShoppingCart :: productIsAvailableAtTheCurrentWebsite() : Boolean body: self.shoppingCart.currentStoreView.store.website.productInWebsite->any(piw| piw.product = self.product ).isAvailable

context AddProductToShoppingCart :: productIsEnabledAtTheCurrentStoreView() : Boolean body: self.shoppingCart.currentStoreView.productInStoreView->any(pisw| pisw.product = self.product ).status = Status::Enabled

context AddProductToShoppingCart :: productIsInStock() : Boolean body:

self.product.stockStatus = StockStatus::InStock

context AddProductToShoppingCart :: optionsAreFromTheProduct() : Boolean body: self.product.option -> includesAll(self.textOption->collect(o)) and self.product.option -> includesAll(self.dateOption->collect(o)) and self.product.option->select(o| o.oclIsTypeOf(OptionWithPredefinedContent) ).oclAsType(OptionWithPredefinedContent).optionValueInOption -> includesAll(self.optionValue)

context AddProductToShoppingCart :: optionsAreRatedOnlyOnce() : Boolean body: self.textOption -> isUnique(o) and self.dateOption -> isUnique(o) and self.optionValue -> isUnique(optionWithPredefinedContent)

context AddProductToShoppingCart :: optionIsRatedWhenIsRequired() : Boolean body: self.product.option -> select(o|o.isRequired) -> forAll ( o | if o.oclIsTypeOf(TextOption) then self.textOption->collect(o) -> includes(o.oclAsType(TextOption)) else if o.oclIsTypeOf(DateOption) then

302 self.dateOption->collect(o) -> includes(o.oclAsType(DateOption)) else self.optionValue.optionWithPredefinedContent -> includes(o) endif endif )

I Effect

context AddProductToShoppingCart::effect()

post addProduct: let productExists: Boolean = let existingItem : ShoppingCartItem = [email protected]@pre -> any(i|i.product=self.product) in existingItem.isDefined() and existingItem.optionValueInOption = self.optionValue and existingItem.textOptionRating -> collect(r| Tupleo=r.textOption,text=r.value) = self.textOption and existingItem.dateOptionRating -> collect(r| Tupleo=r.textOption,date=r.value) = self.dateOption in if productExists then let i = self.shoppingCart.shoppingCartItem -> any( i | i.product = self.product ) in i.quantity = [email protected] quantity + self.quantity else i.oclIsNew() and i.oclIsTypeOf(ShoppingCartItem) and i.shoppingCart = self.shoppingCart and i.product = self.product and i.quantity = self.quantity and i.applyDiscount = true and self.textOption -> forAll(tupleOpt| r.oclIsNew() and r.oclIsTypeOf(TextOptionRating) and r.textOption = tupleOpt.o and r.value = tupleOpt.text and r.shoppingCartItem = i ) and self.dateOption -> forAll(tupleOpt| r.oclIsNew() and r.oclIsTypeOf(DateOptionRating) and r.dateOption = tupleOpt.o and r.value = tupleOpt.date and r.shoppingCartItem = i ) and i.optionValueInOption = self.optionValue endif

post decreaseQuantity: StockConfiguration.allInstances->any(true).decreaseStockWhenOrderIsPlaced implies (self.product.quantity = [email protected]@pre - self.quantity)

Event 7.8.3 UpdateShoppingCart

From use case: Place an order

I Event Specification Attributes lineChange : TupleType (remove: Boolean, quantity: PositiveInteger, customPrice: Money, applyDis- count: Boolean, item: ShoppingCartItem) [1..*]

Generalizations Relationships inherited from ExistingShoppingCartEvent shoppingCart : ShoppingCart [1]

I Initial Integrity Constraints

context UpdateShoppingCart :: itemsAreUpdatedOnlyOnce() : Boolean

303 body:

self.lineChange -> isUnique(item)

I Effect

context UpdateShoppingCart::effect()

post: self.lineChange -> forAll ( lc | let cartItem : ShoppingCartItem = self.shoppingCart.shoppingCartItem->any(i|i=lc.item) in if lc.remove then not [email protected](OclAny) else lc.quantity = cartItem.quantity and self.shoppingCart.oclIsTypeOf(AdministrationShoppingCart) im- plies ( cartItem.customPrice = lc.customPrice and cartItem.applyDiscount = lc.applyDiscount ) endif )

Event 7.8.4 ApplyCouponCode

From use case: Place an order, Add an order

I Event Specification Attributes newCouponCode : String [1]

Generalizations Relationships inherited from ExistingShoppingCartEvent shoppingCart : ShoppingCart [1]

I Effect

context ApplyCouponCode::effect()

post:

self.shoppingCart.couponCode = self.newCouponCode

Event 7.8.5 OrderConfirmation

From use case: Place an order, Add an order

I Event Specification

304 Attributes comments : TupleType (text: String, notifyCustomer:Boolean) [*] creditCardInfo : TupleType (type: String, owner: String, number: String, expires: Date, verification: String) [0..1] eMailSent : Boolean [1]

Relationships shoppingCart : ShoppingCart [1] delivery : Address [1] billing : Address [1] shippingMethod : ShippingMethod [1] paymentMethod : PaymentMethod [1]

I Initial Integrity Constraints

context OrderConfirmation :: guestCheckoutIsEnabledIfNeeded() : Boolean body: (self.shoppingCart.shoppingCartItem.product -> exists (p|p.oclIsTypeOf(DownloadableProduct)) and self.- shoppingCart.currentStoreView.store.website.catalogConfigurationInWebsite.allowGuestCheckoutForDownloadableIt- ems) implies (not self.shoppingCart.oclIsTypeOf(AnonymousShoppingCart))

context OrderConfirmation :: shippingMethodIsEnabledAtTheCurrentWebsite() : Boolean body: let sh:ShippingMethodInWebsite = self.shoppingCart.currentStoreview.store.website. shippingMethodInWebsite->any(sw|sw.shippingMethod = self.shippingMethod) in sh.status = Status::Enabled

context OrderConfirmation :: allowsFreeShippingOnlyForAMinimumAmount() : Boolean body: let sh:ShippingMethodInWebsite = self.shoppingCart.currentStoreview.store.website. shippingMethodInWebsite->any(sw|sw.shippingMethod = self.shippingMethod) in sh.oclIsTypeOf(FreeShippingInWebsite) implies self.shoppingCart.total >= sh.oclAsType(FreeShippingInWebsite).minimumOrderAmount

context OrderConfirmation :: paymentMethodIsEnabledAtTheCurrentWebsite() : Boolean body: let selectedMethodConfiguration : PaymentMethodInWebsite = self.shoppingCart.currentStoreView.store.website.paymentMethodInWebsite->any(sw|sw.paymentMethod = self.paymentMethod) in selectedMethodConfiguration.status = Status::Enabled and selectedMethodConfiguration.minimumAllowed <= self.shoppingCart.total and selectedMethodConfiguration.maximumAllowed >= self.shoppingCart .total

context OrderConfirmation :: creditCardDetailsAreThereIfNeeded() : Boolean body:

self.paymentMethod.oclIsKindOf(CreditCardMethod) implies self.creditCardInfo.isDefined()

I Effect

context OrderConfirmation::effect()

post theOrderIsCreated: let taxConf : TaxConfigurationInWebsite = [email protected]

305 figurationInWebsite in let wbCurrency : Currency = [email protected]figurationInWeb- site.baseCurrency in let purchCurrency : Currency = [email protected] in o.oclIsNew() and o.oclIsTypeOf(Order) and o.couponCode = [email protected] and o.eMailSent = self.eMailSent and o.giftMessage = [email protected] and o.storeView = [email protected] and o.customerGroup = [email protected] and o.billing = self.billing and o.delivery = self.delivery and o.shippingMethod = self.shippingMethod and o.paymentMethod = self.paymentMethod and o.status = OrderStatus::Pending and self.shoppingCart.oclIsTypeOf(CustomerShoppingCart) implies o.customer = [email protected](CustomerShoppingCart).customer@pre and self.shoppingCart.oclIsTypeOf(AdministrationShoppingCart) implies o.customer = [email protected](AdministrationShoppingCart).customer@pre and o.rateApplicationAddress = if taxConf.usedAddress = TaxCalculatingAddress::Billing then o.billing else if taxConf.usedAddress = TaxCalculatingAddress::Delivery then o.delivery else taxConf.website.shippingConfigurationInWebsite.shippingOrigin endif endif and o.genericBaseCurrency = CurrencyConfiguration.allInstances()->any(true).genericBaseCurrency and wbCurrency <> o.genericBaseCurrency implies (u.oclIsNew and u.oclIsTypeOf(UseOfWebsiteRedefinedBaseCurrency) and u.orderOfRedefinedBaseCurrency = o and u.websiteRedefinedBaseCurrency = wbCurrency and u.initialRate = CurrencyRate.allInstances->any(r| r.baseCurrency=o.genericBaseCurrency and r.currency=wbCurrency ).rate) and (purchCurrency <> o.genericBaseCurrency and purchCurrency <> o.websiteRedefinedBaseCurrency) implies (u.oclIsNew and u.oclIsTypeOf(UseOfPurchasingCurrency) and u.orderOfPurchasingCurrency = o and u.purchasingCurrency = purchCurrency and u.initialRate = CurrencyRate.allInstances->any(r| r.baseCurrency=o.genericBaseCurrency and r.currency=purchCurrency ).rate) and self.comments.forAll ( tupleCom | c.oclIsNew() and c.oclIsTypeOf(Comment) and c.text = tupleCom.text and c.notifyCustomer = tupleCom.notifyCustomer and c.createdAt = Now() and c.status = CommentStatus::Pending and o.comment -> includes(c) ) and [email protected]@pre -> forAll ( i | ol.oclIsNew() and ol.oclIsTypeOf(OrderLine) and ol.order = o and ol.productSku = i.product.sku and ol.productName = i.product.productInStoreView -> any(pw|pw.storeView = o.storeView).name and ol.quantity = i.quantity and ol.giftMessage = i.giftMessage and ol.price = i.price and if i.applyDiscount then let discountBasis: Money = if self.shoppingCart.currentStoreView.store.website.taxConfigurationInWebsite.methodRespectingDis- count = TaxRespectingDiscountsMethod::TaxAfterDiscount and self.shoppingCart.currentStoreView.store.website.taxConfigurationInWebsite.applyDiscountIncludingTax then ol.price * ol.quantity + ol.tax else ol.price * ol.quantity endif in ol.discount = i.calculateDiscount( discountBasis, i.appliedShoppingCartPriceRule ) else ol.discount = 0.0 endif and let appliedTaxRate : Bag(TaxRate) = TaxRule.allInstances() -> select ( t | TaxRule.allInstances() -> forAll ( t2 | t2.priority <= t.priority ) ) ->select ( t | t.customerTaxClass -> includes(o.customerGroup.customerTaxClass) and t.productTaxClass -> in- cludes(i.product) ) -> collect ( t | t.taxRate ) -> select ( tr | tr.applicableAddress -> includes(o.rateApplicationAddress) ) -> union ( i.product.fixedTaxRate ) in

306 ol.tax = (appliedTaxRate -> collect ( a | a.rate * i.price * i.quantity / 100 ) -> sum()) and ol.total = (i.total + ol.tax) and ol.textOptions = TextOptionInfo.allInstances() -> select (inf| i.textOption.genericName -> includes(inf.optionName) and i.textOptionRating.value -> includes(inf.value) ) and ol.dateOptions = DateOptionInfo.allInstances() -> select (inf| i.dateOption.genericName -> includes(inf.optionName) and i.dateOptionRating.value -> includes(inf.value) ) and ol.predefinedContentOptions = PredefinedContentOptionInfo.allInstances() -> select (inf| i.optionValueInOption.optionWithPredefinedContent.genericName -> includes(inf.optionName) and i.optionValueInOption.optionValue.genericName -> includes(inf.valueName) ) )

post theShoppingCartItemsAreRemoved:

self.shoppingCart.shoppingCartItem -> isEmpty()

post theShoppingCartIsRemovedIfItIsAdministration:

[email protected](AdministrationShoppingCart) implies (not [email protected] Of(OclAny))

post updateProductQuantities: StockConfiguration.decreaseStockWhenOrderIsPlaced implies [email protected]@pre -> forAll ( i | i.product.quantity = i.product.quantity@pre - i.quantity )

Event 7.8.6 DuplicateAccount

From use case: Add an order

I Event Specification Relationships website : Website [1]

Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1]

I Initial Integrity Constraints

context DuplicateAccount :: customerIsNotVisibleInWebsite() : Boolean body:

not self.website.visibleCustomer -> includes(self.customer)

I Effect

context DuplicateAccount::effect()

post: c.oclIsNew() and

307 c.oclIsTypeOf(Customer) and c.eMail = self.customer.eMail and c.firstName = self.customer.firstName and c.lastName = self.customer.lastName and c.password = self.customer.password and c.namePrefix = self.customer.namePrefix and c.middleName = self.customer.middleName and c.nameSuffix = self.customer.nameSuffix and c.dateOfBirth = self.customer.dateOfBirth and c.taxVatNumber = self.customer.taxVatNumber and c.createdAt = Now() and c.websiteWhereIsAssociated = self.website and c.customerGroup = CustomerGroup.allInstances() -> any(cg|cg.name=’General’) and c.address = self.customer.address and c.defaultBilling = self.customer.defaultBilling and c.defaultDelivery = self.customer.defaultDelivery

Event 7.8.7 NewAdministrationShoppingCart

From use case: Add an order

I Event Specification Generalizations Relationships inherited from ExistingCustomerEvent customer : Customer [1] Relationships inherited from ExistingAdministratorEvent administrator : Administrator [1] Relationships inherited from StoreViewPlacedEvent storeView : StoreView [1]

I Effect

context NewAdministrationShoppingCart::effect()

post: sc.oclIsNew() and sc.oclIsTypeOf(AdministrationShoppingCart) and sc.customer = self.customer and sc.administrator = self.administrator and sc.storeView = self.storeView and sc.currency = self.storeView.store.website.currencyConfigurationInWebsite.baseCurrency

Event 7.8.8 AddProductToShoppingCart

From use case: Place an order

I Event Specification Attributes quantity : PositiveInteger [1] textOption : TupleType(o:TextOption,text:String) [*] dateOption : TupleType(o:DateOption,date:Date) [*]

Relationships optionValue : OptionValueInOption [*]

308 Generalizations Relationships inherited from ExistingProductEvent product : Product [1] Relationships inherited from ExistingShoppingCartEvent shoppingCart : ShoppingCart [1]

I Initial Integrity Constraints

context AddProductToShoppingCart :: productIsAvailableAtTheCurrentWebsite() : Boolean body: self.shoppingCart.currentStoreView.store.website.productInWebsite->any(piw| piw.product = self.product ).isAvailable

context AddProductToShoppingCart :: productIsEnabledAtTheCurrentStoreView() : Boolean body: self.shoppingCart.currentStoreView.productInStoreView->any(pisw| pisw.product = self.product ).status = Status::Enabled

context AddProductToShoppingCart :: productIsInStock() : Boolean body:

self.product.stockStatus = StockStatus::InStock

context AddProductToShoppingCart :: optionsAreFromTheProduct() : Boolean body: self.product.option -> includesAll(self.textOption->collect(o)) and self.product.option -> includesAll(self.dateOption->collect(o)) and self.product.option->select(o| o.oclIsTypeOf(OptionWithPredefinedContent) ).oclAsType(OptionWithPredefinedContent).optionValueInOption -> includesAll(self.optionValue)

context AddProductToShoppingCart :: optionsAreRatedOnlyOnce() : Boolean body: self.textOption -> isUnique(o) and self.dateOption -> isUnique(o) and self.optionValue -> isUnique(optionWithPredefinedContent)

context AddProductToShoppingCart :: optionIsRatedWhenIsRequired() : Boolean body: self.product.option -> select(o|o.isRequired) -> forAll ( o | if o.oclIsTypeOf(TextOption) then self.textOption->collect(o) -> includes(o.oclAsType(TextOption)) else if o.oclIsTypeOf(DateOption) then self.dateOption->collect(o) -> includes(o.oclAsType(DateOption)) else self.optionValue.optionWithPredefinedContent -> includes(o) endif endif )

I Effect

context AddProductToShoppingCart::effect()

post addProduct: let productExists: Boolean = let existingItem : ShoppingCartItem = [email protected]@pre -> any(i|i.product=self.product) in existingItem.isDefined() and existingItem.optionValueInOption = self.optionValue and existingItem.textOptionRating -> collect(r| Tupleo=r.textOption,text=r.value) = self.textOption and existingItem.dateOptionRating -> collect(r| Tupleo=r.textOption,date=r.value) = self.dateOption in if productExists then let i = self.shoppingCart.shoppingCartItem -> any( i | i.product = self.product ) in i.quantity = [email protected]

309 quantity + self.quantity else i.oclIsNew() and i.oclIsTypeOf(ShoppingCartItem) and i.shoppingCart = self.shoppingCart and i.product = self.product and i.quantity = self.quantity and i.applyDiscount = true and self.textOption -> forAll(tupleOpt| r.oclIsNew() and r.oclIsTypeOf(TextOptionRating) and r.textOption = tupleOpt.o and r.value = tupleOpt.text and r.shoppingCartItem = i ) and self.dateOption -> forAll(tupleOpt| r.oclIsNew() and r.oclIsTypeOf(DateOptionRating) and r.dateOption = tupleOpt.o and r.value = tupleOpt.date and r.shoppingCartItem = i ) and i.optionValueInOption = self.optionValue endif

post decreaseQuantity: StockConfiguration.allInstances->any(true).decreaseStockWhenOrderIsPlaced implies (self.product.quantity = [email protected]@pre - self.quantity)

Event 7.8.9 ChangeCurrencyOfAdministrationShoppingCart

From use case: Add an order

I Event Specification Relationships newCurrency : Currency [1]

Generalizations

I Effect

context ChangeCurrencyOfAdministrationShoppingCart::effect()

post:

self.shoppingCart.currency = self.newCurrency

Event 7.8.10 ChangeEMailAndGroupOfAdministrationShoppingCart

From use case: Add an order

I Event Specification Attributes newEMail : EMail [0..1]

Relationships newCustomerGroup : CustomerGroup [0..1]

Generalizations

310 I Effect

context ChangeEMailAndGroupOfAdministrationShoppingCart::effect()

post: self.shoppingCart.redefinedCustomerEMail = self.newEMail and self.shoppingCart.redefinedCustomerGroup = self.newCustomerGroup

Event 7.8.11 ApplyCouponCode

From use case: Place an order, Add an order

I Event Specification Attributes newCouponCode : String [1]

Generalizations Relationships inherited from ExistingShoppingCartEvent shoppingCart : ShoppingCart [1]

I Effect

context ApplyCouponCode::effect()

post:

self.shoppingCart.couponCode = self.newCouponCode

Event 7.8.12 AddProductToShoppingCart

From use case: Place an order

I Event Specification Attributes quantity : PositiveInteger [1] textOption : TupleType(o:TextOption,text:String) [*] dateOption : TupleType(o:DateOption,date:Date) [*]

Relationships optionValue : OptionValueInOption [*]

Generalizations Relationships inherited from ExistingProductEvent product : Product [1] Relationships inherited from ExistingShoppingCartEvent shoppingCart : ShoppingCart [1]

311 I Initial Integrity Constraints

context AddProductToShoppingCart :: productIsAvailableAtTheCurrentWebsite() : Boolean body: self.shoppingCart.currentStoreView.store.website.productInWebsite->any(piw| piw.product = self.product ).isAvailable

context AddProductToShoppingCart :: productIsEnabledAtTheCurrentStoreView() : Boolean body: self.shoppingCart.currentStoreView.productInStoreView->any(pisw| pisw.product = self.product ).status = Status::Enabled

context AddProductToShoppingCart :: productIsInStock() : Boolean body:

self.product.stockStatus = StockStatus::InStock

context AddProductToShoppingCart :: optionsAreFromTheProduct() : Boolean body: self.product.option -> includesAll(self.textOption->collect(o)) and self.product.option -> includesAll(self.dateOption->collect(o)) and self.product.option->select(o| o.oclIsTypeOf(OptionWithPredefinedContent) ).oclAsType(OptionWithPredefinedContent).optionValueInOption -> includesAll(self.optionValue)

context AddProductToShoppingCart :: optionsAreRatedOnlyOnce() : Boolean body: self.textOption -> isUnique(o) and self.dateOption -> isUnique(o) and self.optionValue -> isUnique(optionWithPredefinedContent)

context AddProductToShoppingCart :: optionIsRatedWhenIsRequired() : Boolean body: self.product.option -> select(o|o.isRequired) -> forAll ( o | if o.oclIsTypeOf(TextOption) then self.textOption->collect(o) -> includes(o.oclAsType(TextOption)) else if o.oclIsTypeOf(DateOption) then self.dateOption->collect(o) -> includes(o.oclAsType(DateOption)) else self.optionValue.optionWithPredefinedContent -> includes(o) endif endif )

I Effect

context AddProductToShoppingCart::effect()

post addProduct: let productExists: Boolean = let existingItem : ShoppingCartItem = [email protected]@pre -> any(i|i.product=self.product) in existingItem.isDefined() and existingItem.optionValueInOption = self.optionValue and existingItem.textOptionRating -> collect(r| Tupleo=r.textOption,text=r.value) = self.textOption and existingItem.dateOptionRating -> collect(r| Tupleo=r.textOption,date=r.value) = self.dateOption in if productExists then let i = self.shoppingCart.shoppingCartItem -> any( i | i.product = self.product ) in i.quantity = [email protected] quantity + self.quantity else i.oclIsNew() and i.oclIsTypeOf(ShoppingCartItem) and i.shoppingCart = self.shoppingCart and i.product = self.product and i.quantity = self.quantity and i.applyDiscount = true and self.textOption -> forAll(tupleOpt| r.oclIsNew() and r.oclIsTypeOf(TextOptionRating) and r.textOption = tupleOpt.o and r.value = tupleOpt.text and r.shoppingCartItem = i ) and

312 self.dateOption -> forAll(tupleOpt| r.oclIsNew() and r.oclIsTypeOf(DateOptionRating) and r.dateOption = tupleOpt.o and r.value = tupleOpt.date and r.shoppingCartItem = i ) and i.optionValueInOption = self.optionValue endif

post decreaseQuantity: StockConfiguration.allInstances->any(true).decreaseStockWhenOrderIsPlaced implies (self.product.quantity = [email protected]@pre - self.quantity)

Event 7.8.13 AddGiftMessage

From use case: Place an order, Add an order

I Event Specification Attributes newGiftMessage : String [1]

Generalizations Relationships inherited from ExistingShoppingCartEvent shoppingCart : ShoppingCart [1]

I Effect

context AddGiftMessage::effect()

post:

self.shoppingCart.giftMessage = newGiftMessage

Event 7.8.14 AddGiftMessageToItem

From use case: Place an order, Add an order

I Event Specification Attributes newGiftMessage : String [1]

Relationships shoppingCartItem : ShoppingCartItem [1]

I Effect

context AddGiftMessageToItem::effect()

post:

313 self.shoppingCartItem.giftMessage = newGiftMessage

Event 7.8.15 OrderConfirmation

From use case: Place an order, Add an order

I Event Specification Attributes comments : TupleType (text: String, notifyCustomer:Boolean) [*] creditCardInfo : TupleType (type: String, owner: String, number: String, expires: Date, verification: String) [0..1] eMailSent : Boolean [1]

Relationships shoppingCart : ShoppingCart [1] delivery : Address [1] billing : Address [1] shippingMethod : ShippingMethod [1] paymentMethod : PaymentMethod [1]

I Initial Integrity Constraints

context OrderConfirmation :: guestCheckoutIsEnabledIfNeeded() : Boolean body: (self.shoppingCart.shoppingCartItem.product -> exists (p|p.oclIsTypeOf(DownloadableProduct)) and self.- shoppingCart.currentStoreView.store.website.catalogConfigurationInWebsite.allowGuestCheckoutForDownloadableIt- ems) implies (not self.shoppingCart.oclIsTypeOf(AnonymousShoppingCart))

context OrderConfirmation :: shippingMethodIsEnabledAtTheCurrentWebsite() : Boolean body: let sh:ShippingMethodInWebsite = self.shoppingCart.currentStoreview.store.website. shippingMethodInWebsite->any(sw|sw.shippingMethod = self.shippingMethod) in sh.status = Status::Enabled

context OrderConfirmation :: allowsFreeShippingOnlyForAMinimumAmount() : Boolean body: let sh:ShippingMethodInWebsite = self.shoppingCart.currentStoreview.store.website. shippingMethodInWebsite->any(sw|sw.shippingMethod = self.shippingMethod) in sh.oclIsTypeOf(FreeShippingInWebsite) implies self.shoppingCart.total >= sh.oclAsType(FreeShippingInWebsite).minimumOrderAmount

context OrderConfirmation :: paymentMethodIsEnabledAtTheCurrentWebsite() : Boolean body: let selectedMethodConfiguration : PaymentMethodInWebsite = self.shoppingCart.currentStoreView.store.website.paymentMethodInWebsite->any(sw|sw.paymentMethod = self.paymentMethod) in selectedMethodConfiguration.status = Status::Enabled and selectedMethodConfiguration.minimumAllowed <= self.shoppingCart.total and selectedMethodConfiguration.maximumAllowed >= self.shoppingCart .total

context OrderConfirmation :: creditCardDetailsAreThereIfNeeded() : Boolean

314 body: self.paymentMethod.oclIsKindOf(CreditCardMethod) implies self.creditCardInfo.isDefined()

I Effect context OrderConfirmation::effect() post theOrderIsCreated: let taxConf : TaxConfigurationInWebsite = [email protected] figurationInWebsite in let wbCurrency : Currency = [email protected]figurationInWeb- site.baseCurrency in let purchCurrency : Currency = [email protected] in o.oclIsNew() and o.oclIsTypeOf(Order) and o.couponCode = [email protected] and o.eMailSent = self.eMailSent and o.giftMessage = [email protected] and o.storeView = [email protected] and o.customerGroup = [email protected] and o.billing = self.billing and o.delivery = self.delivery and o.shippingMethod = self.shippingMethod and o.paymentMethod = self.paymentMethod and o.status = OrderStatus::Pending and self.shoppingCart.oclIsTypeOf(CustomerShoppingCart) implies o.customer = [email protected](CustomerShoppingCart).customer@pre and self.shoppingCart.oclIsTypeOf(AdministrationShoppingCart) implies o.customer = [email protected](AdministrationShoppingCart).customer@pre and o.rateApplicationAddress = if taxConf.usedAddress = TaxCalculatingAddress::Billing then o.billing else if taxConf.usedAddress = TaxCalculatingAddress::Delivery then o.delivery else taxConf.website.shippingConfigurationInWebsite.shippingOrigin endif endif and o.genericBaseCurrency = CurrencyConfiguration.allInstances()->any(true).genericBaseCurrency and wbCurrency <> o.genericBaseCurrency implies (u.oclIsNew and u.oclIsTypeOf(UseOfWebsiteRedefinedBaseCurrency) and u.orderOfRedefinedBaseCurrency = o and u.websiteRedefinedBaseCurrency = wbCurrency and u.initialRate = CurrencyRate.allInstances->any(r| r.baseCurrency=o.genericBaseCurrency and r.currency=wbCurrency ).rate) and (purchCurrency <> o.genericBaseCurrency and purchCurrency <> o.websiteRedefinedBaseCurrency) implies (u.oclIsNew and u.oclIsTypeOf(UseOfPurchasingCurrency) and u.orderOfPurchasingCurrency = o and u.purchasingCurrency = purchCurrency and u.initialRate = CurrencyRate.allInstances->any(r| r.baseCurrency=o.genericBaseCurrency and r.currency=purchCurrency ).rate) and self.comments.forAll ( tupleCom | c.oclIsNew() and c.oclIsTypeOf(Comment) and c.text = tupleCom.text and c.notifyCustomer = tupleCom.notifyCustomer and c.createdAt = Now() and c.status = CommentStatus::Pending and o.comment -> includes(c) ) and [email protected]@pre -> forAll ( i | ol.oclIsNew() and ol.oclIsTypeOf(OrderLine) and ol.order = o and ol.productSku = i.product.sku and ol.productName = i.product.productInStoreView -> any(pw|pw.storeView = o.storeView).name and ol.quantity = i.quantity and ol.giftMessage = i.giftMessage and ol.price = i.price and if i.applyDiscount then let discountBasis: Money = if self.shoppingCart.currentStoreView.store.website.taxConfigurationInWebsite.methodRespectingDis- count = TaxRespectingDiscountsMethod::TaxAfterDiscount and self.shoppingCart.currentStoreView.store.website.taxConfigurationInWebsite.applyDiscountIncludingTax then ol.price * ol.quantity + ol.tax

315 else ol.price * ol.quantity endif in ol.discount = i.calculateDiscount( discountBasis, i.appliedShoppingCartPriceRule ) else ol.discount = 0.0 endif and let appliedTaxRate : Bag(TaxRate) = TaxRule.allInstances() -> select ( t | TaxRule.allInstances() -> forAll ( t2 | t2.priority <= t.priority ) ) ->select ( t | t.customerTaxClass -> includes(o.customerGroup.customerTaxClass) and t.productTaxClass -> in- cludes(i.product) ) -> collect ( t | t.taxRate ) -> select ( tr | tr.applicableAddress -> includes(o.rateApplicationAddress) ) -> union ( i.product.fixedTaxRate ) in ol.tax = (appliedTaxRate -> collect ( a | a.rate * i.price * i.quantity / 100 ) -> sum()) and ol.total = (i.total + ol.tax) and ol.textOptions = TextOptionInfo.allInstances() -> select (inf| i.textOption.genericName -> includes(inf.optionName) and i.textOptionRating.value -> includes(inf.value) ) and ol.dateOptions = DateOptionInfo.allInstances() -> select (inf| i.dateOption.genericName -> includes(inf.optionName) and i.dateOptionRating.value -> includes(inf.value) ) and ol.predefinedContentOptions = PredefinedContentOptionInfo.allInstances() -> select (inf| i.optionValueInOption.optionWithPredefinedContent.genericName -> includes(inf.optionName) and i.optionValueInOption.optionValue.genericName -> includes(inf.valueName) ) )

post theShoppingCartItemsAreRemoved:

self.shoppingCart.shoppingCartItem -> isEmpty()

post theShoppingCartIsRemovedIfItIsAdministration:

[email protected](AdministrationShoppingCart) implies (not [email protected] Of(OclAny))

post updateProductQuantities: StockConfiguration.decreaseStockWhenOrderIsPlaced implies [email protected]@pre -> forAll ( i | i.product.quantity = i.product.quantity@pre - i.quantity )

Event 7.8.16 DeleteAdministrationShoppingCart

From use case: Add an Order

I Event Specification Relationships administrationShoppingCart : AdministrationShoppingCart [1]

I Effect

context DeleteAdministrationShoppingCart::effect()

post:

316 not [email protected](OclAny)

Event 7.8.17 CancelOrder

From use case: Cancel an order

I Event Specification Generalizations Relationships inherited from ExistingOrderEvent order : Order [1]

I Initial Integrity Constraints

context CancelOrder :: orderIsPendingOrProcessing() : Boolean body: self.order.status = OrderStatus::Pending or self.order.status = OrderStatus::Processing

I Effect

context CancelOrder::effect()

post:

self.order.status = OrderStatus::Cancelled

Event 7.8.18 HoldOrder

From use case: Hold an order

I Event Specification Generalizations Relationships inherited from ExistingOrderEvent order : Order [1]

I Initial Integrity Constraints

context HoldOrder :: orderWasPendingOrProcessing() : Boolean body: self.order.status = OrderStatus::Pending or self.order.status = OrderStatus::Processing

I Effect

317 context HoldOrder::effect()

post:

self.order.status = OrderStatus::Hold

Event 7.8.19 UnholdOrder

From use case: Unhold an order

I Event Specification Generalizations Relationships inherited from ExistingOrderEvent order : Order [1]

I Initial Integrity Constraints

context UnholdOrder :: orderWasHold() : Boolean body:

self.order.status = OrderStatus::Hold

I Effect

context UnholdOrder::effect()

post: if self.order.invoice -> isEmpty() and self.order.shipment -> isEmpty() then self.order.status = OrderStatus::Pending else self.order.status = OrderStatus::Processing endif

Event 7.8.20 Reorder

From use case: Reorder

I Event Specification Relationships order : Order [1]

I Initial Integrity Constraints

context Reorder :: orderIsPlacedByARegisteredCustomer() : Boolean body:

318 self.order.customer.isDefined()

I Effect

context Reorder::effect()

post: let sc:CustomerShoppingCart=self.order.customer.customerShoppingCart in self.order.orderLine -> forAll ( ol | if [email protected] -> excludes(ol.product) then i.oclIsNew() and i.oclIsTypeOf(ShoppingCartItem) and i.shoppingCart = sc and i.product = ol.product and i.quantity = ol.quantity else let i = sc.shoppingCartItem -> any( i | i.product = ol.product ) in i.quantity = [email protected] + ol.- quantity endif )

Event 7.8.21 AddInvoice

From use case: Add an invoice

I Event Specification Attributes line : TupleType ( product:Product, quantity:PositiveInteger ) [1..*] eMailSent : Boolean [1] capturingMethod : InvoiceCapturingMethod [1]

Generalizations Relationships inherited from ExistingOrderEvent order : Order [1]

I Initial Integrity Constraints

context AddInvoice :: orderAllowsMoreInvoices() : Boolean body:

self.order.status = OrderStatus::Pending or self.order.status = OrderStatus::Processing

context AddInvoice :: productsAreFromTheOrder() : Boolean body:

self.line -> collect(product) -> forAll ( p | self.order.orderLine.productSku -> includes(p.sku) )

context AddInvoice :: quantitiesAreNotOverAllowedValues() : Boolean body:

self.line -> forAll ( li | li.quantity = self.order.orderLine->any(info|info.productSku=li.product.sku).quantity )

319 I Effect

context AddInvoice::effect()

post createInvoice: i.oclIsNew and i.oclIsTypeOf(Invoice) and i.order = self.order and i.eMailSent = self.eMailSent and i.createdAt = Now() and i.capturingMethod = self.capturingMethod and i.isPaidOnline = ( self.order.paymentMethod.oclIsTypeOf(AuthorizeNet) or self.order.paymentMethod.oclIsTypeOf(PayFlowPro) or self.order.paymentMethod.oclIsKindOf(PayPalMethod) or self.order.paymentMethod.oclIsTypeOf(GoogleCheckout) ) and i.status = if self.capturingMethod = InvoiceCapturingMethod::PayLater and i.IsPaidOnline then InvoiceStatus::Pending else InvoiceStatus::Paid endif and self.line -> forAll ( line | il.oclIsNew() and il.oclIsTypeOf(InvoiceLine) and il.invoice = i and il.product = line.product and il.quantity = line.quantity )

post changeOrderStatus: if (self.order.nonReInvoiceableTotal < self.order.total or self.order.shipment.shipmentLine.quantity -> sum() < self.order.orderLine.quantity -> sum()) then self.order.status = OrderStatus::Processing else self.order.status = OrderStatus::Complete endif

Event 7.8.22 PayInvoice

From use case: Register an invoice payment

I Event Specification Relationships invoice : Invoice [1]

I Initial Integrity Constraints

context PayInvoice :: invoiceWasPending() : Boolean body:

self.invoice.status = Status::Pending

I Effect

context PayInvoice::effect()

post:

self.invoice.status = Status::Paid

320 Event 7.8.23 CancelInvoice

From use case: Cancel an invoice

I Event Specification Relationships invoice : Invoice [1]

I Initial Integrity Constraints

context CancelInvoice :: invoiceWasNotCancelled() : Boolean body:

self.invoice.status <> Status::Cancelled

I Effect

context CancelInvoice::effect()

post:

self.invoice.status = Status::Cancelled

Event 7.8.24 AddShipment

From use case: Add a shipment, Add an invoice

I Event Specification Attributes line : TupleType ( product:Product, quantity:PositiveInteger ) [1..*] eMailSent : Boolean [1] trackingNumber : TrackingNumber [*]

Generalizations Relationships inherited from ExistingOrderEvent order : Order [1]

I Initial Integrity Constraints

context AddShipment :: orderAllowsMoreShipments() : Boolean body:

self.order.status = OrderStatus::Pending or self.order.status = OrderStatus::Processing

context AddShipment :: productsAreFromTheOrder() : Boolean body:

321 self.line -> collect(product) -> forAll ( p | self.order.orderLine.productSku -> includes(p.sku) )

context AddShipment :: quantitiesAreNotOverAllowedValues() : Boolean body:

self.line -> forAll ( li | li.quantity = self.order.orderLine->any(info|info.productSku=li.product.sku).quantity )

I Effect

context AddShipment::effect()

post createShipment: s.oclIsNew and s.oclIsTypeOf(Shipment) and s.order = self.order and s.eMailSent = self.eMailSent and s.createdAt = Now() and s.trackingNumber = self.trackingNumber and and self.line -> forAll ( line | sl.oclIsNew() and sl.oclIsTypeOf(ShipmentLine) and sl.shipment = s and sl.orderLine.productSku = line.product.sku and sl.quantity = line.quantity )

post changeOrderStatus: if (self.order.nonReInvoiceableTotal < self.order.total or self.order.shipment.shipmentLine.quantity -> sum() < self.order.orderLine.quantity -> sum()) then self.order.status = OrderStatus::Processing else self.order.status = OrderStatus::Complete endif

Event 7.8.25 AddRefund

From use case: Add a refund

I Event Specification Attributes line : TupleType ( product:Product, quantity:PositiveInteger ) [1..*] eMailSent : Boolean [1] shippingAmount : Money [1] additionalAmount : Money [1] fee : Money [1] refundIsAlreadyReturned : Boolean [1]

Generalizations Relationships inherited from ExistingOrderEvent order : Order [1]

I Initial Integrity Constraints

context AddRefund :: orderAllowsMoreRefunds() : Boolean body:

self.order.status = OrderStatus::Processing or self.order.status = OrderStatus::Complete

322 context AddRefund :: productsAreFromTheOrder() : Boolean body:

self.line -> collect(product) -> forAll ( p | self.order.orderLine.productSku -> includes(p.sku) )

context AddRefund :: quantitiesAreNotOverAllowedValues() : Boolean body: self.line -> forAll ( li | li.quantity = self.order.invoice->select(i|i.status=Status::Paid).invoiceLine ->select(info|info.orderLine.productSku=li.product.sku).quantity->sum() )

I Effect

context AddRefund::effect()

post createRefund: r.oclIsNew and r.oclIsTypeOf(Refund) and r.order = self.order and r.eMailSent = self.eMailSent and r.createdAt = Now() and r.shippingAmount = self.shippingAmount and r.additionalAmount = self.additionalAmount and r.fee = self.fee and r.status = if self.refundIsAlreadyReturned then RefundStatus::Refunded else RefundStatus::Pending endif and self.line -> forAll ( line | rl.oclIsNew() and rl.oclIsTypeOf(RefundLine) and rl.refund = r and rl.product = line.product and rl.quantity = line.quantity )

post changeOrderStatus:

self.order.totalRefunded = self.order.total implies self.order.status = OrderStatus::Closed

Event 7.8.26 CancelRefund

From use case: Cancel a refund

I Event Specification Relationships refund : Refund [1]

I Initial Integrity Constraints

context CancelRefund :: refundWasNotCancelled() : Boolean body:

self.refund.status <> Status::Cancelled

I Effect

context CancelRefund::effect()

323 post:

self.refund.status = Status::Cancelled

Event 7.8.27 AddComment

From use case: Add a comment to an order, invoice, shipment or refund.

I Event Specification Attributes text : String [1] notifyCustomer : Boolean [1]

Relationships commentable : Commentable [1]

I Effect

context AddComment::effect()

post: co.oclIsNew() and co.oclIsTypeOf(Comment and co.text = self.text and co.notifyCustomer = self.notifyCustomer and co.createdAt = now() and co.status = CommentStatus::Pending

324 Part III

Anàlisi i validació de l’esquema

325 8 Anàlisi de l’esquema

El·licitar l’esquema conceptual d’un sistema permet adquirir el coneixement del domini que l’esque- ma representa i expressar-lo de manera clara i inequívoca. Aquest coneixement és útil per a tasques posteriors d’anàlisi. En aques capítol en presentem dues: la comparació de Magento amb el sistema osCommerce [15], a la secció 8.1, i una suggerència de millores en el model de Magento, que es troba a la secció 8.2.

8.1 Comparació amb l’osCommerce

8.1.1 Metodologia

Es proposa, en aquest projecte, realitzar la comparació entre els sistemes Magento i osCom- merce a partir del coneixement que representen, és a dir, dels seus esquemes conceptuals. Les conclusions d’aquesta comparació són equivalents a les que se’n podria obtenir d’una compara- ció entre els dos sistemes (obviant, és clar, els aspectes d’implementació).

Per facilitar la comparació de conceptes entre els dos esquemes s’ha pres la decisió següent: aquells conceptes del domini de Magento que siguin també presents en el domini de osCom- merce es representaran a l’esquema mantenint el nom amb el qual se’ls designava a l’esquema conceptual de osCommerce.

S’ha tractat de manera excepcional el nom “atribut”, donat que representava un concepte lleugerament diferent a cada un dels sistemes, i en tots dos casos es tractava d’un concepte clau. Aquest cas es tracta amb més detall a la secció 8.1.4.

326 8.1.2 Conceptes únics del domini de Magento

Documents posteriors a la venda

Magento incorpora el coneixement referent a aquells fets comercials que s’esdevenen posteri- orment a la realització de l’ordre de compra: pagament, enviament de la comanda i gestió de les devolucions, i dels documents que hi donen suport. El propi sistema n’automatitza la gestió, de tal manera que l’administrador de la botiga només ha de donar l’ordre de creació o de can- cel·lació.

Personalització de les sessions d’usuaris

Magento personalitza la informació mostrada als usuaris amb la intenció d’incentivar la compra de productes que els puguin interessar. Els productes més visualitzats, els últims productes comprats, la llista de productes desitjats, etc. s’enregistren per a cada un dels Customers que utilitzen el sistema. A partir d’aquesta informació, Magento genera la informació a mostrar.

Interacció dels Customers amb el catàleg de productes

Magento incorpora la possibilitat de crear una classificació dels productes a través d’etiquetes, definides lliurement pels usuaris.

8.1.3 Conceptes únics del domini de l’osCommerce

Eines d’e-màrqueting

OsCommerce ofereix un conjunt reduït d’eines d’e-màrqueting, com la publicació de banners publicitaris, l’enviament de novetats via correu electrònic als clients o la possibilitat de fer arribar recomanacions de la tenda a altres internautes.

8.1.4 Variació de conceptes en els dos sistemes

Estructura multibotiga

Magento ofereix una estructura de Websites, Stores i StoreViews, que permet gestionar diverses botigues on-line amb un mateix sistema. No és el cas de osCommerce, que gestiona una única botiga. La majoria de propietats de configuració del sistema o dels productes que formen el catàleg de venda es defineixen seguint aquesta estructura: tenen un valor definit a nivell de tot el sistema, que es pot re-definir per als altres nivells de la jerarquia. És el cas de la llengua o divisa utilitzada per defecte o la configuració dels mètodes de pagament i d’enviament.

327 Extensió del catàleg de productes

L’esquema conceptual de Magento defineix 6 especialitzacions del concepte de Producte, oferint les possibilitats de vendre:

I “Packs” de diversos productes.

I Variacions d’un mateix producte.

I Productes descarregables via Internet.

I Serveis intangibles.

El sistema osCommerce, en canvi, només defineix una única tipologia de producte. Ofereix la possibilitat de definir variacions d’un mateix producte a partir dels atributs, tal i com es comenta més endavant en aquest apartat. A més, osCommerce defineix un element de venta descarregable via Internet com a atribut d’un producte, no com a un producte en si.

Propietats d’un producte

Magento defineix múltiples propietats per al concepte de producte. A més a més, ofereix a l’ad- ministrador de la botiga la capacitat de configurar quines propietats necessiten obligatòriament un valor, o desactivar l’ús d’algunes d’elles, en funció de la tipologia del producte. El nombre de propietats del concepte producte és més reduït a l’esquema conceptual de l’osCommerce.

Càlcul de preus

Magento ofereix diversos possibilitats de modificar el preu final del producte, segons les condi- cions de la compra (client, quantitat de productes comprada, promocions...). Detallem a contin- uació aquells que tenen un concepte equivalent a osCommerce:

I NetPrice

I SpecialNetPrice

I BasePrice: en tots dos sistemes es tracta d’un concepte calculat a partir del NetPrice i SpecialNetPrice. A Magento, a més, inclou dues possibilitats més de modificació del preu.

I Price

A osCommerce, a més, existeix el concepte de FinalPrice, en el qual s’afegeixen al producte la quantita corresponent de shipping costs i taxes. Magento imputa aquests imports al conjunt de la comanda, i no a nivell de ítem de compra.

Aplicació d’impostos

A osCommerce, l’administrador de botiga pot definir l’aplicació dels impostos en funció de la zona geogràfia a la qual resideix el comprador i del producte comprat. Cada impost està associat als productes als quals s’aplica a nivell de producte.

328 A Magento, a més, permet definir una classificació dels productes (per exemple: luxe, estàn- dard i necessitat bàsica) i dels clients en funció de la qual s’aplicaran els impostos.

El concepte d’atribut

El concepte d’atribut apareix en els dos sistemes, però representa un coneixement diferent. A Magento, un atribut és:

Attributes are properties of products in the catalog. The StoreAdministrator can create, delete them and define some characteristics, as well as assigning them a value to each couple Product - Attribute.

En canvi, l’esquema conceptual de l’osCommerce definia el concepte d’atribut de la següent manera:

Several attributes can be defined for each product. Product attributes are used to offer multiple options of a product. An attribute is an option/value pair. The attributes are chosen by the customer.

És a dir, el concepte d’Atributte de osCommerce es correspon al concepte de ProductOption de Magento. El concepte Attribute de Magento, en canvi, no té equivalent a osCommerce. Contràriament al criteri establert per a aquest projecte, en aquest cas s’ha decidit donar preferència a la nomenclatura utilitzada per Magento per damunt de mantenir la nomenclatura present a l’esquema conceptual de l’osCommerce. Al tractar-se d’un dels conceptes clau del sistema, s’ha considerat que aquesta decisió afavoria la comprensibilitat de l’esquema.

Adaptabilitat ampliada

Magento ofereix un amplíssim ventall de paràmetres de configuració, que permeten adaptar-lo a les necessitats de cada cas particular. Com l’osCommerce, ofereix la capacitat de configurar diferents tipus de productes, operar en diferents països i divises o personalitzar el portal web. Magento permet addicionalment configurar paràmetres de la gestió de stocks, establir condi- cions per a les compres realitzades, definir el mètode d’aplicació de les taxes o la visibilitat dels Clients registrats. A més, es poden definir diferents configuracions per cada Website o Store- View.

Localitzacions geogràfiques

El sistema osCommerce definia els conceptes de Country i Zone. Magento n’amplia la granular- itat en definir com a entitats els conceptes de Municipality i PostalArea.

8.1.5 Conclusions

Magento suposa una clara evolució dels sistemes de e-commerce. Introdueix un coneixement relacionat amb la venta per internet que no era representat per osCommerce. En conseqüència, les possibilitats el sistema es veuen molt ampliades. Si osCommerce era una bona solució Open Source per al comerç electrònic, Magento Community Edition és una

329 solució capaç de donar servei a projectes comercials de tamany considerable. Per il·lustrar aquest fet, presentem a continuació una comparació del nombre d’elements que formen cada un dels dos esquemes conceptuals:

Sistema Entitats Atributs Associa- Invariants Casos Esdeveni- cions d’ús ments Magento 218 982 255 703 168 2021 osCommerce 78 202 60 215 129 216

Taula 8.1: Comparació del nombre d’elements que formen cada un dels dos esquemes concep- tuals. Font: model USEx i documentació dels respectius esquemes. 1Dels 202 esdeveniments definits, 72 han estat especificats en aquest projecte.

8.2 Millores suggerides a Magento

Identificació de les instàncies

En la implementació actual de Magento, algunes de les entitats més rellevants del sistema s’i- dentifiquen a partir de nombres d’identificació interns de la Base de Dades. En la modelització conceptual, tots els objectes del domini han de ser identificables [8]. Així, s’evitaria situacions en què poden existir dos objectes conceptualment iguals per a les parts inter- essades en el sistema (per exemple, dos productes amb les mateixes característiques exactes: nom, preu, atributs, etc.). Considerem, doncs, que les algunes d’aquestes entitats poden ser identificades per alguna de les seves propietats inherents, evitant així la utilització de nombres d’identificació interns creats específicament amb aquest propòsit. Les detallem a continuació:

I Una Store es pot identificar amb l’atribut name.

I Una TaxRate es pot identificar amb l’atribut genericName.

I Una Option es pot identificar amb l’atribut genericName i el Product al qual està associ- ada.

I Una OptionValue es pot identificar amb l’atribut genericName i la Option a la qual està associada.

I Un BundleProductItem es pot identificar amb l’atribut name i el BundleProduct al qual està associat.

I Un DownloadableItem es pot identificar amb l’atribut name i el DownloadableProduct al qual està associat.

I Una Category es pot identificar amb l’atribut genericName i la parentCategory a la qual està associada.

I Un EnumerationValue es pot identificar amb l’atribut genericName i l’Attribute al qual està associat.

I Una CatalogPriceRule i una ShoppingCartPriceRule es poden identificar amb l’atribut name.

330 I Un Review es pot identificar a partir dels atributs authorNickName i text.

I Un Newsletter es pot identificar a partir dels atributs subject i message.

La classe Store

La capacitat multibotiga de Magento, amb què es basen molts altres aspectes del sistema, util- itza bàsicament els conceptes de Website i StoreView. El concepte de Store, en canvi, està infrautilitzat. El seu ús es limita a l’agrupació de diversos StoreViews sota un nom comú, i a l’associació d’una Category arrel. A nivell conceptual, representa un concepte lleugerament diferent del de Website, doncs un lloc web pot allotjar diverses “botigues”. Tampoc no coincideix amb el concepte de StoreView, doncs diverses formes de visualització d’una botiga no són diverses botigues, sinó una. Es proposen, doncs, dues opcions:

I En primera opció, eliminar el concepte de Store, assignant la definició d’una Category arrel a nivell de Website o StoreView.

I Com a opció contrària, també es contempla la opció de dotar el concepte Store de més interrelació amb altres aspectes del sistema: de la mateixa forma que molts atributs poden ser definits a nivell de Website i StoreView, permetre la seva redefinició a nivell de Store en els casos en què això sigui coherent amb el coneixement representat.

Duplicitat entre ConfigurableProduct i ProductOptions

Els conceptes de ConfigurableProduct i ProductOptions representen un coneixement extremada- ment semblant: variacions d’un mateix producte. Productes com una samarreta que es vengui en diferents colors o un ordinador amb una segmentació de gammes de més a menys presentacions poden ser modelitzats per ambdós conceptes. En el primer cas, cada una de les variacions (samarreta verda, samarreta groga...) es mod- elitza com un SimpleProduct independent, cada un amb els seus preus i descomptes. Addi- cionalment, es defineix un producte comú que representa, de forma abstracta, el conjunt dels subproductes. Aquest producte és un ConfigurableProduct. És habitual que l’administrador de la botiga deshabiliti la compra directa dels subproductes concrets. En el moment de la compra, un Customer sel·lecciona al catàleg el producte abstracte i, posteriorment, tria el subproducte que prefereix. En el segon cas, es defineix un únic producte, que es defineix com a SimpleProduct i al qual se li assigna un conjunt d’opcions (ProductOption). En el moment de la compra, cada opció serà omplerta pel client amb un valor, que pot ser lliure (la opció és oberta) o escollit dins d’un conjunt pre-definit per l’administrador de la botiga (la opció és tancada). Els preus estan definits pel producte únic, però es pot associar un increment o decrement del preu associat a cada valor de la opció. Es proposa racionalitzar la duplicitat que representen l’estructura de ConfigurableProduct i les ProductOptions tancades, descartant un dels dos conceptes.

331 Localitzacions geogràfiques

Per modelitzar d’una forma clara el coneixement de Magento en l’àmbit de les Locations (països, municipis, àrees postals, adreces...) s’han fet diverses assumpcions:

I Cada concepte correspon a una entitat pròpia.

I Les instàncies de Country i Zone estan predefinides pel sistema i no es poden crear, mod- ificar o eliminar.

I Les instàncies de Municipality i PostalArea es creen només la primera vegada que són necessàries per a la creació o edició d’una adreça, a partir de la informació introduïda per un usuari del sistema, i no es destrueixen.

Aquesta estructura comporta diversos inconvenients:

I Zone modelitza el concepte de regió, província, comunitat autònoma o estat federat. Les instàncies definides poden no adaptar-se a les necessitats d’una determinada botiga, per no estar definides per a una determinada geografia o no amb la granularitat que requereix la botiga. Aquest aspecte té una incidència important en la definició de TaxRates (taxes impositives), que poden associar-se a una regió o estat determinat.

I El sistema no controla que les Municipalities i PostalAreas donades d’alta al sistema cor- responguin a poblacions i àrees postals reals, o que pertanyin al Country / Zone indicat per l’usuari. El sistema no detectarà, doncs, dades errònies o malintencionades que els Customers puguin introduïr en la realització d’una compra.

Es proposa que el sistema incorpori també instàncies predefinides de Municipalities i PostalAr- eas, per aquells països en què es dispongui de dades fiables. La població d’aquests conceptes s’actualitza a un ritme raonablement lent.

Addicionalment, es proposa dotar el sistema d’una funcionalitat que permeti a l’StoreAdmin- istrator definir, editar i eliminar instàncies de qualsevol d’aquests conceptes. En definir o editar una adreça, s’associaria a instàncies ja existents. El risc d’introduïr instàncies que no es corre- sponguin amb la realitat es manté, però les dades només són introduïdes per l’StoreAdministra- tor.

Propietats d’un producte

El sistema Magento permet configurar quines de les propietats d’un producte (és a dir, atributs del concepte Product) requereixen obligatòriament un valor. Si bé aquesta possibilitat és raonable per a propietats com la descripció o el pes, és inconcebible que un producte (que en el domini de Magento es considera en venda) no tingui associat cap preu, o ni tampoc cap codi que l’identifica com a únic (SKU). Per raons de qualitat de l’esquema, s’ha eliminat aquesta possibilitat en l’esquema conceptual dissenyat.

332 9 Proves automàtiques d’esquemes conceptuals

Un cop desenvolupat l’esquema conceptual de Magento, hem definit i executat proves automà- tiques amb l’objectiu de validar-lo. En aquest capítol es defineix què són les proves automàtiques d’esquemes conceptuals, mentre que al capítol 10 es descriu el procés de proves aplicat en el projecte actual.

La secció 9.1 d’aquest capítol presenta conceptes d’un procés de proves automàtiques genèric, que podria aplicar-se a qualsevol tipologia de software. La secció 9.2 descriu breument l’adaptació d’aquest procés a les proves d’esquemes conceptuals amb què es basa la segona part del pro- jecte i la rellevància d’aquestes proves. La definició completa d’aquesta adaptació és a [16]. Finalment, a la secció 9.3 es descriuen les eines que fan possible l’execució d’aquestes proves.

En aquest capítol i els posteriors, s’utilitzen indistintament els conceptes de test i prova.

9.1 Un procés de proves genèric

9.1.1 Descripció del procés

Planificar i executar un procés de test/proves per a un esquema conceptual és una de les tèc- niques existents1 per assegurar-ne la seva qualitat (veure la secció 2.2 sobre qualitat dels es- quemes conceptuals). El procés de test d’un esquema conceptual guarda certes similituds amb el procés de test d’un software en funcionament. Un procés de test de software assumeix que el sistema que es posa a prova (SUT: System Under Test) ofereix un conjunt d’operacions. Provar el SUT significa cridar aquestes operacions amb el context i les dades d’entrada apropiades, i comprovar que retorna el resultat esperat [16]. Veurem més endavant 9.2.1 la definició equivalent per a un

1Veure [16] per a més informació sobre altres tècniques existents.

333 procés de test d’un esquema conceptual. Una fase de test o proves pretén assegurar que el comportament del SUT és precisament el que se n’espera. De manera complementària, però, a [6]es defineix el procés de test de la següent forma:

“To test a program is to try to make it fail, [...] the goal of testing becomes to uncover faults by triggering failures.”

Un procés de test inclou la planificació i execució d’un o més casos de test. Un cas de test és una especificació d’un cas per posar a prova un sistema, que inclou què es posa a prova, amb quines dades d’entrada, quines condicions i quin resultat se n’espera (veure [17]).

9.1.2 Significat dels resultats obtinguts

A partir del resultat esperat que se l’hi indica i el resultat real obtingut, un cas de test sempre retorna un veredicte (veure [11]):

Afirmatiu/Pass El resultat obtingut coincideix amb el resultat esperat. Negatiu/Fail El resultat obtingut no coincideix amb el resultat esperat. Error L’execució del cas de test ha succeït de manera anòmala.

Un veredicte negatiu o error implica un comportament no esperat. La causa d’aquest veredicte pot provenir tant del sistema que es posa a prova com del codi de test. Un veredicte afirmatiu en tots els casos de test indica que el SUT segueix el comportament esperat, per aquells casos posats a prova. El aquest punt, es pot donar per finalitzat el procés de test o dissenyar nous casos de test. No es pot assegurar al 100 % la validació d’un sistema amb una quantitat determinada de testos, però existeixen criteris que permeten evaluar la cobertura de les proves dutes a terme2.

9.1.3 Avantatges de les proves automàtiques

La definició de les proves dissenyades com a codi executable i el fet que les mateixes proves continguin afirmacions sobre les expectatives de resultats permet que el procés de test es pugui dur a terme de manera automàtica: un cop definits els casos de test, la tasca del responsable de les proves es limita a engegar el procés i observar-ne els resultats. Un procés de test amb proves automàtiques es pot repetir infinites vegades mantenint amb exactitud les mateixes condicions i resultats esperats. Això permet executar el procés en diverses etapes d’un sistema en construcció/evolució i veure l’evolució dels veredictes.

S’espera que la construcció/evolució del sistema que es posa a prova comporti progressiva- ment un canvi de veredicte (de negatiu a afirmatiu) en els casos de test. A la vegada, es controla que l’evolució del sistema no comporti canvis en sentit contrari (d’afirmatiu a negatiu)3.

2Per a les proves de sistemes software, el criteri de cobertura de codi evalua el nombre de línies de codi del sistema que han estat executades per les proves. Una cobertura de codi del 100% no assegura que totes les situacions possibles que pot afrontar el sistema hagin estat provades, però sí una part considerable. 3La tècnica es coneix com a Test de No-Regressió

334 9.2 El procés de proves d’un esquema conceptual

9.2.1 Adaptació dels conceptes

Un esquema conceptual no és un sistema d’informació, sinó el coneixement que aquest sistema d’informació necessita sobre el domini amb el qual tracta i les funcions que ha de dur a terme.

Per posar un sistema a prova és necessari que sigui executable. És executable un esquema conceptual? Segons [16], “un esquema conceptual es pot considerar un programa [executable] només si existeix un processador de propòsit general capaç de comportar-se d’acord amb les regles estructurals i de comportament definides en l’esquema”. A la secció 9.3 veurem que l’eina USEx ens permet treballar amb esquemes conceptuals executables.

Reprenem la definició d’un procés de test de software donada a la secció 9.1:

“Un procés de test de software assumeix que el sistema que es posa a prova (SUT: System Under Test) ofereix un conjunt d’operacions. Provar el SUT significa cridar aquestes operacions amb el context i les dades d’entrada apropiades, i comprovar que retorna el resultat esperat”.

Ara el que posem a prova és un esquema conceptual (CSUT: Conceptual Schema Under Test). En lloc d’operacions, ofereix esdeveniments de domini, que poden requerir o no dades d’entrada. El context en què s’invoca l’esdeveniment és un estat concret de la base d’informació.

El resultat esperat correspon al compliment o no d’expressions OCL executades sobre la base d’informació: precondicions i postcondicions de l’esdeveniment, restriccions generals de l’esquema o expressions definides en el propi cas de test.

9.2.2 Estructura d’un cas de test d’esquemes conceptuals

Un cas de test automàtic és codi executable, i està format per una o diverses instruccions. En els casos de test d’esquemes conceptuals, les instruccions es classifiquen en 4 tipologies4.

Instruccions que modifiquen la base d’informació.

Permeten crear, modificar o destruïr instàncies independentment dels esdeveniments de domini. Són útils en les següents situacions:

I Per substituïr esdeveniments que encara no hagin estan dissenyats.

5 I Per substituïr esdeveniments que no hagin estan suficientment provats encara .

I Per deixar la base d’informació en un estat inconsistent al qual no es pot arribar a partir d’esdeveniments de domini.

Instruccions que verifiquen l’estat de la base d’informació.

Es subdivideixen en dos tipus.

4La sintaxi de les instruccions està documentada a [16]. 5Quan en un cas de test és necessari utilitzar més d’un esdeveniment és aconsellable focalitzar la prova en un d’ells. Per la resta, si no es pot assegurar que tenen el comportament esperat perquè han estat suficientment provats en tests anteriors, és aconsellable substituïr-los per instruccions que modifiquen la BI.

335 I Instruccions que validen la part estructural de l’esquema, determinant si la base d’informa- ció compleix o no les restriccions OCL generals. Un resultat afirmatiu o negatiu equival a dir que la base d’informació és consistent o inconsistent, respectivament.

I Instruccions que permeten definir una consulta sobre el contingut de la base d’informació i comprovar que correspon amb el valor esperat. El veredicte és afirmatiu si el resultats obtingut i esperat coincideixen.

Instruccions que creen esdeveniments de domini.

Permeten donar d’alta un esdeveniment de domini i associar-lo a les dades d’entrada correspo- nents.

Instruccions que verifiquen l’ocurrència (o no) d’un esdeveniment de domini.

Verificar l’ocurrència d’un esdeveniment de domini equival a:

I Comprovar que l’estat actual de la BI és consistent.

I Comprovar que les precondicions de l’esdeveniment es compleixen.

I Executar les modificacions que comporta l’esdeveniment

I Comprovar que el nou estat de la BI és consistent.

I Comprovar que les postcondicions de l’esdeveniment es compleixen.

També és possible verificar la no ocurrència d’un esdeveniment de domini, que no correspon al cas contrari sinó a:

I Comprovar que l’estat actual de la BI és consistent.

I Comprovar que les precondicions de l’esdeveniment no es compleixen.

En tots dos casos, el veredicte és afirmatiu només si totes les comprovacions són certes.

9.2.3 Rellevància dins el cicle de desenvolupament de software

Dissenyar un procés de test de l’esquema conceptual i incloure’l dins el cicle de vida de desen- volupament del software permet detectar alguns errors amb molta més antel·lació. Habitualment, això repercuteix en una major reducció de costos en tot el procés de desenvolupament.

En projectes desenvolupats en la metodologia de desenvolupament dirigit per models (MDD)6, posar a prova l’esquema conceptual permet validar un model independent de qualsevol platafor- ma o tecnologia. Una de les propostes de la metodologia MDD és que la transformació de l’esquema a un model dependent de la plataforma (que pot ser el software final) sigui feta de manera automàtica, i que la correctesa de la transformació quedi demostrada formalment pel cas general. En aquest

6Es pot trobar més informació sobre l’arquitectura dirigida per models (MDA), especificada per l’OMG, a [9].

336 cas, la fase de test de l’esquema conceptual substituïria la fase de test de software inclosa en el cicle de desenvolupament habitual. Si la transformació d’esquema a software és manual o no se n’ha demostrat la correctesa formalment, el procés de test de l’esquema conceptual serà complementari al test de software clàssic. En projectes on l’esquema conceptual és el producte a obtenir en darrera instància7 la qualitat de l’esquema conceptual és crítica.

9.2.4 Modelització conceptual dirigida per tests

Les proves d’esquemes conceptuals es poden executar sobre esquemes complets o fragments d’aquests. La fase de test, doncs, es pot planificar en paral·lel a l’elaboració de l’esquema.

Aquest fet obre portes al desenvolupament d’una metodologia de modelització conceptual dirigida per tests (TDCM)8. La proposta consisteix en definir els requeriments dels actors involu- crats com a tests sobre esquemes conceptuals. Posteriorment, el desenvolupament de l’esque- ma s’encamina únicament a obtenir el comportament que s’espera en els tests dissenyats.

9.3 Entorn proposat

9.3.1 USE / USEx

L’eina original USE (UML Based Specification Environment) és un entorn per a l’especificació i validació de sistemes d’informació especificats en UML i OCL. Ha estat desenvolupada per la Universitat de Bremen.

Implementa un subconjunt reduït d’elements d’UML i OCL, que permeten:

I Especificar i interpretar esquemes conceptuals

I Validar la sintaxi de les expressions OCL i la seva consistència amb l’esquema UML.

I Crear objectes que representen instàncies de l’esquema conceptual i avaluar expressions OCL en funció d’aquestes instàncies.

L’eina USEx és una extensió de USE creada amb la intenció de donar més expressivitat als esquemes conceptuals, ampliant el subconjunt d’elements UML i OCL implementats. Aquesta expressivitat complementària ha estat clau per a representar la complexitat de l’esquema elabo- rat. L’eina ha estat creada pel departament d’Enginyeria del Software i Sistemes d’Informació de la UPC.

USE i USEx defineixen un llenguatge propi amb el qual definir els models. Per a la definició de les restriccions, regles de derivació i operacions, el llenguatge és pràcticament idèntic a l’OCL.

Gràcies a USEx, podem dotar d’executabilitat l’esquema conceptual posat a prova. Quan USEx treballa amb un esquema conceptual permet que la màquina sobre la qual s’està executant (un processador de propòsit general) respecti o evalui les regles estructurals i de comportament definides. Un esquema conceptual reconegut i interpretat per USEx és executable.

7Sovint, l’esquema conceptual serveix de punt de partida d’altres projectes: per al desenvolupament d’un sistema que englobli múltiples aplicacions o si el projecte serà dut a terme per diversos equips. 8La metodologia ha estat proposada per l’Albert Tort a [14]

337 9.3.2 El llenguatge CSTL

El Conceptual Schema Testing Language (CSTL) és un llenguatge definit a [16] que permet especificar casos de test d’esquemes conceptuals amb les instruccions exposades a la secció 9.2.2. Un document CSTL està format per les seccions següents:

I Una part fixa (anomenada “fixture”), on s’hi troben només instruccions que modifiquen la base d’informació. Permet definir un estat de la base d’informació comú com a punt de partida de tots els casos de test. Les instruccions definides s’executen automàticament abans de l’execució de cada cas de test.

I Components auxiliars d’aquesta part fixa (anomenades “fixture component”). De la mateixa naturalesa que l’anterior, però només s’executen quan en un cas de test hi apareix la in- strucció: load nom_component_auxiliar;

I Casos de test. El llenguatge CSTL en defineix tres tipus: concrets, abstractes i invocacions d’un cas abstracte, però per al projecte només s’ha utilitzat el primer. Els casos de test s’executen de manera independent. Vegem alguns exemples de les instruccions que pot contenir un cas de test.

– Modificar la BI. La navegació entre objectes segueix la sintaxi OCL: objecte1.propietatBinaria := true; objecte2.propietatComplexa := objecte1; – Verificar l’estat de la BI: assert consistency; assert inconsistency; assert equals resultatEsperat expressioOCL; – Crear un esdeveniment de domini: esdeveniment1 := new NomEsdeveniment(param1 := valor1, ...); – Validar la ocurrència o no d’un esdeveniment de domini: assert occurrence esdeveniment1; assert non-ocurrence esdeveniment1;

9.3.3 CSTL Processor

CSTLProcessor és una eina desenvolupada pel departament d’Enginyeria del Software i Sis- temes d’Informació de la UPC, que permet executar testos automàtics sobre esquemes concep- tuals executables. L’eina ha estat dissenyada reutilitzant al màxim USEx, que és necessari per a l’execució de testos. USEx interpreta l’esquema conceptual, crea o destrueix instàncies a partir d’una informació rebuda i avalua expressions OCL sobre el model. CSTLProcessor interpreta els testos redactats en llenguatge CSTL, proporciona a USEx la informació per donar d’alta instàncies i les restriccions OCL a crear, i en recull i processa els resultats. El procés es descriu a amb el diagrama que es presenta a continuació.

338 Figura 9.1: Diagrama d’execució d’un cas de test amb les eines proposades

Especificació procedural dels esdeveniments

Els canvis que comporta un esdeveniment de domini a la base d’informació queden definits a les pre i postcondicions, però CSTL processor no infereix el conjunt d’instruccions que han de fer efectiu aquest canvi.

És necessari declarar, en un fitxer a part, les instruccions de modificació de la BI que corre- sponen a cada esdeveniment.

339 10 Validació de l’esquema conceptual de Magento

Arribats en aquest punt, l’esquema estructural i els esdeveniments especificats als capítols 5 i 7 s’han especificat en USEx, i per tant són executables. És el moment de definir una estratègia per a la validació d’aquest esquema (veure secció 10.1), executar les proves i analitzar-ne els resultats (veure secció 10.2).

L’execució d’aquestes proves permet extreure conclusions sobre l’estat actual de l’entorn USEx/CSTL, que es detallen a la secció 10.3.

10.1 Estratègia de les proves

Definir l’estratègia de proves implica el disseny d’un conjunt representatiu de casos de test que permetin validar l’esquema conceptual en qüestió. Cal primer, definir quin coneixement del domi- ni es vol validar (secció 10.1.1), en quines condicions es duran a terme les proves (secció 10.1.2) i en què consistiran (secció 10.1.3).

10.1.1 Visió General

El procés de validació d’un esquema conceptual a partir de proves automàtiques permet incre- mentar la qualitat de l’esquema conceptual, però mai assegurar que la qualitat assolida ja és la òptima. Sempre es podrien definir i executar nous casos de test.

Per aquest motiu, és important definir quins aspectes es consideren crítics a l’hora de validar l’esquema dissenyat, prioritzar les validacions a realitzar i seleccionar les que es duran a terme.

L’aspecte més crític del sistema Magento és aquell coneixement que permet realitzar la fun- ció per a la qual ha estat dissenyat: la compra d’un producte per part d’un subjecte, a través d’Internet, que ha estat posat a la venda per un altre subjecte. Aquest procés es representa a

340 l’esquema conceptual amb els següents esdeveniments:

I NewSession: un usuari no identificat entra al sistema Magento.

I LogIn: l’usuari s’identifica com a un client registrat.

I AddProductToShoppingCart: el client afegeix un o més productes al carro de la compra.

I OrderConfirmation: el client confirma que vol comprar els productes que ha afegit anteri- orment al carro de la compra.

I NewProduct: l’administrador de la botiga dóna d’alta un nou producte a la botiga.

Per a validar correctament un esdeveniment, la Base d’Informació de l’esquema conceptual ha de ser consistent, és a dir, no violar cap de les restriccions d’integritat definides. Per aquest motiu, per a dur a terme la validació dels esdeveniments anteriors es fa necessari validar també la consistència de esquema estructural.

10.1.2 Descripció de la Fixture

Per al disseny dels casos de test, cal definir l’estat inicial (previ a l’execució de les proves) de la Base d’Informació. Per a referir-nos a la definició d’aquest estat inicial utilitzarem l’anglicisme “Fixture”. En un procés de validació per proves automàtiques, es pot definir més d’una Fixture. Cada cas de test, però, n’utilitza una única. Per a la validació de l’esquema conceptual de Magento, definirem una única Fixture per a tots els casos de test. Instanciarà els elements bàsics que permeten obtenir un estat de la BI consistent amb l’esquema conceptual dissenyat. Conceptualment, l’estat de la Base d’Informació un cop carregada la Fixture representa l’estat del sistema abans que cap usuari l’hagi utilitzat mai. Els elements definits són els següents:

I Una estructura de Websites: dos Websites, cada un amb una Store i una StoreView. Es defineixen també dos Languages i una Timezone.

I Les instàncies de Store Configuration, en tres àmbits: System, Website i StoreView.

I Les instàncies de Product Properties Configuration.

I Els PaymentMethods i ShippingMethods, en tres àmbits: System, Website i StoreView.

I Altres elements necessaris: una Category, dos CustomerGroups, una CustomerTaxClass i les Currencies de Euro i Dòlar americà, amb la respectiva conversió.

Els Fixture Components són declaracions d’instàncies que amplien l’estat inicial de la Base d’In- formació definit a la Fixture. Des d’un cas de test, poden carregar-se dinàmicament un o més Fixture Components.

Els Fixture Components dissenyats per a la validació de l’esquema conceptual de Magento es descriuen a continuació:

Fixture Component: Administrator

Crea un nou Administrator i un nou Role amb tots els permisos activats. Assigna el Role a l’Administrator creat.

341 Fixture Component: Anonymous Session

Crea una nova AnonymousShoppingCart associada a una nova AnonymousSession.

Fixture Component: Customers

Crea dos Customers associats al mateix Website.

Fixture Component: Price Rules

Crea una Price Rule, la seva especificació a nivell de StoreView i una Condition.

Fixture Component: Products

Crea els següents elements:

I Un Product, dos Attributes i els AttributeRating i Value corresponents. Els Attribute Rating es defineixen a nivell de System (GlobalAttributeRating), Website (WebsiteAttributeRating) i StoreView (StoreViewAttributeRating).

I Altres elements necessaris: una instància de TaxRate, una ProductTaxClass i un Attribute- Set.

10.1.3 Descripció dels casos de test

Cada cas de test dissenyat correspon a una “història”: una succeció de fets que l’usuari duu a terme quan interactua amb el sistema. Per a dissenyar les històries que han donat lloc als casos de test descrits en aquesta secció, s’ha recorregut a l’experimentació amb el sistema, duent a terme els passos de l’usuari i observant el resultat obtingut. A continuació, es detalla cada un dels casos de test dissenyats, acompanyats d’una breu descripció i del resultat que s’espera obtenir.

Esquema estructural

El primer cas de test valida la consistència de la Base d’Informació quan només s’ha declarat la Fixture. Ho fa assegurant que totes les restriccions d’integritat definides es compleixen en l’estat inicial de la BI.

Esdeveniment de domini: NewSession

Per aquest esdeveniment s’han definit dos casos de test. El primer cas prova l’esdeveniment en un entorn corrent, on s’espera que l’estat de la BI satisfaci totes les precondicions. El segon cas defineix dos esdeveniments NewSession amb la mateixa IPAddress, esperant que el segon esdeveniment no pugui ocórrer, doncs la IPAddress identifica una sessió.

342 Esdeveniment de domini: LogIn

S’han definit quatre casos de test. El primer cas prova l’esdeveniment en un entorn corrent, on s’espera que l’estat de la BI satisfaci totes les precondicions. Seguidament, es prova el LogIn d’un Customer que tingui definida la seva lastCurrentCurrency, esperant que l’esdeveniment ocorri també correctament.

Finalment, dues proves esperen la no ocurrència de l’esdeveniment, assignant a dues instàn- cies el mateix Customer o intentant el LogIn d’un customer en un Website on no està registrat.

Esdeveniment de domini: AddProductToShoppingCart

S’han definit set casos de test per a aquest esdeveniment.

Els dos primers proven l’esdeveniment en un entorn corrent, on s’espera que l’estat de la BI satisfaci totes les precondicions. En el primer cas, el Product s’afegeix a un CustomerShopping- Cart, mentre que per al segon s’utilitza un AnonymousShoppingCart. En els tres testos següents s’espera la no occurrència de l’esdeveniment: afegint un pro- ducte que no està activat (status = #Disabled), no disponible al Website actual o fora d’estoc (stockStatus = #OutOfStock). Els dos últims testos validen que tota la postcondició ha estat provada, preparant la base d’informació per validar les branques de la condició “if” no afectades pels dos primers testos. El primer cas consisteix a afegir un Product ja existent al ShoppingCart, validant que el producte segueix apareixent només una vegada però n’incrementa la quantitat d’elements. El segon cas comprova que l’estoc total d’aquell producte decreix quan el producte s’afegeix al ShoppingCart.

Esdeveniment de domini: OrderConfirmation

Vuit casos proven l’esdeveniment OrderConfirmation. Tres casos de test proven l’esdeveniment en un entorn corrent, on s’espera que l’estat de la BI satisfaci totes les precondicions. Cada un d’ells el prova amb un tipus diferent de ShoppingCart: Anonymous, Customer o Administration. Els dos casos següents proven la no occurrència de l’eseveniment quan el ShippingMethod triat està deshabilitat, o quan l’import de la Order no arriba a l’import mínim necessari pel Pay- mentMethod seleccionat.

Altra vegada, tres proves validen la correctesa de parts de la postcondició que s’han provat: en dos casos la Order s’associa a una Currency diferent de la definida per defecte pel sis- tema (concretament, s’associa a la ’websiteRedefinedBaseCurrency’ o la ’currentCurrency’ de la Session actual). En l’últim cas, s’afegeixen a la Order preus personalitzats (customPrice) i descomptes.

Esdeveniment de domini: NewProduct

Un total de nou casos d’ús proven aquest esdeveniment de domini. El primer de tots prova l’esdeveniment en un entorn corrent, on s’espera que l’estat de la BI satisfaci totes les precondicions.

343 El segon cas dóna d’alta dos esdeveniments NewProduct amb el mateix SKU1, esperant la no ocurrència del segon. Els set casos de test restants intenten violar les restriccions d’integritat inicials relacionades amb els valors de les propietats (atributs UML) del Product que es pretén crear o els objectes Attribute relacionats. En els 7 casos, doncs, s’espera la no ocurrència de l’esdeveniment.

10.2 Resultats de les proves

Després de diverses iteracions, s’han obtingut veredictes afirmatius per a les proves especifi- cades. Per presentar-ne una evidència, a continuació es mostra una captura de pantalla de l’eina CSTLProcessor indicant que la bateria de test ha obtingut un veredicte afirmatiu en el seu conjunt.

Figura 10.1: Captura de pantalla de l’eina CSTL, després de l’execució dels testos realitzats.

Tanmateix, durant l’execució de les proves s’han detectat i corregit múltiples errors2. Aquests errors s’han documentat i s’analitzen a les següents seccions.

1SKU són les incicials de Stock-keeping unit, o número de referència, un identificador utilitzat amb l’objectiu de per- metre el seguiment sistemàtic de productes i serveis ofets als clients. 2L’esquema especificat en els capítols 5, 6 i 7 inclou ja les correccions descrites.

344 10.2.1 Errors detectats durant l’especificació de l’esquema en USEx

Per poder interpretar correctament l’esquema conceptual desenvolupat, l’eina USEx necessita assegurar la seva correctesa sintàctica. Per aquest motiu, l’especificació en USEx ha permès detectar una primera onada d’errors, tant a l’esquema estructural com a l’esquema de compor- tament.

Els errors detectats en l’especificació USEx de l’esquema de comportament s’han documen- tat i s’analitzen en aquesta secció. En total, s’han detectat, documentat i resolt 299 errors. Els classifiquem en quatre grups:

I Sintàctic o lèxic: l’esquema especificat conté expressions que no tenen una estructura permesa per l’especificació UML/OCL, utilitzen operacions no definides o bé són incoher- ents amb l’estructura de classes definida. Habitualment, aquests errors es deuen a errors ortogràfics, a una interpretació errònia de l’especificació UML/OCL, a una interpretació er- rònia de l’estructura de classes definida a l’esquema.

I Semàntic: l’esquema especificat és incomplet o incorrecte, i no representa acuradament el coneixement del domini. Si bé l’objectiu d’aquesta fase no és detectar aquest tipus d’errors, en alguns casos la inspecció de l’esquema duta a terme per corregir-ne d’altres ha permès detectar-los també.

I Adaptació a la sintaxi USEx: l’esquema especificat utilitza expressions correctes de l’e- specificació UML/OCL que USEx no reconeix. Aquests errors afecten habitualment a les restriccions d’integritat.

I Canvis no actualitzats: l’error es troba en una expressió que ja s’havia validat satisfactòri- ament amb anterioritat, però ha esdevingut incorrecta després d’una modificació a l’esque- ma conceptual introduïda posteriorment.

A continuació, es detallen el nombre d’errors detectats per cada tipologia:

Figura 10.2: Tipologia dels errors detectats durant l’especificació de l’esquema en USEx

345 10.2.2 Errors detectats en l’execució de les proves automàtiques

L’execució de les proves de test amb l’entorn CSTL ha detectat 170 errors en l’esquema especifi- cat, ja sigui a l’esquema estructural, l’esquema de comportament o a un dels cinc esdeveniments de domini detallats a la secció 10.1.

Els presentem classificats en els mateixos grups definits a la secció 10.2.1, però introduïnt els següents canvis.

I Semàntic: en aquest cas, l’error es detecta en obtenir un resultat negatiu o erroni en algun cas de test.

I Adaptació al CSTLProcessor: el model especificat utilitza expressions correctes de l’e- specificació de UML/OCL que l’eina CSTLProcessor no reconeix (per exemple, la utilització d’atributs i relacions derivades en pre i post condicions).

I Introduït en la conversió a USEx: l’especificació USEx no correspon a l’esquema con- ceptual original dissenyat. Per tant, s’han introduït errors durant l’especificació en USEx.

A continuació, es detallen el nombre d’errors detectats per cada tipologia:

Figura 10.3: Tipologia dels errors detectats en l’execució de les proves automàtiques

346 10.2.3 Conclusions

La validació dels esquemes conceptuals com a una activitat necessària

Durant el procés de validació s’han detectat aproximadament 400 errors en l’esquema concep- tual. Per tant, quan la fase d’elicitació de l’esquema es va donar per tancada, l’esquema no representava correctament el coneixement del seu domini. S’ha utilitzat també la tècnica de la inspecció i revisió de forma complementària, i es podrien haver utilitzat altres tècniques per millorar la qualitat de l’esquema, però s’ha comprovat que les proves automàtiques són un bon mètode per a aquesta tasca.

Validació sintàctica i semàntica

Ambdues activitats, detallades a les seccions 10.2.1 i 10.2.2 respectivament, són complemen- tàries. La primera es focalitza en errors de forma: errors a la sintaxi OCL, tipus de definició incorrectes o camins OCL erronis són els errors més comuns.

La validació semàntica, en canvi, es pot centrar en detectar el coneixement que no s’ha representat correctament o de manera incompleta en l’esquema conceptual dissenyat. Degut a que USEx no fa una validació exhaustiva de l’esquema, les proves automàtiques han revelat també errors sintàctics que no s’havien descobert anteriorment.

Una eina completa per a la modelització conceptual

En l’execució de les proves automàtiques s’han detectat 70 errors introduïts en l’esquema du- rant l’especificació en sintaxi USEx. En aquest cas, el coneixement s’havia elicitat i representat correctament en UML/OCL, però l’especificació manual en sintaxi USEx havia degradat la seva qualitat. En altres casos, la sintaxi de USEx no reconeixia les estructures UML escollides per al disseny de l’esquema. Aquestes casuístiques exemplifiquen la necessitat d’una eina de software que cobreixi el procés de modelització complet, incloent les següents activitats:

1. Especificació del model en UML/OCL. Incloent totes les estructures definides per l’especi- ficació més actualitzada de UML/OCL (veure [12, 10]). És necessari també que l’eina doti l’esquema d’executabilitat automàticament. 2. Suport per a la documentació del model. Generar una documentació extensa com la d’aquest projecte hauria de ser automàtic a partir del model definit, afegint només els apartats de descripció textuals i els detalls organitzatius (fragmentació en subesquemes, agrupació en grups de subesquemes, aspectes de presentació, etc.). És necessari que la eina proporcioni una la capacitat de representar gràficament els conceptes UML. 3. Especificació i execució de les proves automàtiques. Totes les funcionalitats ofertes per CSTL es podrien incloure en aquesta eina.

La combinació de USEx/CSTL cobreix l’últim i parcialment el primer d’aquests requisits.

347 Incorporar nous actors en la fase de test

En el projecte actual, les activitats d’elicitació de l’esquema conceptual i l’especificació i execució dels casos de test s’han dut a terme per l’autor. Això pot evitar la detecció d’errors introduïts du- rant l’elicitació del domini: si l’autor no ha interpretat correctament els requisits del sistema, les proves especificades esperaran un comportament incorrecte, en lloc de representar el coneixe- ment real del domini.

En un projecte d’enginyeria, és crític fer participar les parts interessades en el sistema en el procés d’especificació de les proves, i contrastar amb ells els resultats esperats i obtinguts. En un projecte d’enginyeria inversa, com és el cas que ens ocupa, és poc habitual disposar de la opinió de les parts interessades en el sistema. El seu coneixement pot ser substituït per l’experimentació amb el sistema, duent a terme exactament les mateixes accions que es descriuen a l’esquema conceptual. En tots dos casos, és positiu que participin diversos actors en les diferents fases del procés.

10.3 Avaluació de l’entorn CSTL/USEx

Les eines USEx i CSTLProcessor desenvolupen correctament les funcions per a les quals han estat dissenyades. En aquest projecte, han permès dotar d’executabilitat l’esquema conceptual dissenyat, dissenyar els diferents casos de test i executar-los.

Durant la realització d’aquest projecte, però, també s’hi han detectat diverses limitacions. En tots els casos, l’autor del projecte i el desenvolupador de les pròpies eines han ideat con- juntament alternatives per superar aquestes limitacions, mantenint alhora la representativitat de l’esquema. L’ús d’aquestes eines de manera intensiva ha permès el·licitar algunes suggerències a nivell funcional, sorgides amb la intenció d’ampliar les possibilitats i la usabilitat de les eines, que es detallen a les seccions 10.3.1 i 10.3.2. Addicionalment, s’han detectat i documentat algunes situacions en què la eina CSTLProcessor no segueix correctament el comportament especificat, que es detallen a la secció 10.3.3.

10.3.1 Suggeriments de millora per a l’eina USEx

Modulabilitat

Permetre l’especificació de l’esquema conceptual en diversos documents. Introduïr el concepte de mòdul de l’esquema conceptual, que podria ser equivalent al concepte de fitxa amb què s’ha estructurat la documentació d’aquest projecte. Per a la suggerència detallada al punt 1 de la secció 10.3.2, caldria dotar USEx de la capacitat d’activar/desactivar-ne alguns mòduls de forma dinàmica, mantenint l’esquema complet carregat.

Usabilitat

Ampliar la sintaxi de l’eina USEx amb un símbol o conjunt de símbols que permetin definir un bloc de codi com a comentari, eliminant-ne la seva rellevància per a la interpretació de l’esquema. El

348 símbol utilitzat actualment, el doble guió, s’ha d’introduïr al principi de cada línia que es vulgui comentar.

Expressivitat de l’esquema conceptual

USEx reconeix un subconjunt dels conceptes definits per l’especificació de UML/OCL (veure [12, 10], que generalment és suficient per expressar el coneixment que es vol representar en la majoria d’esquemes conceptuals. En el cas de l’esquema dissenyat en aquest projecte, però s’ha cregut convenient utilitzar una jerarquia de classes associatives per a la definició dels mè- todes de pagament i transport (ShippingMethods i PaymentMethods). USEx no reconeix aquesta estructura.

Per a la fase de proves, aquests conceptes han estat substituïts per altres que permetien expressar un coneixement raonablement semblant, amb la mínima pèrdua de precisió. Ara bé, ampliar encara més l’expressivitat dels esquemes conceptuals reconeguts per USEx permetria evitar aquesta pèrdua de precisió, a més del temps invertit en la substitució de conceptes.

10.3.2 Suggeriments de millora per a l’eina CSTLProcessor

Modulabilitat

Treballar amb un esquema conceptual relativament gran pot dificultar l’execució de la fase de test. Amb la intenció de poder focalitzar cada prova o conjunt de proves en aspectes concrets de l’esquema, s’han formulat les següents recomanacions:

1. Utilitzar l’estructura de mòduls de l’esquema conceptual suggerida a la secció 10.3.1 per a l’execució de testos sobre mòduls o conjunts de mòduls de l’esquema conceptual. És a dir, permetre restringir el CSUT a l’esquema especificat en els mòduls “actius”. En el cas de Magento, és raonable exectuar un test que abarqui només la creació de sessions i l’autenticació dels usuaris. En l’actualitat, carregar el model complet implica també definir molts altres aspectes (mètodes de pagament i transport, per exemple) que no hi tenen relació perquè totes les restriccions d’integritat es compleixin. La casuística es dóna quan les restriccions d’integritat impedeixen que la població d’una clase sigui buida. La incorporació d’aquesta possibilitat, però, planteja el següent interrogant: la desacti- vació d’algunes parts de l’esquema conceptual pot evitar la detecció d’errors existents en el subesquema provat. Malgrat que aquell subesquema es considerarà correctament validat, en ampliar el CSUT a l’esquema conceptual complet poden aflorar aquests errors, violant la propietat de no-regressió3 dels testos. Per resoldre-ho es plantejen dues possibilitats:

(a) Enfocar l’estratègia de test dins el paradigma de les proves d’integració. Fragmentar l’esquema en subesquemes (components) validats de manera independent, en una primera fase, i definir una segona fase on els subesquemes es proven conjuntament4. (b) Implementar la declaració de valors i un estat de la Base d’Informació per defecte al propi esquema conceptual, eliminant així la necessitat d’instanciar objectes per a totes aquelles classes que no intervenen en el cas de test corresponent.

2. Definir la fixture i els fixture components en un document específic, tal i com es defineixen els mètodes. En la fase de proves d’aquest projecte, tots els testos partien d’una mateixa

3La propietat de no-regressió en els testos verifica que les funcionalitats validades en versions prèvies del SUT es mantenen després de la incorporació de noves funcionalitats (veure [4]). 4Habitualment la integració dels diferents components es fa de manera progressiva i incremental

349 fixture base, motiu pel qual s’han definit en el mateix fitxer. Seria interessant poder classi- ficar els casos de test en diferents fitxers, mantenint tots la mateixa fixture. 3. Definir des de l’eina CSTLProcessor quins tests s’executaran, sense necessitat de moure de carpeta el fitxer de definició. La definició pot ser a nivell de cas de test o de testprogram. 4. Automatitzar la relació de mòduls actius per a cada test program, permetent executar la bateria de testos completa. 5. Oferir la possibilitat de validar la definició dels mètodes de manera independent a l’execució dels casos de test, tal i com ja es permet fer actualment amb el CSUT.

Detecció d’errors

La verificació de l’esquema conceptual a partir de proves executables va estretament lligada a la detecció i correcció d’errors. L’eina CSTLProcessor ofereix algunes eines que faciliten aquesta detecció, que es podrien ampliar incorporant les següents recomanacions:

1. Oferir la possibilitat de consultar l’estat de la Base d’Informació en un punt concret de l’execució del test, ja sigui gràficament o com a sortida de text. Aquesta funcionalitat està incorporada en la majoria d’eines de detecció d’errors en el programari tradicional, que permeten consultar en tot moment l’estat de les variables definides. Cal destacar que USEx ja permet obtenir una representació gràfica de la Base d’Informació definida a partir d’un fitxer d’entrada. 2. Detallar el resultat de les proves a nivell de assert statements. Actualment el resultat es detalla a nivell de cas de test. 3. Dotar de més expressivitat els missatges de fallada o error d’un cas de test. En l’avaluació de pre/post-condicions amb resultat negatiu, per exemple, determinar el motiu pel qual aquella condició s’ha avaluat com a falsa.

Documentació

L’eina CSTLProcessor disposa d’una documentació funcional (veure [17]) que especifica la sin- taxi utilitzada per a l’especificació de la fixture i els casos de test. Seria interessant ampliar aque- sta especificació a l’especificació dels esdeveniments, especialment per a les construccions més complexes de UML.

Usabilitat

CSTLProcessor incorpora un editor de text molt complet que permet modificar els documents d’especificació de l’esquema conceptual i la implementació dels mètodes. De la mateixa manera que ho permeten altres eines d’edició de text, seria interessant detectar si el document s’ha modificat amb alguna eina externa mentre estava obert amb CSTLProcessor, i plantejar a l’usuari si vol mantenir la versió carregada a l’eina o recarregar la última versió guardada en disc.

Expressivitat de l’esquema conceptual

CSTLProcessor reconeix un subconjunt dels conceptes definits per l’especificació de UML/OCL5, que generalment és suficient per expressar el coneixment que es vol representar en la majoria

5L’especificació formal d’ambdós llenguatges es pot trobar a [12, 10].

350 d’esquemes conceptuals. En l’esquema dissenyat en aquest projecte, s’ha cregut convenient utilitzar freqüentment el concepte de Tupla i el de Datatype, tant per la part estàtica com dinàmica, conceptes no reconeguts per l’eina CSTLProcessor. Per a la fase de proves, aquests conceptes han estat substituïts per altres que permetien expressar un coneixement raonablement semblant, amb la mínima pèrdua de precisió. Permetre que l’eina reconegui els conceptes de Tupla i Datatype permetria evitar aquesta pèrdua de precisió, a més del temps invertit en la substitució de conceptes.

10.3.3 Errors detectats en l’entorn de test

Funcionalitats implementades

S’han detectat dues funcionalitats ofertes pel CSTLProcessor que no funcionen tal i com es- tan especificats. En tots dos casos, però, es pot aconseguir el comportament destijat utilitzant solucions alternatives.

En primer lloc, l’entorn de test no calcula el valor dels atributs i extrems d’associacions derivades. Si es requereixen per a l’especificació de restriccions d’integritat o de pre/post condi- cions, és necessari substituir l’atribut o associació per una operació equivalent. En segon lloc, les instruccions de assert per a esdeveniments de domini s’apliquen sobre l’estat de l’esdeveniment en el moment de la seva creació, obviant els canvis que s’hagin produït posteriorment.

Estructures sintàctiques

S’han detectat errors d’interpretació de l’eina CSTLProcessor en expressions del codi que, o bé s’adapten a la sintaxi definida a la documentació, o bé en constitueixen una variació menor no reconeguda, però que facilitaria la tasca de codificació. Es detallen a continuació, acompanyats d’una instrucció d’exemple.

Estructura Instrucció d’exemple Creació de diverses instàncies en obj1 := new CLASS1(attr1 := obj2; attr2 := una mateixa instrucció. new CLASS2(attr3 := obj3)); Creació d’una variable i assig- obj1.attr1 := new CLASS1(attr2 := obj2); nació a una propietat en la mateixa instrucció. Utilització d’una variable definida obj1 := new CLASS1(attr1 := obj2); anteriorment en les expressions assert equals true obj3.attr1->exists(a|a = de OCL exists, forAll i select. obj1) Utilització d’una variable definida obj1 := new CLASS1(attr1 := obj2); anteriorment en expressions OCL assert equals true [obj1 = obj3] declarades entre els símbols [ i ].

351 Part IV

Documentació del treball

352 11 Eines utilitzades

En el capítol actual es presenten les eines utilitzades per a la realització del projecte. En la secció 11.1 es descriu breument l’eina i l’ús que se n’hi ha donat. La secció 11.2 és un quadre resum amb la versió, llicència i desenvolupador de cada una d’elles.

11.1 Descripció

11.1.1 Magic Draw

Magic Draw és una eina de suport a la modelització i disseny de software, bases de dades, processos de negoci, etc. Implementa la majoria d’elements del llenguatge UML i disposa d’una interfície d’edició de diagrames eficaç i intuïtiva, que la converteixen en una eina molt adeqüada per a la documentació d’esquemes conceptuals. Tots els diagrames que conformen l’esquema conceptual han estat dissenyats amb Magic Draw, i en la majoria dels casos en un entorn Windows.

La llicència de Magic Draw és comercial. Si bé n’existia una versió lliure, va deixar de ser disponible abans de començar el projecte. Exposant l’interès en utilitzar aquesta eina per a finalitats acadèmiques a l’empresa desenvolupadora, es va concedir a l’autor del projecte una llicència de prova per versió 16.0 Enterprise Edition. La llicència caducava a finals de novembre del 2010, però a petició de l’autor va ser allargada fins al març de 2011.

S’ha valorat com a alternativa les eines ArgoUML i StarUML, totes dues gratuïtes, però les seves possibilitats eren sensiblement menors i dificultaven la correcta execució del projecte. L’ex- periència anterior de l’autor i el co-director del projecte en MagicDraw també va afavorir l’elecció d’aquest software.

353 11.1.2 Entorn XML

XML és un metallenguatge, és a dir, una manera de definir llenguatges. Va ser desenvolupat pel World Wide Web Consortium com a un estàndard per a l’intercanvi d’informació estructurada entre diferents plataformes. Existeixen múltiples aplicacions que permeten extreure o llegir infor- mació en llenguatges definits amb XML: bases de dades, fulls de càlcul, fitxers de configuració, llicències de software, etc. XHTML o SVG són llenguatges definits en XML i utilitzats en la web.

XSLT és un llenguatge definit amb XML que permet especificar transformacions de fitxers escrits en qualsevol llenguatge XML. Habitualment s’utilitza per a transformar documents XML en pàgines web en HTML o XHTML, però les seves possibilitats permeten la conversió a qualsevol format del qual se’n conegui l’especificació. En el projecte que ens ocupa, l’esquema conceptual de Magento s’ha plasmat en diversos suports i per a dues finalitats diferents. D’una banda, per ser llegit i revisat per part de l’autor i, fi- nalment, incloure’l en aquesta memòria. De l’altra, per ser interpretat per les eines que s’utilitzen en la fase de proves. Ambdues representacions de l’esquema parteixen d’una mateixa infor- mació bàsica, però poden contenir elements diferenciats (les descripcions dels sub-esquemes estructurals o la definició dels casos d’ús, per exemple, no apareixen a la respresentació de l’esquema que es sotmet a les proves automàtiques). Per aquest motiu, és interessant per a l’autor mantenir una separació entre l’especificació en si d’aquests elements i la seva representació en cada cas, simplificant les tasques de modificació i evitant la introducció de diferències entre una versió i l’altra. Amb aquest objectiu s’han dissenyat diversos fitxers XML on s’ha emmagatzemat la informa- ció relativa a l’esquema. Per a obtenir la documentació resultant (en format LATEX), s’han desen- volupat les corresponents fulles de transformació XSLT. Durant la realització del projecte, s’ha generat també, per a ús intern, una documentació d’aspecte semblant a la memòria en format HTML, que no s’inclou en el treball. Per a la realització de les proves, s’han desenvolupat també fulles XSLT que transfomen part del model al llenguatge reconegut per l’entorn USEx/CSTL.

Per a l’edició de fulls XML i XSLT s’han utilitzat dues eines de software. XMLCopyEditor en Windows i EditiX XML Editor en Linux. Totes dues eines s’han utilitzat tant com a editors de text com per a les transformacions XSLT. Per a aquesta última tasca s’ha utilitzat també Saxon que, mitjançant una interfície per línia de comandes, permet transformar múltiples fitxers alhora.

11.1.3 Zend Server

Zend Server és un entorn que permet simular en una sola màquina la comunicació client-servidor pròpia dels sistemes dissenyats en PHP. El paquet utilitzat inclou la instal·lació del servidor Apache, intèrpret de PHP5 i gestor de base de dades mySQL. Durant la realització del projecte, s’havien fet proves amb les eines easyPHP (Windows) i LAMP (Linux), però l’aparició de diversos errors en la instal·lació i ús va fer que es descartéssin. La versió de Magento instal·lada a l’equip personal de l’autor, utilitzada per a l’experimentació amb el sistema, s’ha executat en l’entorn Zend Server sobre un sistema operatiu Windows.

11.1.4 Entorn de les proves automàtiques

Les eines utilitzades per a les proves automàtiques (USE, USEx i CSTL) han estat específica- ment descrites a la secció 9.3. En tots els casos, es tracta d’eines desenvolupades en l’entorn acadèmic. Són gratuïtes i multiplataforma.

354 11.1.5 Sistemes operatius

Per a l’execució del projecte, s’ha alternat l’ús de dos sistemes operatius: Windows Vista i Lin- ux Ubuntu 10. Per al primer es disposava d’una llicència original mentre que el segon és de distribució lliure. En cada punt del treball, s’ha escollit aquell sistema operatiu que més s’adaptava a les ne- cessitats del software amb el qual s’estava treballant.

11.1.6 Edició de text i documentació

S’han utilitzat múltiples editors i processadors de text. A més dels editors de text comentats en l’apartat d’entorn XML, s’ha utilitzat els editors ged- it (Linux) i Notepad++ (Windows). La documentació interna s’ha redactat en OpenOffice 3.2 (Windows i Linux).

S’ha escollit l’entorn LATEX per a documentar la memòria final del projecte. Els documents LATEX s’han generat automàticament a partir de les fulles XSLT per a la documentació de l’esque- ma conceptual. El procés de generació del document ha utilitzat també les eines sed, find i rename de Linux. La resta de la memòria s’ha documentat amb l’ajuda de les eines Kile i LYX (Linux).

La transformació dels documents LATEX a PDF s’ha dut a terme amb l’eina pdflatex (Linux).

355 11.2 Resum de llicències i versions

El projecte s’ha realitzat sense necessitat d’incórrer en despeses per llicències de Software, ja sigui per a la utilització d’eines gratuïtes o perquè l’entitat propietària n’ha concedit la llicència a l’autor gratuïtament. N’és una excepció el sistema operatiu Windows Vista, del que l’autor del projecte ja disposava amb anterioritat. No s’ha utilitzat software sense llicències. Per a aquelles eines en què existeix més d’una llicència, s’ha utilitzat sempre la llicència gratuïta. Es detalla a continuació un resum de les llicències amb què es distribuïa cada una de les eines descrites, i la versió que se n’ha utilitzat.

Eina Desenvolupador Llicència Versió MagicDraw NoMagic Inc. Pagament (gratuïta 16.0 Enter- per a fins acadèmics i priseEdition a petició) XML Comunitat GPL 1.2.0.6 CopyEditor open-source EditiX XML JADISoft Pagament (versió 020110 Editor gratuïta reduïda) FreeEdition Saxon Saxonica Pagament (versió 8.8 gratuïta reduïda) Zend Server Zend Pagament (versió Community Technologies gratuïta reduïda) Edition 5.0 USE Database GPL 2.5.0 Systems Group (University of Bremen) USEx Departament 1.0 ESSI - UPC CSTL Departament beta Processor ESSI - UPC gedit Comunitat GPL 2.30.2 open-source Notepad++ Comunitat GPL 5.6.8 open-source OpenOffice Comunitat LGPL 3.2 open-source Kile Comunitat GPL 2.1 open-source LYX Comunitat GPL 2.0 open-source

356 12 Aspectes organitzatius i econòmics

12.1 Planificació temporal

12.1.1 Planificació inicial

El projecte que ens ocupa va iniciar-se amb uns primers contactes entre l’autor i el director i co- director, entre desembre i gener dels anys 2009 i 2010. Amb aquests contactes, es van establir els objectius i l’abast. Al febrer de l’any 2010, es va planificar el següent calendari per a la realització de les tasques que comportava. Adicionalment, es va acordar amb el co-director del projecte que les reunions de seguiment es farien amb periodicitat quinzenal.

357 Figura 12.1: Planificació inicial del projecte

12.1.2 Seguiment real

Al llarg del projecte, la planificació inicial s’ha vist modificada per diversos motius:

I L’esforç necessari per algunes de les tasques planificades ha set considerablement supe- rior al que s’havia planificat. Especialment, en les tasques d’Adquisició de Coneixement i de confecció de l’Esquema Conceptual. La complexitat del sistema analitzat era superior a l’esperada.

I La decisió de documentar l’esquema el·laborat en format XML ha comportat l’aparició de tasques auxiliars que no estaven planificades: la definició de l’estructura d’aquests docu- ments i la seva transformació a LATEX per a incloure’ls al document final.

I La situació laboral de l’autor s’ha vist modificada a partir de l’1 de setembre de 2010, reduïnt considerablement el nombre d’hores setmanals de dedicació. A partir de setembre, les reunions s’han realitzat amb periodicitat bimensual.

358 Figura 12.2: Seguiment temporal del projecte

359 12.2 Valoració econòmica

El cost econòmic d’aquest projecte, suposant que es realitzés en un entorn laboral/comercial, prové de dos conceptes:

I Les hores invertides per l’autor, co-director i director del projecte.

I El cost de la llicència de l’eina de modelització MagicDraw, que no s’hauria pogut obtenir de forma gratuïta.

El cost de les hores invertides s’ha calculat assumint que el projecte ha estat desenvolupat per un analista, i que el co-director i el director realitzen les tasques de cap de projecte. Els salaris mitjans dels quals s’ha partit són els següents 1:

I Analista: 17,04 C / hora

I Cap de projecte: 22,44 C / hora.

Comptabilitzant que la dedicació de de cada una de les parts ha estat la següent:

I Analista: 1181 hores, detallades a la secció 12.1.2.

I Cap de projecte: 40 hores, desglossades de la següent forma:

– Reunions fins al 31 d’agost: 2 hores/reunió x 2 reunions/mes x 8 mesos = 32 hores. – Reunions a partir de l’1 de setembre: 2 hores/reunió x 0.5 reunions/mes x 8 mesos = 8 hores.

Es presenta a continuació el cost econòmic de les hores invertides:

I Analista: 1181 hores x 17,04 C/hora = 20.103 C

I Cap de projecte: 40 hores x 22,44 C/hora = 897 C

Així doncs, el cost total de les hores invertides en aquest projecte ascendeix a 21.000 C.

El cost de la llicència d’explotació de MagicDraw corresponent a la distribució que s’ha utilitzat en aquest projecte, la Enterprise Edition, ascendeix actualment a 1.355 C2.

1El salari brut anual mitjà s’ha consultat a http://salarios.infojobs.net. Ambdós salaris s’han calculat a partir del salari brut anual mitjà, de 30.000 i 39.500 C respectivament, i considerant una jornada estàndar de 1.760 hores anuals. 2http://www.magicdraw.com/main.php?ts=navig&menu=pricing®ion=1&cmd_show_pricing=Show+Prices

360 12.3 Possibilitats de treball futur

La limitació d’abast realitzada en diversos àmbits del projecte que ens ocupa permet que la tasca realitzada sigui continuada per altres treballs, tinguin o no la naturalesa de projecte final de carrera. Identifiquem a continuació els aspectes més rellevants.

1. Refinament de l’esquema conceptual dissenyat.

Tal i com es detalla a la secció 7.1, en aquest projecte s’ha limitat l’abast en l’especificació dels esdeveniments. Per obtenir l’esquema complet, caldria dur a terme una segona iteració especificant els esdeveniments que no han estat especificats en l’actualitat i refinar els actuals a partir dels conceptes que han quedat fora de l’abast. És especialment interessant l’especificació dels esdeveniments referits específicament a Pro- ductes No Simples i la refinació d’aquells en què, en el projecte actual, s’ha assumit que l’esde- veniment sempre tractava amb un SimpleProduct.

2. Ampliació de la fase de proves a l’esquema complet dissenyat.

En aquest projecte, la fase de proves s’ha limitat als esdeveniments necessaris per a realitzar la compra d’un producte (veure la secció 10.1.1). Vista l’experiència positiva de la fase de proves actual, seria molt interessant desenvolupar un pla de proves per a l’esquema complet i realitzar- ne l’execució. Aquesta tasca té una alta càrrega de treball, i podria plantejar-se com a un nou projecte final de carrera.

3. Actualització de l’esquema conceptual dissenyat.

Magento és un sistema en evolució, i això comporta que el coneixement que representa es vegi ampliat o modificat a mesura que apareixen noves versions. Mantenir l’esquema actualitzat és una tasca constant i necessària perquè l’esquema no quedi obsolet. Tal com es detalla a la secció 3.3, l’esquema conceptual desenvolupat en aquest projecte correspon a la Community Edition v1.4.0.1.

4. Revisió, desenvolupament i millora de l’entorn de proves USEx/CSTL.

Les eines utilitzades estan encara en fase de desenvolupament i millora contínua, i tenen com a objectiu la definició d’una metodologia de disseny d’esquemes conceptuals orientada a les proves [17]. En el grup de treball GMC de la UPC hi treballa actualment, i especialment en el marc d’una tesi doctoral. Els suggeriments elaborats a la secció 10.3 del projecte que ens ocupa pretenen ser una contribució a aquesta recerca.

361 13 Conclusions

En relació a la Modelització conceptual

I La modelització conceptual és una de les activitats de l’enginyeria del software i té com a resultat principal l’obtenció d’un esquema conceptual.

I Els esquemes conceptuals representen el coneixement sobre el domini que un sistema d’informació necessita per tal de ser útil en la resolució de problemes i en el suport als processos i activitats desenvolupats en aquest domini. Saber el coneixement que requereix un sistema software abans d’implementar-lo és necessari.

I La gestió del coneixement a les empreses de desenvolupament de software, amb entorns en canvi constant, implica la necessitat que aquest coneixement estigui plasmat en docu- ments accessibles i permanents.

En relació a Els sistemes de comerç electrònic

I Han experimentat un gran creixement en els últims anys: el volum total de vendes per internet al conjunt de l’Estat el 2009 va ser de 7.760 milions d’euros i el 22,6 % de la població catalana assegura haver comprat per Internet en els últims 3 mesos.

I Permeten establir un canal de venda complementari a negocis ja existents, o desenvolupar nous negocis basats en l’e-comerç.

I Automatitzen múltiples processos habituals en una venda, reduint les despeses que aquesta suposa per al venedor.

362 En relació a Magento

I És actualment un dels sistemes majoritaris al mercat. Marques multinacionals com Nokia o Nespresso l’utilitzen per a la venda per Internet.

I És un sistema complex, que ofereix un conjunt de possibilitats molt ampli.

I Està format per dues parts clarament diferenciades: el portal de comerç electrònic, des d’on es realitzen les compres, i l’administració de la botiga.

I Disposa actualment de dues versions comercials i una de gratuïta, totes elles amb codi obert.

I Es nodreix d’una comunitat extensa de desenvolupadors independents que aporten coneixement i desenvolupen eines complementàries a Magento.

I No existeix cap esquema conceptual documentat i accessible del sistema.

En relació a L’esquema conceptual de Magento

I Representa el coneixement de conceptes molt diversos dins l’àrea del comerç elec- trònic: configuració general, paràmetres de connexió amb serveis de tercers, administració del catàleg de productes, registre i fidelització d’usuaris, recull d’informació d’activitat real- itzada a la botiga i gestió dels documents post-venda.

I Utilitza l’estructura de StoreViews, Stores i Websites com a base del seu coneixement.

I Té 218 entitats, 168 casos d’ús i 202 esdeveniments. Per les seves dimensions, es pot considerar un esquema gran.

En relació a El procés d’elicitació de l’esquema conceptual

I Hauria set positiu establir prèviament uns criteris de documentació del projecte: deci- sions preses en la modelització, experimentació realitzada, informació consultada en cada cas.

I L’experimentació amb l’esquema s’ha realitzat en diverses iteracions, analitzant la pràc- tica totalitat del sistema a cada una d’elles, i augmentant la profunditat de l’anàlisi progres- sivament.

I Recollint les conclusions anteriors, sembla necessari disposar d’una metodologia de treball i organitzativa per abordar un projecte d’aquestes característiques.

363 En relació a Les eines utilitzades en el projecte

I El projecte s’ha realitzat sense necessitat d’incórrer en despeses per llicències de Software, ja sigui per a la utilització d’eines gratuïtes o perquè l’entitat propietària n’ha concedit la llicència a l’autor gratuïtament.

I Les eines utilitzades han realitzat la funció per a les quals se les havia escollit. En alguns moments, però, han requerit un esforç addicional d’aprenentatge per part de l’autor del projecte.

I S’ha detectat la necessitat de disposar d’una única eina que cobreixi el procés de mod- elització conceptual complet. Aquesta eina hauria de permetre realitzar les activitats d’e- specificació en UML/OCL, documentació de l’esquema i realització de les proves.

En relació a Les eines USEx i CSTL

I La combinació de les eines USEx i CSTL és un punt de partida per a desenvolupar una eina de software que cobreixi totalment el procés de modelització conceptual. Ac- tualment, cobreix la fase de proves i, parcialment per motius d’expressivitat, l’especificació de l’esquema.

I L’expressivitat complementària de USEx ha estat clau per a representar la complexitat de l’esquema elaborat. Malgrat això, no reconeix encara algunes estructures complexes incloses en l’especificació de UML/OCL.

I L’entorn CSTL ha respost molt satisfactòriament a la realització de les proves d’esquemes conceptuals. El seu ús ha permès analitzar-ne les seves capacitats i mancances, i elaborar una llista de recomanacions futures.

I Per a la realització del projecte, ha set especialment beneficiós el contacte directe amb el desenvolupador de USEx i CSTL. La resolució de dubtes i errors, tant en l’ús de les eines com en el seu desenvolupament, s’han resolt amb la màxima celeritat.

En relació a Les proves d’esquemes conceptuals

I Constitueixen un mètode per a la validació d’esquemes conceptuals.

I No requereixen un gran esforç d’aprenentatge per a una persona amb coneixements de programació i enginyeria del software.

I Els casos de test especificats es poden obtenir fàcilment a partir de situacions quotidi- anes d’interacció amb el sistema, que poden ser expressats per actors sense coneixements d’enginyeria del software o programació.

364 I Han permès detectar un nombre considerable d’errors en l’esquema especificat, en un temps considerablement menor al que s’hauria invertit detectant els errors a través de la observació i revisió.

I És clau establir un mètode de validació dels casos de test especificats. En la majoria dels projectes, això es pot fer involucrant els actors que interactuaran amb el sistema en la definició de les proves de l’esquema conceptual, doncs permetran detectar errors d’inter- pretació del domini comesos per l’autor de l’esquema i/o el dissenyador de les proves. En un projecte d’enginyeria inversa, l’experimentació amb el sistema pot realitzar aquesta funció.

En relació a Les propostes de millora per a l’esquema conceptual de Magento

I S’han detectat aspectes de millora, relacionats amb la duplicitat de conceptes, la infra- utilització d’alguns i la inexistència d’identificadors de les instàncies.

En relació a La comparació entre Magento i osCommerce

I L’esquema conceptual de Magento amplia el coneixement relatiu a conceptes ja in- troduïts per osCommerce com l’estructura multibotiga, les propietats d’un producte o el càlcul de preus i impostos.

I Els esquemes conceptuals de Magento i osCommerce anomenen Attribute a dos con- ceptes lleugeraments diferenciats.

I Els esquemes conceptuals de Magento i osCommerce tenen coneixement sobre al- guns conceptes no comuns: documents posteriors a la venda, etiquetes de classificació dels productes per part dels usuaris o eines d’e-marqueting.

En relació a La documentació d’esquemes conceptuals grans

I Converteix en repte la comprensibilitat de l’esquema complet.

I S’ha realitzat descrivint l’esquema conceptual a partir de múltiples sub-esquemes que agrupen conceptes relacionats. Al seu torn, aquests sub-esquemes s’han organitzat en cinc grups.

I L’organització en grups de sub-esquemes s’ha mantingut en la descripció de l’esquema de comportament, afegint un sisè grup.

365 En relació a El treball previ realitzat en el projecte “Esquema conceptual de l’osCommerce”

I Ha estat un referent imprescindible per a realitzar el projecte que ens ocupa. Per la simil- itud entre els dos treballs, algunes idees i conceptes de l’esquema conceptual de l’osCom- merce i la seva memòria han servit de punt de partida, i han permès enfocar el treball a algunes àrees noves.

En relació a L’experiència personal adquirida

I Gràcies al projecte final de carrera, he pogut aplicar o desenvolupar coneixements rela- cionats amb l’Enginyeria Informàtica, molts d’ells vistos durant la carrera.

I He pogut aplicar en un cas real els coneixements obtinguts en les assignatures d’Enginyeria del Software.

I He conegut aspectes fonamentals del model de negoci dels sistemes de comerç elec- trònic.

I He planificat i executat una fase de proves sobre un sistema executable.

I He contribuït, en la mesura del que ha estat possible, en el context d’un treball acadèmic més extens: la elaboració d’una metodologia de desenvolupament d’esquemes conceptu- als.

I He tingut la oportunitat de treballar de forma autònoma, adquirint habilitats d’organització i planificació del temps i la feina, i modificant la planificació establerta quan les condicions ho han requerit.

I Globalment, el projecte ha requerit un esforç considerable, però ha estat una experiència positiva.

366 Part V

Annexos i Bibliografia

367 A Detall en codi CSTL del procés de validació de l’esquema conceptual

A.1 Especificació dels esdeveniments validats

NewSession

method NewSession { timeNow := new Time(hour:=13,min:=45,sec:=00); dateNow := new Date(day:=23,month:=10,year:=2010); now := new DateTime(date := dateNow, time := timeNow); s:=new AnonymousSession; s.ipAddress := self.ipAddress; s.createdAt := now; s.lastActivityAt := now; s.storeView := self.website.defaultStore.defaultStoreView; s.currentCurrency := s.storeView. currencyConfigurationInStoreView.defaultCurrency; cart := new AnonymousShoppingCart; s.anonymousShoppingCart := cart; }

LogIn

method LogIn { timeNow := new Time(hour:=13,min:=45,sec:=00); dateNow := new Date(day:=23,month:=10,year:=2010); now := new DateTime(date := dateNow, time := timeNow); s := new CustomerSession;

368 s.ipAddress := self.anonymousSession.ipAddress; s.createdAt := self.anonymousSession.createdAt; s.lastActivityAt := now; s.customer := self.customer; s.storeView := self.anonymousSession.storeView; if self.customer.lastCurrentCurrency.isDefined() then

s.currentCurrency := self.customer.lastCurrentCurrency; else

s.currentCurrency := self.anonymousSession.currentCurrency; endif }

AddProductToShoppingCart

method AddProductToShoppingCart { productExists := [self.shoppingCart.shoppingCartItem -> any(i| i.product=self.product ).isDefined() and self.shoppingCart.shoppingCartItem -> any(i| i.product=self.product ).optionValueInOption = self.optionValue]; if productExists then i := [self.shoppingCart.shoppingCartItem->any(i| i.product=self.product )]; i.quantity := [i.quantity + self.quantity]; else i := new ShoppingCartItem; i.shoppingCart := self.shoppingCart; i.product := self.product; i.quantity := self.quantity; i.applyDiscount := true; index:=0; while self.textOption->size()>index do

tupleOpt:=self.textOption->asSequence()->at(index+1); r := new TextOptionRating; r.textOption := tupleOpt.o; r.value := tupleOpt.text; r.shoppingCartItem := i; index := index+1; endwhile index:=0; while self.dateOption->size()>index do

tupleOpt:=self.dateOption->asSequence()->at(index+1); r := new DateOptionRating; r.dateOption := tupleOpt.o; r.value := tupleOpt.date;

369 r.shoppingCartItem := i; index := index+1; endwhile i.optionValueInOption := self.optionValue; endif if StockConfiguration.allInstances->any(true). decreaseStockWhenOrderIsPlaced then self.product.quantity := [self.product.quantity - self.quantity]; endif }

OrderConfirmation

method OrderConfirmation { timeNow := new Time(hour:=13,min:=45,sec:=00); dateNow := new Date(day:=23,month:=10,year:=2010); now := new DateTime(date := dateNow, time := timeNow); //theOrderIsCreated taxConf := self.shoppingCart.currentStoreView.store. website.taxConfigurationInWebsite; wbCurrency := self.shoppingCart.currentStoreView.store.website. currencyConfigurationInWebsite.baseCurrency; purchCurrency := self.shoppingCart.purchasingCurrency; o := new Order; o.couponCode := self.shoppingCart.couponCode; o.eMailSent := self.eMailSent; o.giftMessage := self.shoppingCart.giftMessage; o.storeView := self.shoppingCart.currentStoreView; o.customerGroup := self.shoppingCart.customerGroup; o.billing := self.billing; o.delivery := self.delivery; o.shippingMethod := self.shippingMethod; o.paymentMethod := self.paymentMethod; o.status := #Pending; if self.shoppingCart.oclIsTypeOf(CustomerShoppingCart) then o.customer := self.shoppingCart. oclAsType(CustomerShoppingCart).customer; endif if self.shoppingCart.oclIsTypeOf(AdministrationShoppingCart) then o.customer := self.shoppingCart. oclAsType(AdministrationShoppingCart).customer; endif if [taxConf.usedAddress = #Billing] then o.rateApplicationAddress := o.billing; else if [taxConf.usedAddress = #Delivery] then o.rateApplicationAddress := o.delivery; else o.rateApplicationAddress := taxConf.website. shippingConfigurationInWebsite.shippingOrigin; endif endif //currenciesAreSetted

370 o.genericBaseCurrency := CurrencyConfiguration. allInstances()->any(true).genericBaseCurrency; conditionIf := [not(wbCurrency=o.genericBaseCurrency)]; if [conditionIf] then u := new UseOfWebsiteRedefinedBaseCurrency( orderOfRedefinedBaseCurrency := o, websiteRedefinedBaseCurrency := wbCurrency ); u.initialRate := [CurrencyRate.allInstances->any(r|

r.baseCurrency=o.genericBaseCurrency and r.currency=wbCurrency ).rate]; endif conditionIf1 := [not(purchCurrency=o.genericBaseCurrency) and not(purchCurrency=o.websiteRedefinedBaseCurrency)]; if [conditionIf1] then u := new UseOfPurchasingCurrency( orderOfPurchasingCurrency := o, purchasingCurrency := purchCurrency ); u.initialRate := [CurrencyRate.allInstances->any(r|

r.baseCurrency=o.genericBaseCurrency and r.currency=purchCurrency ).rate]; endif //commentsAreAdded if self.comments->notEmpty() then index:=0; while self.comments->size()>index do

tupleCom:=self.dateOption->asSequence()->at(index+1); c := new Comment; c.text := tupleCom.text; c.notifyCustomer := tupleCom.notifyCustomer; c.createdAt := dateTimeNow; c.status := #Pending; o.comment := o.comment->union(Set{c}); index := index+1; endwhile else o.comment := Set{}; endif //itemsAreAdded index:=0; while self.shoppingCart.shoppingCartItem->size()>index do

i:=self.shoppingCart.shoppingCartItem->asSequence()->at(index+1); ol := new OrderLine; ol.order := o; ol.productSku := i.product.sku; ol.productName := i.product.productInStoreView->any(pw|

371 pw.storeView=o.storeView ).name(); ol.quantity := i.quantity; ol.giftMessage := i.giftMessage; ol.price := i.price; if i.applyDiscount then discountBasis := 0.0; if [self.shoppingCart.currentStoreView.store.website. taxConfigurationInWebsite.methodRespectingDiscount = #TaxAfterDiscount and self.shoppingCart.currentStoreView.store.website. taxConfigurationInWebsite.applyDiscountIncludingTax] then

discountBasis := ol.price*ol.quantity+ol.tax; else

discountBasis := 0.0;//ol.price*ol.quantity; endif ol.discount := i.calculateDiscount( discountBasis, i.appliedShoppingCartPriceRule ); else ol.discount := 0.0 ; endif appliedTaxRate := TaxRule.allInstances() -> select(t|

TaxRule.allInstances() -> forAll(t2|t2.priority<=t.priority) ) -> select(t|

t.customerTaxClass -> includes(o.customerGroup.customerTaxClass) ) -> select(t| t.productTaxClass->includes(i.product) ) -> collect(t|t.taxRate) -> select(tr|

tr.applicableAddress -> includes(o.rateApplicationAddress) ) -> union(i.product.fixedTaxRate); ol.tax := 0.0; ol.total := i.total+ol.tax; index := index+1; endwhile //updateProductQuantities if StockConfiguration.allInstances->any(true). decreaseStockWhenOrderIsPlaced then

while self.shoppingCart.shoppingCartItem->size()>index do

i:=self.shoppingCart.shoppingCartItem->asSequence()->at(index+1); i.product.quantity := [i.product.quantity - i.quantity]; index := index+1;

372 endwhile endif //theShoppingCartItemsAreRemoved index:=0; while self.shoppingCart.shoppingCartItem->size()>index do

i:=self.shoppingCart.shoppingCartItem->asSequence()->at(index+1); delete i; index := index+1; endwhile //the new order is relied to the event self.createdOrder := o; }

NewProduct

method NewProduct { //productIsCreated p := Product.allInstances->any(true); if self.productType=#Simple then p := new SimpleProduct; else if self.productType=#Grouped then p := new GroupedProduct; else if self.productType=#Configurable then p := new ConfigurableProduct; else if self.productType=#Virtual then p := new VirtualProduct; else if self.productType=#Bundle then p := new BundleProduct; else //self.productType=#Downloadable p := new DownloadableProduct; endif endif endif endif endif p.productTaxClass := self.productTaxClass; p.fixedTaxRate := self.fixedTaxRate; p.attributeSet := self.attributeSet; p.crossSellProduct := self.crossSellProduct; p.upSellProduct := self.upSellProduct; p.relatedProduct := self.relatedProduct; //we create, for each Website/StoreView, the //corresponding ProductInWebsite/ProductInStoreView index:=0; while Website.allInstances->size()>index do w:=Website.allInstances->asSequence()->at(index+1); piw := new ProductInWebsite(product := p, website := w); piw.isAvailable := self.website->includes(w); index:=index+1; endwhile index:=0; while StoreView.allInstances->size()>index do

stw:=StoreView.allInstances->asSequence()->at(index+1); pis := new ProductInStoreView(

373 product := p, storeView := stw ); index:=index+1; endwhile //we create, for each ableToRateAttribute, a global rating //and as website/storeview ratings as websites/storeviews //exist in the system ableToRateAttributes := self.attributeSet.attribute -> select(a|

a.associatedProductType->includes(self.productType) ); index2:=0; while ableToRateAttributes->size()>index2 do a:=ableToRateAttributes->asSequence()->at(index2+1); gar := new GlobalAttributeRating ( productOfGloballyRatedAttribute := p, globallyRatedAttribute := a ); index:=0; while Website.allInstances->size()>index do

w:=Website.allInstances->asSequence()->at(index+1); war := new WebsiteAttributeRating ( websiteRatedProduct := p, websiteRatedAttribute := a, websiteWhereIsRated := w ); index:=index+1; endwhile index:=0; while StoreView.allInstances->size()>index do

s:=StoreView.allInstances->asSequence()->at(index+1); sar := new StoreViewAttributeRating ( storeViewRatedProduct := p, storeViewRatedAttribute := a, storeViewWhereIsRated := s ); index:=index+1; endwhile index2:=index2+1; endwhile //attributesAreRated //we associate global ratings to its values (only for those values //that have been given to the domain event) index:=0; while self.values->size()>index do t:=self.values->asSequence()->at(index+1); attribute := t.a; value := t.v; attribute.globalAttributeRating->any(gar| gar.productOfGloballyRatedAttribute=p ).genericAttributeValue:=value; index:=index+1;

374 endwhile //propertiesAreRated p.sku := self.sku; p.genericName := self.name; p.genericNetPrice := self.netPrice; p.genericWeight := self.weight; p.genericStatus := self.status; p.genericIsNewFrom := self.isNewFrom; p.genericIsNewUntil := self.isNewUntil; p.genericSpecialNetPrice := self.specialNetPrice; p.genericSpecialNetPriceFrom := self.specialNetPriceFrom; p.genericSpecialNetPriceUntil := self.specialNetPriceUntil; p.genericDescription := self.description; p.genericShortDescription := self.shortDescription; p.genericMetaDescription := self.metaDescription; p.genericMetaKeyword := self.metaKeyword; p.genericMetaTitle := self.metaTitle; p.genericImageGalleryPath := self.imageGalleryPath; p.genericBaseImagePath := self.baseImagePath; p.genericSmallImagePath := self.smallImagePath; p.genericThumbnailPath := self.thumbnailPath; p.genericUrlKey := self.urlKey; p.genericIsAvailableForGoogleCheckout := self.isAvailableForGoogleCheckout; p.genericIsGiftMessageAllowed := self.giftMessageAllowed; p.genericIsVisibleOnCatalog := self.visibleOnCatalog; p.genericIsVisibleOnSearch := self.visibleOnSearch; p.stockStatus := self.stockStatus; p.quantity := self.quantity; p.qtyToBecomeOutOfStock := self.qtyToBecomeOutOfStock; p.minQtyAllowedInShoppingCart := self.minQtyAllowedInShoppingCart; p.maxQtyAllowedInShoppingCart := self.maxQtyAllowedInShoppingCart; p.notifyForQuantityBelow := self.notifyForQuantityBelow; p.backOrderPolicy := self.backOrderPolicy; self.createdProduct := p; }

A.2 Especificació dels casos de test

A.2.1 Structural Schema

Test 1:

test assertFixture{ assert consistency; }

375 A.2.2 NewSession

Test 1:

test A1_newSession_simple{

newSession1 := new NewSession(website := website1, ipAddress := ’127.168.0.1’); assert occurrence newSession1; }

Test 2:

test A2_newSession_existingIPAddress{ assert equals [AnonymousSession.allInstances() -> exists(s|s.ipAddress=’127.168.0.1’)] false; newSession1 := new NewSession( website := website1, ipAddress := ’127.168.0.1’ ); assert occurrence newSession1; assert equals [AnonymousSession.allInstances() -> exists(s|s.ipAddress=’127.168.0.1’)] true; newSession2 := new NewSession( website := website1, ipAddress := ’127.168.0.1’ ); assert non-occurrence newSession2; }

A.2.3 LogIn

Test 1:

test B1_logIn_simple{ load customers; load anonymousSessionFComponent; logIn1 := new LogIn( customer := adria, anonymousSession := anonSession ); assert occurrence logIn1; }

376 Test 2:

test B2_logIn_existingCustomerSession{ load customers; load anonymousSessionFComponent; logIn1 := new LogIn( customer := adria, anonymousSession := anonSession ); assert occurrence logIn1; logIn2 := new LogIn( customer := adria, anonymousSession := anonSession ); assert non-occurrence logIn2; }

Test 3:

test B3_logIn_customerWithLastCurrentCurrency{ load customers; load anonymousSessionFComponent; adria.lastCurrentCurrency := usDollar; logIn1 := new LogIn( customer:= adria, anonymousSession := anonSession ); assert occurrence logIn1; assert equals adria.customerSession.currentCurrency usDollar; }

Test 4:

test B4_logIn_customerAndAnonSessionHaveDifferentWebsite{ load customers; load anonymousSessionFComponent; anonSession.storeView := storeView2; logIn1 := new LogIn( customer:= adria, anonymousSession := anonSession ); assert equals [adria.websiteWhereIsVisible -> includes(anonSession.storeView.store.website)] false; assert non-occurrence logIn1; }

377 A.2.4 AddProductToShoppingCart

Test 1:

test D1_addProductToShoppingCart_simpleAnonymousSC{ load anonymousSessionFComponent; load someProducts; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert occurrence addProductToSCEvent1; }

Test 2:

test D2_addProductToShoppingCart_simpleCustomerSC{ load customers; load anonymousSessionFComponent; load someProducts; logIn1 := new LogIn( customer := adria, anonymousSession := anonSession ); assert occurrence logIn1; //we simulate restorePreviousShoppingCart’s behaviour, //creating a customerSC adriaSC := new CustomerShoppingCart( customer:=adria, createdAt:=nowFixture, updatedAt:=nowFixture ); addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := adriaSC, quantity := 2 ); assert occurrence addProductToSCEvent1; }

Test 3:

test D3_addProductToShoppingCart_productNotAvailable{ load anonymousSessionFComponent; load someProducts; //we set the wii product unavailable for the website of the anonSession productWii.productInWebsite->any(piw| piw.website=anonSession.storeView.store.website

378 ).isAvailable := false; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert non-occurrence addProductToSCEvent1; }

Test 4:

test D4_addProductToShoppingCart_productNotEnabled{ load anonymousSessionFComponent; load someProducts; //we set the wii product as disabled productWii.genericStatus := #Disabled; productWii.productInStoreView->any(pist| pist.storeView=anonSession.storeView ).redefinedStatus := #Disabled; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert non-occurrence addProductToSCEvent1; }

Test 5:

test D5_addProductToShoppingCart_productOutOfStock{ load anonymousSessionFComponent; load someProducts; //we set the wii product as disabled productWii.stockStatus := #OutOfStock; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert non-occurrence addProductToSCEvent1; }

Test 6:

test D6_addProductToShoppingCart_productAlreadyAdded{

379 load anonymousSessionFComponent; load someProducts; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert occurrence addProductToSCEvent1; addProductToSCEvent2 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 1 ); assert occurrence addProductToSCEvent2; }

Test 7:

test D7_addProductToShoppingCart_- productQuantityDecreasesIfNeeded{ load anonymousSessionFComponent; load someProducts; initialQuantity := productWii.quantity; purchasedQuantity := 2; decreaseStock := StockConfiguration.allInstances->any(true) .decreaseStockWhenOrderIsPlaced; expectedFinalQuantity := 0; if decreaseStock then

expectedFinalQuantity := [initialQuantity-purchasedQuantity]; else expectedFinalQuantity := initialQuantity; endif anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := purchasedQuantity ); assert occurrence addProductToSCEvent1; finalQuantity := productWii.quantity; assert equals finalQuantity expectedFinalQuantity; }

A.2.5 OrderConfirmation

Test 1:

test E1_orderConfirmation_simpleAnonymousSC{

380 load customers; load anonymousSessionFComponent; load someProducts; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert occurrence addProductToSCEvent1; orderConfirmation1 := new OrderConfirmation( shoppingCart:=anonSC, delivery:=anAddressParis, billing:=anAddressParis, shippingMethod:=flatRateSM, paymentMethod:=purchaseOrderPM, eMailSent := false, comments:=Set{} ); assert occurrence orderConfirmation1; }

Test 2:

test E2_orderConfirmation_simpleCustomerSC{ load customers; load anonymousSessionFComponent; load someProducts; logIn1 := new LogIn( customer := adria, anonymousSession := anonSession ); assert occurrence logIn1; //simulem el comportament de RestorePreviousShoppingCart, //que crea la customerSC adriaSC := new CustomerShoppingCart( customer:=adria, createdAt:=nowFixture, updatedAt:=nowFixture ); addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := adriaSC, quantity := 2 ); assert occurrence addProductToSCEvent1; orderConfirmation1 := new OrderConfirmation( shoppingCart:=adriaSC, delivery:=anAddressParis, billing:=anAddressParis, shippingMethod:=flatRateSM, paymentMethod:=purchaseOrderPM, eMailSent := false, comments:=Set{}

381 ); assert occurrence orderConfirmation1; }

Test 3:

test E3_orderConfirmation_withWebsiteRedefinedBaseCurrencySC{ load customers; load anonymousSessionFComponent; load someProducts; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert occurrence addProductToSCEvent1; anonSC.currentStoreView.store.website. currencyConfigurationInWebsite.redefinedBaseCurrency := usDollar; orderConfirmation1 := new OrderConfirmation( shoppingCart:=anonSC, delivery:=anAddressParis, billing:=anAddressParis, shippingMethod:=flatRateSM, paymentMethod:=purchaseOrderPM, eMailSent:=false, comments:=Set{} ); assert occurrence orderConfirmation1; }

Test 4:

test E4_orderConfirmation_withPurchasingCurrencySC{ load customers; load anonymousSessionFComponent; load someProducts; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert occurrence addProductToSCEvent1; anonSession.currentCurrency := usDollar; orderConfirmation1 := new OrderConfirmation( shoppingCart:=anonSC, delivery:=anAddressParis, billing:=anAddressParis, shippingMethod:=flatRateSM, paymentMethod:=purchaseOrderPM, eMailSent:=false,

382 comments:=Set{} ); assert occurrence orderConfirmation1; }

Test 5:

test E5_orderConfirmation_disabledShippingMethod{ load customers; load anonymousSessionFComponent; load someProducts; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert occurrence addProductToSCEvent1; flatRateSM.genericStatus := #Disabled; orderConfirmation1 := new OrderConfirmation( shoppingCart:=anonSC, delivery:=anAddressParis, billing:=anAddressParis, shippingMethod:=flatRateSM, paymentMethod:=purchaseOrderPM, eMailSent:=false, comments:=Set{} ); assert non-occurrence orderConfirmation1; }

Test 6:

test E6_orderConfirmation_- minimumAllowedForPaymentMethodNotReached{ load customers; load anonymousSessionFComponent; load someProducts; payFlowProPM.genericMinimumAllowed := 1000.0; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert occurrence addProductToSCEvent1; orderConfirmation1 := new OrderConfirmation( shoppingCart:=anonSC, delivery:=anAddressParis, billing:=anAddressParis, shippingMethod:=flatRateSM, paymentMethod:=payFlowProPM,

383 eMailSent:=false, comments:=Set{} ); assert non-occurrence orderConfirmation1; }

Test 7:

test E7_orderConfirmation_customPriceAndApplyDiscountDefined{ load customers; load anonymousSessionFComponent; load someProducts; load scPriceRulesFComponent; anonSC := anonSession.anonymousShoppingCart; addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := anonSC, quantity := 2 ); assert occurrence addProductToSCEvent1; anonSC.shoppingCartItem->any(true).customPrice := 50.0; anonSC.shoppingCartItem->any(true).applyDiscount := true; orderConfirmation1 := new OrderConfirmation( shoppingCart:=anonSC, delivery:=anAddressParis, billing:=anAddressParis, shippingMethod:=flatRateSM, paymentMethod:=purchaseOrderPM, eMailSent:=false, comments:=Set{} ); assert inconsistency; }

Test 8:

test E8_orderConfirmation_withAdministrationShoppingCart{ load customers; load someProducts; load administratorFComponent; load scPriceRulesFComponent; adminSC := new AdministrationShoppingCart( administrator:=admin1, customer:=adria, storeView := storeView1, currency:=euro ); addProductToSCEvent1 := new AddProductToShoppingCart( product := productWii, shoppingCart := adminSC, quantity := 2

384 ); assert occurrence addProductToSCEvent1; adminSC.shoppingCartItem->any(true).customPrice := 50.0; adminSC.shoppingCartItem->any(true).applyDiscount := true; orderConfirmation1 := new OrderConfirmation( shoppingCart:=adminSC, delivery:=anAddressParis, billing:=anAddressParis, shippingMethod:=flatRateSM, paymentMethod:=purchaseOrderPM, eMailSent:=false, comments:=Set{} ); assert occurrence orderConfirmation1; assert equals orderConfirmation1.createdOrder.shippingCosts() 5.0; assert equals orderConfirmation1.createdOrder.orderLine->any(true).discount 6.0; assert equals orderConfirmation1.createdOrder.total() 99.0; }

A.2.6 NewProduct

Test 1:

test C1_newProduct_simple{ load customers; load anonymousSessionFComponent; load someProducts; textValueID002 := new TextualValue(value := ’ID002’); textValueNintendo := new TextualValue(value := ’Nintendo’); tupleValueID002 := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=textValueID002 ); tupleValueNintendo := new TupleNewProduct( a:=attributeManufacturer, v:=textValueNintendo ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent1.values := Set{tupleValueID002,tupleValueNintendo}; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150.0; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.weight := 0.8; newProductEvent1.quantity := 10; assert occurrence newProductEvent1; }

385 Test 2:

test C2_newProduct_existingSku{ load customers; load anonymousSessionFComponent; load someProducts; textValueID002 := new TextualValue(value := ’ID002’); textValueID003 := new TextualValue(value := ’ID003’); textValueNintendo := new TextualValue(value := ’Nintendo’); tupleValueID002 := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=textValueID002 ); tupleValueID003 := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=textValueID003 ); tupleValueNintendo := new TupleNewProduct( a:=attributeManufacturer, v:=textValueNintendo ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent1.values := Set{ tupleValueID002, tupleValueNintendo }; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.quantity := 10; assert occurrence newProductEvent1; newProductEvent2 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent2.values := Set{ tupleValueID003, tupleValueNintendo }; newProductEvent2.productType := #Simple; newProductEvent2.sku := ’skuDS’; newProductEvent2.name := ’Nintendo DS Repetida’; newProductEvent2.netPrice := 150; newProductEvent2.status := #Enabled; newProductEvent2.stockStatus := #InStock; newProductEvent1.quantity := 10; assert non-occurrence newProductEvent2;

386 }

Test 3:

test C3_newProduct_twoValuesForTheSameAttribute{ load customers; load anonymousSessionFComponent; load someProducts; textValueID002 := new TextualValue(value := ’ID002’); textValueNintendo := new TextualValue(value := ’Nintendo’); tupleValueID002 := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=textValueID002 ); tupleValueNintendo := new TupleNewProduct( a:=attributeManufacturer, v:=textValueNintendo ); tupleValueID002bis := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=textValueID002 ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent1.values := Set{ tupleValueID002, tupleValueNintendo, tupleValueID002bis }; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.quantity := 10; assert non-occurrence newProductEvent1; }

Test 4:

test C4_newProduct_aValueWithTheWrongType{ load customers; load anonymousSessionFComponent; load someProducts; intValue999 := new IntegerValue(value := 999); tupleValue999 := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=intValue999

387 ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent1.values := Set{tupleValue999,tupleValue999}; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.quantity := 10; assert non-occurrence newProductEvent1; }

Test 5:

test C5_newProduct_aValueForANotAbleToRateAttribute{ load customers; load anonymousSessionFComponent; load someProducts; nonUsedAttributesAS := new AttributeSet( name := ’NonUsedAttributes’ ); attributeUniqueIdentifier.attributeSet := nonUsedAttributesAS; textValueID002 := new TextualValue(value := ’ID002’); textValueNintendo := new TextualValue(value := ’Nintendo’); tupleValueID002 := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=textValueID002 ); tupleValueNintendo := new TupleNewProduct( a:=attributeManufacturer, v:=textValueNintendo ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent1.values := Set{ tupleValueID002, tupleValueNintendo }; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.quantity := 10; assert inconsistency;

388 }

Test 6:

test C6_newProduct_aRequiredAttributeWithoutValue{ load customers; load anonymousSessionFComponent; load someProducts; textValueID002 := new TextualValue(value := ’ID002’); textValueNintendo := new TextualValue( value := ’Nintendo’ ); tupleValueNintendo := new TupleNewProduct( a:=attributeManufacturer, v:=textValueNintendo ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent1.values := Set{tupleValueNintendo}; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.quantity := 10; assert non-occurrence newProductEvent1; }

Test 7:

test C7_newProduct_aUniqueAttributeRepeated{ load customers; load anonymousSessionFComponent; load someProducts; textValueID002 := new TextualValue(value := ’ID002’); textValueNintendo := new TextualValue(value := ’Nintendo’); tupleValueID002 := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=textValueID002) ; tupleValueNintendo := new TupleNewProduct( a:=attributeManufacturer, v:=textValueNintendo ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS

389 ); newProductEvent1.values := Set{ tupleValueID002, tupleValueNintendo }; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.quantity := 10; assert occurrence newProductEvent1; newProductEvent2 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent2.values := Set{ tupleValueID002, tupleValueNintendo }; newProductEvent2.productType := #Simple; newProductEvent2.sku := ’skuWii’; newProductEvent2.name := ’Nintendo Wii’; newProductEvent2.netPrice := 200; newProductEvent2.status := #Enabled; newProductEvent2.stockStatus := #InStock; newProductEvent1.quantity := 10; assert non-occurrence newProductEvent2; }

Test 8:

test C8_newProduct_aValueForANotAbleToRateProperty{ load customers; load anonymousSessionFComponent; load someProducts; //we remove the #Simple product type from the product types //that the weight property can be rated for ppconfType.shortDescriptionAssociatedToProductType := Set{

#Grouped, #Configurable, #Downloadable, #Virtual, #Bundle }; textValueID002 := new TextualValue(value := ’ID002’); textValueNintendo := new TextualValue(value := ’Nintendo’); tupleValueID002 := new TupleNewProduct( a:=attributeUniqueIdentifier, v:=textValueID002 ); tupleValueNintendo := new TupleNewProduct(

390 a:=attributeManufacturer, v:=textValueNintendo ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent1.values := Set{ tupleValueID002, tupleValueNintendo }; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.quantity := 10; //we rate the weigth attribute for the new product newProductEvent1.shortDescription := ’A little videogame machine’; assert non-occurrence newProductEvent1; }

Test 10:

test C10_newProduct_aRequiredPropertyWithoutValue{ load customers; load anonymousSessionFComponent; load someProducts; //we define that the description attribute should be //mandatory rated for all product types ppconfOblig.descriptionIsMandatory := true; textValueID002 := new TextualValue(value := ’ID002’); textValueNintendo := new TextualValue(value := ’Nintendo’); tupleValueNintendo := new TupleNewProduct( a:=attributeManufacturer, v:=textValueNintendo ); newProductEvent1 := new NewProduct( website := Set{website1,website2}, productTaxClass := taxableGoodsPTC, attributeSet := commonAttributesAS ); newProductEvent1.values := Set{tupleValueNintendo}; newProductEvent1.productType := #Simple; newProductEvent1.sku := ’skuDS’; newProductEvent1.name := ’Nintendo DS’; newProductEvent1.netPrice := 150; newProductEvent1.status := #Enabled; newProductEvent1.stockStatus := #InStock; newProductEvent1.quantity := 10; //we do not rate the description property assert inconsistency; }

391 B Eines desenvolupades de suport al procés de documentació

Tal i com s’ha comentat a la secció 11.1.2, durant la realització del projecte s’han desenvolupat eines d’ajuda a la documentació de l’esquema conceptual elicitat. Bàsicament, fitxers XML on enregistrar la informació de forma estructurada, i fitxers XSLT que permeten transformar aquesta informació a un format apte per a la seva presentació (en aquest projecte, LATEX, per a la confecció de la memòria, i HTML, per a ús intern).

B.1 Estructura dels fitxers de documentació

En aquesta secció, es presenta un exemple de l’estructura dissenyada en XML per documentar cada un dels següents elements de l’esquema conceptual:

I Sub-esquema de l’esquema estructural

I Cas d’ús

I Esdeveniment

En tots tres casos, s’utilitzen exemples reals de l’esquema de Magento. Aquells conceptes que puguin no quedar prou clars s’acompanyen amb una breu descripció.

Documentació d’un Sub-esquema de l’esquema estructural

A continuació es descriu l’estructura del sub-esquema “ProductTags”. Les etiquetes XML segueix- en l’ordre establert en la documentació de l’esquema (veure la secció 4.2). El diagrama que representa gràficament el sub-esquema s’ha generat amb MagicDraw i exportat com a imatge. Per a incloure’l al document, a la fitxa de documentació s’especifica el nom del fitxer d’imatge.

392 Els elements ’origin’ i ’modificació’ són per a ús intern, i no apareixen al document final. Per aquest motiu, la informació que contenen s’ha redactat indistintament en llengua catalana o anglesa.

Tags are one-word descriptors that customers can add to products. ProductTags.jpg

TagInStoreView

popularity

is the number of times the tag has been applied in this storeView, adding the base popularity.

self.basePopularity + self.tag.productTagging -> select ( t | t.storeView = self.storeView ) -> size() Observation (Reports -> Tags -> Popular)

Tag

isIdentifiedByItsName
Tags are identified by its name. Tag.allInstances() -> isUnique(name)

393

Observation (Catalog -> Tag -> Create Tag -> we cannot create one with the same name)

Tag

isSpecifiedInAllStoreViews
Each tag should be related to all existing store views in the system.

self.storeView -> includesAll(StoreView.allInstances()) Designer’s decision. Logical consequence of the generic-redefined structure.

Tags act as keywords. Tags help customers to organize and remember the products that they have seen. System saves the following information for tags: the description word itself.

Pending tags are those recently added by costumers. Administrators can after change its status to Approved or Disabled.

394 A ProductTagging is the fact that a Customer has assigned a Tag to a Product in a Store View. Magento saves the time this assignation was made.

Administrators can also add tags to products. In this case, only the product tagged and the selectionned tag are saved. Some tag properties can be set at the store view scope: is added to the number of times the tag is assigned to calculate its popularity.

used by the system to emphasize the most used tags.

Creat. Separació en Product Tags i Product Reviews. Repàs general. Canvi de Tagging of a Product per ProductTagging, altres canvis menors.

Documentació d’un Cas d’ús

Per a exemplificar la documentació dels casos d’ús s’ha utilitzat el cas “Delete a Website”. De la documentació, cal destacar l’estructura d’escenari principal i extensions. Tal i com es pot comprovar, les extensions no es defineixen “a part” de l’escenari principal, tal i com es presenten al document final, sino en el punt de l’acció en el qual es desenvolupen. La fulla de transformació XSLT s’encarrega de generar la numeració correcta.

395 Delete a website Store Administrator The store administrator wants to delete a website

The store administrator selects the website to be deleted.

The system asks the store administrator a confirmation The store administrator does not want to delete the website The use case ends

The store administrator confirms that he wants to delete the website The system deletes the website. DeleteWebsite

Documentació d’un Esdeveniment

Finalment, es mostra un exemple de documentació de l’esdeveniment “SetDefaultStoreViewOfStore”. Per a la definició dels elements (atributs, relacions i generalitzacions) associats a l’esquema, s’han establert valors per defecte per a la multiplicitat (1) i el nom de rol de les associacions (el nom de la classe al qual s’associa l’esdeveniment, en minúscula), en el cas que aquests valors no s’hagin definit.

396 SetDefaultStoreViewOfStore StoreView Store self.store.storeView->includes(self.storeView) self.store.defaultStoreView = self.storeView

B.2 Fulles de transformació XSLT

Per a cada una de les estructures anteriors, s’ha desenvolupat una fulla de transformació, amb l’objectiu de que la documentació de l’esquema conceptual que es genera a partir de la operació de transformació pugui incloure’s automàticament dins el document general. També s’han gener- at dues fulles de transformació addicionals, per generar el resum de casos d’ús i esdeveniments detallat a l’inici dels capítols respectius.

El codi d’aquestes fulles forma part del material addicional del projecte, que opcionalment s’entregarà per mitjans electrònics.

397 [Referències Bibliogràfiques]

[1] E.J. Chikofsky and J. H. Cross II. Reverse engineering and design recovery: A taxonomy. IEEE Software, 1990. [2] Observatorio Nacional de las Telecomunicaciones y la Sociedad de la Información. Comer- cio electronico b2c 2010. 2011. [3] R. Edward Freeman. The Stakeholder of the Modern Corporation. 1984. [4] Cem Kaner, James Bach, and Bret Pettichord. Lessons Learned in Software Testing. John Wiley & Sons, 2001.

[5] Odd Ivar Lindland, Guttorm Sindre, and Arne Solvberg. Understanding quality in conceptual modeling. IEEE Software, 1994. [6] B. Meyer. Seven principles of software testing. IEEE Computer 41, 2008. [7] Antoni Olivé Ramon. Modelització dels esquemes Conceptuals. L’estructura. Edicions UPC, 2002.

[8] Antoni Olivé Ramon. Conceptual Modelling of Information Systems. Springer, 2007. [9] Object Management Group (OMG). Model driven architecture specifications. Technical report, 2010. [10] Object Management Group (OMG). Object constraint language (ocl) specification version 2.2. Technical report, 2010. [11] Object Management Group (OMG). Uml testing profile, version 1.0. Technical report, 2010. [12] Object Management Group (OMG). Unified modeling language superstructure specification version 2.3. Technical report, 2010.

[13] Fundació Observatori per a la Societat de la Informació de Catalunya. Nota de premsa: Resultats de l’enquesta sobre equipament i ús de les tic a les llars de catalunya al 2010. 2011. [14] Albert Tort Pugibet. Test-driven conceptual modeling: A method and a tool. http://sites.upc.edu/ www-mpi/ER2008/PhD/papers/AlbertTort.pdf.

[15] Albert Tort Pugibet. Esquema conceptual de l’. Master’s thesis, Universitat Politècnica de Catalunya, 2007. [16] Albert Tort Pugibet. Cstl: A conceptual schema testing language. Master’s thesis, Universitat Politècnica de Catalunya, 2008.

[17] Albert Tort Pugibet. An approach to testing conceptual schemas. Data & Knowledge Engineering, 2010.

398