001 002 package market; 003 004 import java.util.Hashtable; 005 import java.util.Iterator; 006 007 import market.stdform.ButtonIDs; 008 import market.stdform.FSCheckable; 009 import market.stdform.FSWorkerDefault; 010 import market.stdform.FSWorkerEdit; 011 import market.stdform.FSWorkerOrder; 012 import market.stdform.MSLogOff; 013 import sale.FormSheet; 014 import sale.Gate; 015 import sale.GateChangeTransition; 016 import sale.SaleProcess; 017 import sale.SalesPoint; 018 import sale.Shop; 019 import sale.Transition; 020 import sale.UIGate; 021 import sale.stdforms.MsgForm; 022 import users.User; 023 import users.UserManager; 024 import data.StockItem; 025 import data.events.VetoException; 026 import data.ooimpl.CountingStockImpl; 027 import data.stdforms.SingleTableFormSheet; 028 029 /** 030 * The worker process. This process handles execution of the orders. 031 */ 032 public class SProcessWorker extends SProcessMarket{ 033 034 /** 035 * ID for serialization. 036 */ 037 private static final long serialVersionUID = -5031583540761145768L; 038 039 public static final int BUYPROCESS = 0; 040 public static final int TILLQUEUE = 1; 041 public static final int WAREHOUSEQUEUE = 2; 042 public static final int OLDOFFER = 3; 043 044 /** 045 * Gate for displaying how much workers are currently logged on and 046 * how much orders aren`t yet executed. 047 */ 048 private UIGate uig_initial = new UIGate(null, null); 049 050 /** 051 * Gate for displaying a checklist for the order of the currently selected customer. 052 */ 053 private UIGate uig_order = new UIGate(null, null); 054 055 /** 056 * Gate for displaying that the execution of the order isn`t completed. 057 */ 058 private UIGate uig_notReady = new UIGate(null, null); 059 060 /** 061 * Gate for editing the count of articles in case of a shortage. 062 */ 063 private UIGate uig_edit = new UIGate(null, null); 064 065 /** 066 * Gate for displaying that no article was selected. 067 */ 068 private UIGate uig_noItemSelected = new UIGate(null, null); 069 070 /** 071 * Gate for affirming that the correction of the article`s count was successful. 072 */ 073 private UIGate uig_editConfirmation = new UIGate(null, null); 074 075 private SingleTableFormSheet stfs_order; 076 private FSCheckable fsc_edit; 077 078 /** 079 * Current {@link CSOrder} to execute. 080 */ 081 private CSOrder cso_order = null; 082 083 /** 084 * A new order that might contains articles which aren`t yet avaible in case of a shortage. 085 */ 086 private CountingStockImpl cs_newOrder; 087 088 /** 089 * Current selected [@link SICustomer}. 090 */ 091 private SICustomer sic_customer = null; 092 093 /** 094 * The global warehouse-queue. 095 */ 096 private static SSListenable ss_orders = SMarket.getWarehouseQueue(); 097 098 /** 099 * A Hashtable storing which articles are already executed and which not. 100 */ 101 private Hashtable<CIArticle, Object> h_articlesDone = new Hashtable<CIArticle, Object>(); 102 103 /** 104 * The {@link CIArticle} which count has to be edit. 105 */ 106 private CIArticle ci_article = null; 107 108 /** 109 * An Array containing the count of an article in all different datastructures in the market, 110 * temporarily items in shoppingbaskets, orders waiting at the till-queue, 111 * orders waiting at the warehouse-queue and items in the markets-offer. 112 */ 113 private int[] databaseCount = new int[4]; 114 115 116 /** 117 * @param sName the name of the process. 118 */ 119 public SProcessWorker(String sName) { 120 super(sName); 121 } 122 123 124 //################################### Gates ############################################################ 125 126 /** 127 * Attaches {@link FSWorkerDefault}, its actions and the menu to {@link #uig_initial}. 128 * @return the set up {@link #uig_initial}. 129 */ 130 protected Gate getInitialGate() { 131 FormSheet fs = new FSWorkerDefault(getOrderCount(), getWorkerCount()); 132 setTransition(fs, changeToOrderGate(), ButtonIDs.BTN_OK); 133 uig_initial.setFormSheet(fs); 134 uig_initial.setMenuSheet(new MSLogOff(logOff())); 135 return uig_initial; 136 } 137 138 /** 139 * Attaches {@link FSWorkerOrder} and its actions to {@link #uig_order}. 140 * @return the set up {@link #uig_order}. 141 */ 142 private Gate getOrderGate(){ 143 stfs_order = FSWorkerOrder.getOrderTable(sic_customer.getName(), cso_order, h_articlesDone); 144 setTransition(stfs_order, GateChangeTransition.CHANGE_TO_COMMIT_GATE, ButtonIDs.BTN_ACCEPT); 145 setTransition(stfs_order, changeToEditGate(), ButtonIDs.BTN_EDIT); 146 setTransition(stfs_order, GateChangeTransition.CHANGE_TO_ROLLBACK_GATE, ButtonIDs.BTN_BACK); 147 uig_order.setFormSheet(stfs_order); 148 return uig_order; 149 } 150 151 /** 152 * Attaches {@link MsgForm} and its ok-action to {@link #uig_notReady}. 153 * @return the set up {@link #uig_notReady}. 154 */ 155 private Gate notReadyGate(){ 156 FormSheet fs = new MsgForm("Auftrag nicht fertig","Sie haben nicht alle Artikel abgearbeitet.\n"+ 157 "Bitte erledigen Sie dies zunächst damit der Auftrag abgeschlossen werden kann!"); 158 setTransition(fs, new GateChangeTransition(uig_order), FormSheet.BTNID_OK); 159 uig_notReady.setFormSheet(fs); 160 return uig_notReady; 161 } 162 163 /** 164 * Attaches {@link FSWorkerEdit} and its actions to {@link #uig_edit}. 165 * @return the set up {@link #uig_edit}. 166 */ 167 private Gate getEditGate(){ 168 fsc_edit = FSWorkerEdit.create(ci_article, getDatabaseCount()); 169 setAction(fsc_edit, new sale.Action(){ 170 private static final long serialVersionUID = -3206671846726678734L; 171 172 public void doAction(SaleProcess p, SalesPoint sp) throws Throwable { 173 if(fsc_edit.checkTextFields(FSCheckable.ALL_ERRORMESSAGES_AT_ONCE, false)){ 174 uig_edit.setNextTransition(changeToEditConfirmationGate()); 175 } 176 } 177 }, ButtonIDs.BTN_OK); 178 setTransition(fsc_edit, new GateChangeTransition(getOrderGate()), ButtonIDs.BTN_BACK); 179 uig_edit.setFormSheet(fsc_edit); 180 return uig_edit; 181 } 182 183 /** 184 * Attaches {@link MsgForm} and its ok-action to {@link #uig_noItemSelected}. 185 * @return the set up {@link #uig_noItemSelected}. 186 */ 187 private Gate getNoItemSelectedGate(){ 188 FormSheet fs = new MsgForm("Kein Artikel gewählt", "Sie müssen zunächst einen Artikel auswählen!"); 189 setTransition(fs, new GateChangeTransition(getOrderGate()), FormSheet.BTNID_OK); 190 uig_noItemSelected.setFormSheet(fs); 191 return uig_noItemSelected; 192 } 193 194 /** 195 * Attaches {@link MsgForm} and its ok-action to {@link #uig_editConfirmation}. 196 * @return the set up {@link #uig_editConfirmation}. 197 */ 198 private Gate getEditConfirmationGate(){ 199 FormSheet fs = new MsgForm("Bestand aktualisiert", "Der Bestand wurde erfolgreich korrigiert!"); 200 setTransition(fs, new GateChangeTransition(getOrderGate()), FormSheet.BTNID_OK); 201 uig_editConfirmation.setFormSheet(fs); 202 return uig_editConfirmation; 203 } 204 205 /** 206 * @return the Gate to jump to if the current order is complete. 207 */ 208 public Gate getCommitGate() { 209 return new Gate(){ 210 private static final long serialVersionUID = 7420935906435390919L; 211 212 public Transition getNextTransition(SaleProcess pOwner, User usr) 213 throws InterruptedException { 214 return commit(); 215 } 216 }; 217 } 218 219 /** 220 * @return the Gate to jump to if the current order has to be rolled back. 221 */ 222 public Gate getRollbackGate() { 223 return new Gate(){ 224 private static final long serialVersionUID = -4697158069254762692L; 225 226 public Transition getNextTransition(SaleProcess pOwner, User usr) 227 throws InterruptedException { 228 return rollback(); 229 } 230 }; 231 } 232 233 //################################## Transitions ########################################################## 234 235 /** 236 * @return a Transition that changes to the {@link #getOrderGate()} if there are any orders left, 237 * otherwise returns back to {@link #getInitialGate()}, 238 * sets the next customer and order. 239 */ 240 private Transition changeToOrderGate(){ 241 return new Transition(){ 242 private static final long serialVersionUID = -8976232730398727428L; 243 244 public Gate perform(SaleProcess pOwner, User usr) { 245 setNextCustomer(); 246 if(sic_customer==null) return getInitialGate(); 247 try { 248 ss_orders.remove(sic_customer, pOwner.getBasket()); 249 } catch (VetoException e) { 250 System.err.println(e.getMessage()); 251 return getInitialGate(); 252 } 253 cso_order = sic_customer.removeOrderFromQueue(); 254 initiateArticlesDone(); 255 return getOrderGate(); 256 } 257 }; 258 } 259 260 /** 261 * @return a Transition that changes to the {@link #getInitialGate()} if order is completed, 262 * otherwise changes to the {@link #notReadyGate()}, 263 * adds a new CSOrder to the customer if some articles were not avaible. 264 */ 265 private Transition commit(){ 266 return new Transition(){ 267 private static final long serialVersionUID = -2854924689394242526L; 268 269 public Gate perform(SaleProcess pOwner, User usr) { 270 if(h_articlesDone.contains(Boolean.FALSE)) return notReadyGate(); 271 if(cs_newOrder != null) sic_customer.addOrderToQueue(cs_newOrder, false); 272 if(sic_customer.getOrderCount(false)==0) getBasket().commit(); 273 else getBasket().rollback(); 274 Shop.getTheShop().removeStock(cso_order.getName()); 275 sic_customer = null; 276 cso_order = null; 277 cs_newOrder = null; 278 h_articlesDone.clear(); 279 return getInitialGate(); 280 } 281 }; 282 } 283 284 /** 285 * @return a Transition that changes to the {@link #getInitialGate()}, 286 * rolls back the previously selected order. 287 */ 288 private Transition rollback(){ 289 return new Transition(){ 290 private static final long serialVersionUID = -4226978331728960463L; 291 292 public Gate perform(SaleProcess pOwner, User usr) { 293 pOwner.getBasket().rollback(); 294 sic_customer.rollbackOrder(cso_order); 295 sic_customer = null; 296 cso_order = null; 297 h_articlesDone.clear(); 298 ci_article = null; 299 cs_newOrder = null; 300 return getInitialGate(); 301 } 302 }; 303 } 304 305 /** 306 * @return a Transition that changes to the {@link #getEditGate()} if an article was choosed, 307 * otherwise changes to the {@link #getNoItemSelectedGate()}. 308 */ 309 private Transition changeToEditGate(){ 310 return new Transition(){ 311 private static final long serialVersionUID = 6357200452359202447L; 312 313 public Gate perform(SaleProcess pOwner, User usr) { 314 if(stfs_order.getSelectedRecord()==null) return getNoItemSelectedGate(); 315 ci_article = Conversions.recordToCIArticle(stfs_order.getSelectedRecord()); 316 countDatabase(); 317 return getEditGate(); 318 } 319 }; 320 } 321 322 /** 323 * @return a Transition that changes to the {@link #getEditConfirmationGate()}, 324 * corrects the count of the currently selected article in market`s dates. 325 */ 326 private Transition changeToEditConfirmationGate(){ 327 return new Transition(){ 328 private static final long serialVersionUID = -2296457263704403221L; 329 330 public Gate perform(SaleProcess pOwner, User usr) { 331 // Checks whether the new number of articles is smaller than the old one 332 // because if their is no shortage there is no need to update 333 // this can be done during an inventure or something like this 334 if(Integer.parseInt(fsc_edit.getEntry(FSWorkerEdit.JTFC_REAL))<=getDatabaseCount()){ 335 updateOffer(); 336 } 337 return getEditConfirmationGate(); 338 } 339 }; 340 } 341 342 /** 343 * @return a Transition that changes to the {@link #getStopGate()}, 344 * fires Event: updateWorkerScreen to all MarketEventListeners 345 */ 346 private Transition logOff(){ 347 return new Transition(){ 348 private static final long serialVersionUID = 3092364801488034964L; 349 350 public Gate perform(SaleProcess pOwner, User usr) { 351 SMarket.fireUpdateWorkerScreen(); 352 return getStopGate(); 353 } 354 }; 355 } 356 357 //#################################### internal methods ##################################################### 358 359 /** 360 * @return the count of all workers which are currently logged on. 361 */ 362 private static int getWorkerCount(){ 363 int worker = 0; 364 Iterator salespoints = Shop.getTheShop().getSalesPoints().iterator(); 365 while(salespoints.hasNext()){ 366 User user = ((SalesPoint)salespoints.next()).getUser(); 367 if(((UMUserBase)UserManager.getGlobalUM()).getWarehouseWorker().match(user)){ 368 worker++; 369 } 370 } 371 return worker; 372 } 373 374 /** 375 * @return the count of all orders which aren`t completed yet. 376 */ 377 private static int getOrderCount(){ 378 int order = 0; 379 Iterator it = ss_orders.iterator(null, false); 380 while(it.hasNext()){ 381 order = order + ((SICustomer)it.next()).getOrderCount(true); 382 } 383 return order; 384 } 385 386 /** 387 * Selects the next customer and order to be execute. 388 */ 389 private void setNextCustomer(){ 390 Long time = new Long(System.currentTimeMillis()); 391 SICustomer sic_next = null; 392 Iterator it = ss_orders.iterator(null, true); 393 while(it.hasNext()){ 394 SICustomer sic_current = (SICustomer)it.next(); 395 if((time.compareTo(sic_current.getOrderQueueTime())>=0) && (sic_current.getOrderCount(true) > 0)){ 396 time = sic_current.getOrderQueueTime(); 397 sic_next = sic_current; 398 } 399 } 400 sic_customer = sic_next; 401 } 402 403 /** 404 * Initiates the HashTable with false in the value-fields to signal the articles aren`t executed yet. 405 */ 406 private void initiateArticlesDone(){ 407 Iterator<StockItem> it = cso_order.iterator(null, false); 408 while(it.hasNext()){ 409 h_articlesDone.put((CIArticle) it.next().getAssociatedItem(null), Boolean.FALSE); 410 } 411 } 412 413 /** 414 * @return the count of the selected article currently stored in the market`s database. 415 */ 416 private int getDatabaseCount(){ 417 return databaseCount[BUYPROCESS]+ 418 databaseCount[TILLQUEUE]+ 419 databaseCount[WAREHOUSEQUEUE]+ 420 databaseCount[OLDOFFER]; 421 } 422 423 /** 424 * Adds a given value to the value at the specified position, 425 * used by different implementations of OfferEventListener 426 * to find out the count of an article considering all datastructures of the market. 427 * 428 * @param value the value that will be added. 429 * @param type the index of the array at which the value will be added, 430 * use {@link #BUYPROCESS} to {@link #OLDOFFER}. 431 */ 432 public synchronized void addDatabaseCount(int value, int type){ 433 databaseCount[type] += value; 434 } 435 436 /** 437 * Sets {@link #databaseCount} to the current count of the selected article, 438 * uses Event: countArticles of OfferEventListener. 439 */ 440 private void countDatabase(){ 441 databaseCount = new int[4]; 442 SPCustomer.fireCountArticles(ci_article.getName(), this); 443 SMarket.getTillQueue().fireCountArticles(ci_article.getName(), this); 444 SMarket.getWarehouseQueue().fireCountArticles(ci_article.getName(), this); 445 addDatabaseCount(cso_order.countItems(ci_article.getName(), null), WAREHOUSEQUEUE); 446 addDatabaseCount(SMarket.getOffer().countItems(ci_article.getName(), null), OLDOFFER); 447 } 448 449 /** 450 * Updates the count of the selected article in market`s database, 451 * if necessary fires Event: offerIsEmpty to OfferEventListeners. 452 */ 453 private void updateOffer(){ 454 String aKey = ci_article.getName(); 455 int realCount = Integer.parseInt(fsc_edit.getEntry(FSWorkerEdit.JTFC_REAL)); 456 int newCount = databaseCount[OLDOFFER] + databaseCount[BUYPROCESS] 457 + databaseCount[TILLQUEUE] + databaseCount[WAREHOUSEQUEUE] - realCount; 458 //if real count of the article doesn`t reach for orders at the till and warehouse 459 //and selections of the customers, then first temporarily selected articles will be rolled back 460 //by firing event: offerIsEmpty to {@link SPCustomer} 461 if(realCount < (databaseCount[BUYPROCESS] + databaseCount[TILLQUEUE] + databaseCount[WAREHOUSEQUEUE])){ 462 SPCustomer.fireOfferIsEmpty(aKey); 463 //if real count still doesn`t reach, the article will be removed from orders at the till-queue 464 //by firing event: offerIsEmpty to {@link SICustomer}s at the till-queue 465 if(realCount < (databaseCount[TILLQUEUE] + databaseCount[WAREHOUSEQUEUE])){ 466 SMarket.getTillQueue().fireOfferIsEmpty(aKey); 467 //if real count still doesn`t reach, the article will be removed from orders 468 //at the warehouse-queue, 469 //by firing event: offerIsEmpty to {@link SICustomer}s at the warehouse-queue 470 if(realCount<databaseCount[WAREHOUSEQUEUE]){ 471 SMarket.getWarehouseQueue().fireOfferIsEmpty(aKey); 472 //if real count doesn`t reach for the current order, 473 //the missing one will be added as a new inactive order to the warehouse-queue 474 if(realCount < cso_order.countItems(aKey, null)){ 475 int i = cso_order.countItems(aKey, null) - realCount; 476 cso_order.remove(aKey, i, null); 477 if(cs_newOrder == null){ 478 cs_newOrder = new CountingStockImpl("new Order", SMarket.getArticleCatalog()); 479 } 480 cs_newOrder.add(aKey, i, null); 481 if(!cso_order.contains(aKey, null)) h_articlesDone.remove(ci_article); 482 newCount -= i; 483 } 484 } 485 } 486 487 } 488 if(newCount <= 0) newCount = SMarket.getOffer().countItems(aKey, null); 489 if(newCount > 0){ 490 try { 491 SMarket.getOffer().remove(aKey, newCount, null); 492 } catch (Exception e) { 493 System.out.println(e.getMessage()); 494 } 495 } 496 } 497 }