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    }