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