diff --git a/doc/src/user/en/01-introducion.rst b/doc/src/user/en/01-introducion.rst index a6ae75001..8645238bc 100644 --- a/doc/src/user/en/01-introducion.rst +++ b/doc/src/user/en/01-introducion.rst @@ -1,5 +1,5 @@ -Introduction -############ +Introduction +############# .. contents:: diff --git a/doc/src/user/en/02-criterios.rst b/doc/src/user/en/02-criterios.rst index c0baf0bf8..186fd7de0 100644 --- a/doc/src/user/en/02-criterios.rst +++ b/doc/src/user/en/02-criterios.rst @@ -1,4 +1,4 @@ -Criteria +Criteria ######### .. contents:: @@ -10,7 +10,7 @@ Several operations can be carried out with criteria in the program: * Criteria administration * Allocation of criteria to resources. * Allocation of criteria to tasks. -* Filtering entities according to criteria. Tasks and order items can be filtered according to criteria to carry out operations in the program. +* Filtering entities according to criteria. Tasks and order items can be filtered according to criteria to carry out operations in the program. Only the first function out of the three described above will be explained in this section. The two kinds of allocation will be dealt with later, the allocation of resources in the chapter on "Resource management" and the filtering function in the chapter on "Task planning". diff --git a/doc/src/user/en/03-calendarios.rst b/doc/src/user/en/03-calendarios.rst index c491c0b44..db84965ca 100644 --- a/doc/src/user/en/03-calendarios.rst +++ b/doc/src/user/en/03-calendarios.rst @@ -1,4 +1,4 @@ -Calendars +Calendars ########### .. contents:: @@ -114,7 +114,7 @@ The following must be carried out to set up a default calendar: * Go to the *Administration* menu. * Click the *Configuration* procedure. -* Where *Default calendar* appears, select the calendar to be used as the program’s default calendar. +* Where *Default calendar* appears, select the calendar to be used as the program's default calendar. * Click *Save*. .. figure:: images/default-calendar.png diff --git a/doc/src/user/en/04-avances.rst b/doc/src/user/en/04-avances.rst index c56f18492..1dfa35db4 100644 --- a/doc/src/user/en/04-avances.rst +++ b/doc/src/user/en/04-avances.rst @@ -1,4 +1,4 @@ -Progress +Progress ######### .. contents:: diff --git a/doc/src/user/en/05-recursos.rst b/doc/src/user/en/05-recursos.rst index 919c8eab2..3b08430fe 100644 --- a/doc/src/user/en/05-recursos.rst +++ b/doc/src/user/en/05-recursos.rst @@ -1,4 +1,4 @@ -Management of resources +Management of resources ####################### .. _recursos: diff --git a/doc/src/user/en/06-pedidos.rst b/doc/src/user/en/06-pedidos.rst index dd66d02c5..7f72db055 100644 --- a/doc/src/user/en/06-pedidos.rst +++ b/doc/src/user/en/06-pedidos.rst @@ -1,4 +1,4 @@ -Orders and order elements +Orders and order elements ############################## .. contents:: @@ -13,10 +13,10 @@ The following sections will describe the operations that users can carry out wit Order ====== -An order is a project or work that a client requests from a company. The order for the planned works identifies the project in the company. The difference with comprehensive management programs such as “NavalPlan” is that they only need to use certain order details. These details are: +An order is a project or work that a client requests from a company. The order for the planned works identifies the project in the company. The difference with comprehensive management programs such as "NavalPlan" is that they only need to use certain order details. These details are: -* Order name -* Order code +* Order name +* Order code * Total amount of order * Estimated start date * End date @@ -211,7 +211,7 @@ Apart from the required criterion, one or various hour groups that are part of t * The system creates an hour group by default, which is associated to the order element. The details that can be modified for an hour group are: * Code for the hour group if it is not automatically generated. - * Type of criterion. Users can choose to assign a machine or worker criterion. + * Type of criterion. Users can choose to assign a machine or worker criterion. * Number of hours in the hour group. * List of criteria to be applied to the hour group. To add new criteria, users have to click "Add criterion" and select one from the search engine, which appears after clicking the button. @@ -253,7 +253,7 @@ Working with materials is carried out as follows: * The system shows the materials that belong to the selected categories. * From the materials list, users select the materials to assign to the order element. * Users click "Assign". -* The system shows the selected list of materials on the "Materials" tab with new fields to complete. +* The system shows the selected list of materials on the "Materials" tab with new fields to complete. .. figure:: images/order-element-material-assign.png :scale: 50 @@ -269,7 +269,7 @@ For subsequent monitoring of materials, it is possible to change the status of a * The program shows two rows with the material divided. * Users change the status of the row containing the material. -The advantage of using this dividing tool is the possibility of receiving partial deliveries of material without having to wait to receive it all in order to mark it as received. +The advantage of using this dividing tool is the possibility of receiving partial deliveries of material without having to wait to receive it all in order to mark it as received. Managing quality forms ------------------------------------ @@ -290,7 +290,7 @@ To manage quality forms: * The program has a search engine for quality forms. There are two types of quality forms: according to element or percentage. * Element: Every element is independent. - * Percentage: Every question increases progress in the order element by a percentage. It must be possible for percentages to be increased to 100%. + * Percentage: Every question increases progress in the order element by a percentage. It must be possible for percentages to be increased to 100%. * Users select one of the forms created in the administration interface and click "Assign". * The program assigns the form chosen from the list of forms assigned to the order element. diff --git a/doc/src/user/en/07-planificacion.rst b/doc/src/user/en/07-planificacion.rst index 57da08cae..7724fbad8 100644 --- a/doc/src/user/en/07-planificacion.rst +++ b/doc/src/user/en/07-planificacion.rst @@ -1,4 +1,4 @@ -Task planning +Task planning ####################### .. _planificacion: @@ -8,7 +8,7 @@ Task planning ============= -Planning in “NavalPlan” is a process that has been described throughout all of the chapters of the user guide, the chapters on orders and the assigning of resources being particularly important in this respect. This chapter describes basic planning procedures after the order and the Gantt charts have been configured properly. +Planning in "NavalPlan" is a process that has been described throughout all of the chapters of the user guide, the chapters on orders and the assigning of resources being particularly important in this respect. This chapter describes basic planning procedures after the order and the Gantt charts have been configured properly. .. figure:: images/planning-view.png :scale: 35 @@ -34,7 +34,7 @@ The planning view combines three different views: * Orange area: Indicating a resource load over 100% as a result of the current project. * Yellow area: Indicating a resource load over 100% as a result of other projects. -* Graph view and value gained indicators. These can be viewed from the “Value gained” tab. The generated graph is based on the value gained technique and the indicators that are calculated for each of the workdays of the project. The calculated indicators are: +* Graph view and value gained indicators. These can be viewed from the "Value gained" tab. The generated graph is based on the value gained technique and the indicators that are calculated for each of the workdays of the project. The calculated indicators are: * BCWS: accumulative time function for the number of hours planned up to a certain date. It will be 0 at the planned start of the task and the total number of planned hours at the end. As with all accumulative graphs, it will always increase. The function for a task will be the sum of the daily assignments until the calculation day. This function has values for all times, provided that resources have been assigned. * ACWP: accumulative time function for the hours attributed in the work reports up to a certain date. This function will only have a value of 0 before the date of the task's first work report and its value will continue to increase as time passes and work report hours are added. It will have no value after the date of the last work report. @@ -56,20 +56,20 @@ In the project planning, users can carry out the following procedures: * Creating a new milestone. Click the task before the milestone that is to be added and select the "Add milestone" option. Milestones can be moved by selecting the task with the mouse pointer and dragging it to the desired position. * Moving tasks without disturbing dependencies. Right click the body of the task, and drag it to the desired position. If no restrictions or dependencies are disturbed, the system will update the daily assignment of resources to the task and place the task in the selected date. - * Assign restrictions. Click the task in question and select the ”Task properties” option. A *pop-up* will appear with a “Restrictions” field that can be changed. Restrictions can conflict with dependencies, which is why each order states whether dependencies take priority or not over restrictions. The restrictions that can be established are: + * Assign restrictions. Click the task in question and select the "Task properties" option. A *pop-up* will appear with a "Restrictions" field that can be changed. Restrictions can conflict with dependencies, which is why each order states whether dependencies take priority or not over restrictions. The restrictions that can be established are: * *As soon as possible*: Indicating that the task must start as soon as possible. * *Not before*. Indicating that the task must not start before a certain date. * *Start on a specific date*. Indicating that the task must start on a specific date. -The planning view also offers several procedures that ultimately function as viewing options: +The planning view also offers several procedures that ultimately function as viewing options: * Zoom level: Users can choose the zoom level they require. There are several zoom levels: annual, four-monthly, monthly, weekly and daily. * Search filters: Users can filter tasks based on labels or criteria. * Critical path. As a result of using the *Dijkstra* algorithm to calculate paths on graphs, the critical path was implemented which can be viewed by clicking on the "Critical path" button from the viewing options. -* Show labels: Enabling users to view the labels assigned to tasks in a project, which can be viewed on screen or printed. -* Show resources: Enabling users to view the resources assigned to tasks in a project, which can be viewed on screen or printed. +* Show labels: Enabling users to view the labels assigned to tasks in a project, which can be viewed on screen or printed. +* Show resources: Enabling users to view the resources assigned to tasks in a project, which can be viewed on screen or printed. * Print: Enabling users to print the Gantt chart being viewed at that moment. Resource load view @@ -94,7 +94,7 @@ The order list view allows users to go to the order editing and deleting options Advanced assignment view ---------------------------- -The advanced assignment view is explained in depth in the “Resource assignment” chapter. +The advanced assignment view is explained in depth in the "Resource assignment" chapter. diff --git a/doc/src/user/en/08-asignacion.rst b/doc/src/user/en/08-asignacion.rst index de1cf33c5..163deffcf 100644 --- a/doc/src/user/en/08-asignacion.rst +++ b/doc/src/user/en/08-asignacion.rst @@ -1,4 +1,4 @@ -Assignment of resources +Assignment of resources ######################## .. asigacion_ @@ -11,7 +11,7 @@ The assignment of resources is one of the program's most important features, and Both types of assignment are explained in the following sections. -To carry out either of the two types of resource assignment, the following steps are necessary: +To carry out either of the two types of resource assignment, the following steps are necessary: * Go to the planning of an order. * Right click on the task to be planned. @@ -37,7 +37,7 @@ To carry out either of the two types of resource assignment, the following steps .. figure:: images/resource-assignment.png :scale: 50 - Resource assignment + Resource assignment * Users select "Search resources". * The program shows a new screen consisting of a criteria tree and a list to the right of workers that fulfil the selected criteria: @@ -64,7 +64,7 @@ To carry out either of the two types of resource assignment, the following steps Specific assignment =================== -This is the specific assignment of a resource to a project task, i.e. the user decides which specific “name and surname(s)” or “machine” must be assigned to a task. +This is the specific assignment of a resource to a project task, i.e. the user decides which specific "name and surname(s)" or "machine" must be assigned to a task. Specific assignment can be carried out on the screen shown in this image: @@ -72,7 +72,7 @@ Specific assignment can be carried out on the screen shown in this image: .. figure:: images/asignacion-especifica.png :scale: 50 - Specific resource assignment + Specific resource assignment When a resource is specifically assigned, the program creates daily assignments in relation to the percentage of daily assigned resources selected, by previously comparing it with the available resource calendar. For example, an assignment of 0.5 resources for a 32-hour task means that 4 hours per day are assigned to the specific resource to fulfil the task (supposing a working calendar of 8 hours per day). @@ -92,7 +92,7 @@ Generic assignment occurs when users do not choose resources specifically, but l .. figure:: images/asignacion-xenerica.png :scale: 50 - Generic resource assignment + Generic resource assignment The assignment system uses the following assumptions as a basis: @@ -105,19 +105,19 @@ The generic assignment algorithm functions in the following way: * All resources and days are treated as containers where daily assignment of hours fit, based on the maximum assignment capacity in the task calendar. * The system searches for the resources that fulfil the criterion. -* The system analyses which assignments currently have different resources that fulfil criteria. +* The system analyses which assignments currently have different resources that fulfil criteria. * The resources that fulfil the criteria are chosen from those that have sufficient availability. * If freer resources are not available, assignments are made to the resources that have less availability. * Over-assignment of resources only starts when all the resources that fulfil the respective criteria are 100% assigned until the total amount required to carry out the task is attained. -Generic machine assignment +Generic machine assignment -------------------------- Generic machine assignment functions in the same way as worker assignment. For example, when a machine is assigned to a task, the system stores a generic assignment of hours for all machines that fulfil the criteria as described for the resources in general. However, in addition, the system performs the following procedure for machines: * For all machines chosen for generic assignment: - * It collects the machine's configuration information: alpha value, assigned workers and criteria. + * It collects the machine's configuration information: alpha value, assigned workers and criteria. * If the machine has an assigned list of workers, the program chooses the number required by the machine depending on the assigned calendar. For example, if the machine calendar is 16 hours per day and the resource calendar is 8 hours, the program assigns two resources from the list of available resources. * If the machine has one or several assigned criteria, the program makes generic assignments from among the resources that fulfil the criteria assigned to the machine. @@ -125,14 +125,14 @@ Generic machine assignment functions in the same way as worker assignment. For e Advanced assignment =================== -Advanced assignments allow users to design assignments that are automatically carried out by the application in order to personalise them. This procedure allows users to manually choose the daily hours that are dedicated by resources to tasks that are assigned or define a function that is applied to the assignment. +Advanced assignments allow users to design assignments that are automatically carried out by the application in order to personalise them. This procedure allows users to manually choose the daily hours that are dedicated by resources to tasks that are assigned or define a function that is applied to the assignment. The steps to follow in order to manage advanced assignments are: * Go to the advanced assignment window. There are two ways to access advanced assignments: * Go to a specific order and change the view to advanced assignment. In this case, all the tasks on the order and assigned resources (specific and generic) will be shown. - * Go to the resource assignment window by clicking the "Advanced assignment" button. In this case, the assignments that show the resources (generic and specific) assigned for a task will be shown. + * Go to the resource assignment window by clicking the "Advanced assignment" button. In this case, the assignments that show the resources (generic and specific) assigned for a task will be shown. .. figure:: images/advance-assignment.png :scale: 45 @@ -153,12 +153,12 @@ The steps to follow in order to manage advanced assignments are: * Date. Date on which the segment ends. If the following value is established (length), the date is calculated, alternatively, length is calculated. - * Defining the length of each segment. This indicates what percentage of the task’s duration is required for the segment. + * Defining the length of each segment. This indicates what percentage of the task's duration is required for the segment. * Defining the amount of work. This indicates what workload percentage is expected to be completed in this segment. The quantity of work must be incremental. For example, if there is a 10% segment, the next one must be larger (for example, 20%). * Segment graphs and accumulated loads. - * Users then click “Accept”. + * Users then click "Accept". * The program stores the function and applies it to the daily resource assignments. .. figure:: images/stretches.png diff --git a/doc/src/user/en/09-partes.rst b/doc/src/user/en/09-partes.rst index fb7518213..90a6d5d5f 100644 --- a/doc/src/user/en/09-partes.rst +++ b/doc/src/user/en/09-partes.rst @@ -1,25 +1,25 @@ -Work reports +Work reports ################# .. contents:: Work reports enable the monitoring of the hours that existing resources dedicate to the tasks in which they are scheduled. -The program allows users to configure new forms to enter dedicated hours, specifying the fields that they want to appear in these models, to incorporate reports from tasks that are carried out by workers and to monitor workers. +The program allows users to configure new forms to enter dedicated hours, specifying the fields that they want to appear in these models, to incorporate reports from tasks that are carried out by workers and to monitor workers. Before being able to add entries for resources, users must at least specify a work report type that defines the structure, including all the rows that are added to it. Users can create as many work reports on the system as necessary. -Work report type +Work report type ================ -A work report has a series of fields that are common to the whole report, and a set of work report lines with specific values for the fields defined in each row. For example, resources and tasks are common to all reports, however, there can be other new fields such as “incidents”, which are not required in all types. +A work report has a series of fields that are common to the whole report, and a set of work report lines with specific values for the fields defined in each row. For example, resources and tasks are common to all reports, however, there can be other new fields such as "incidents", which are not required in all types. Users can configure different work report types so that a company can design its reports to meet its own needs: .. figure:: images/work-report-types.png :scale: 40 - Work report types + Work report types The administration of the work report types allows users to configure this type of feature and add new text fields or optional tags. In the first tab for editing work report types, it is possible to configure the type for the obligatory attributes (if they are applicable to the whole report or if they are specified at line level), and add new optional fields. @@ -45,16 +45,16 @@ Users can add new fields to the reports: Creating a work report type with personalised fields -Users can configure date, resource and order element fields if they appear in the header of the report, which means they apply to the whole report, or if they are added to each of the rows. +Users can configure date, resource and order element fields if they appear in the header of the report, which means they apply to the whole report, or if they are added to each of the rows. -Finally, new additional text fields or tags can be added to the existing ones, in the work report header or in each line, by using the “Additional text” and “Tag type” fields respectively. Users can configure the order in which these elements are to be entered in the “Management of additional fields and tags” tab. +Finally, new additional text fields or tags can be added to the existing ones, in the work report header or in each line, by using the "Additional text" and "Tag type" fields respectively. Users can configure the order in which these elements are to be entered in the "Management of additional fields and tags" tab. Work report list ================ As soon as the format of the reports to be incorporated into the system have been configured, users can enter the details in the created form according to the structure defined in the corresponding work report type. In order to do this, users need to follow these steps: -* Click the “New work report” button associated with the desired report from the list of work report types. +* Click the "New work report" button associated with the desired report from the list of work report types. * The program then shows the report based on the configurations given for the type. See the following image. .. figure:: images/work-report-type.png @@ -68,8 +68,8 @@ As soon as the format of the reports to be incorporated into the system have bee * Task code: Code of the task to which the work report is being assigned. Similar to the rest of the fields, if the field is the header, the value is entered once or as many times as necessary on the lines of the report. * Date: Date of the report or each line, depending on whether the heading or line is configured. * Number of hours. The number of work hours in the project. - * Start and finish times. Start and finish times for the work in order to calculate definitive work hours. This field only appears in the case of the hour assignment policies, “According to start and finish times” and ”According to the number of hours and start and finish range”. - * Type of hours: Enabling users to choose the type of hour, e.g. “Normal”, “Extraordinary”, etc. + * Start and finish times. Start and finish times for the work in order to calculate definitive work hours. This field only appears in the case of the hour assignment policies, "According to start and finish times" and "According to the number of hours and start and finish range". + * Type of hours: Enabling users to choose the type of hour, e.g. "Normal", "Extraordinary", etc. * Click "Save" or "Save and continue". diff --git a/doc/src/user/en/10-etiquetas.rst b/doc/src/user/en/10-etiquetas.rst index 17160b5d2..ca8d23e92 100644 --- a/doc/src/user/en/10-etiquetas.rst +++ b/doc/src/user/en/10-etiquetas.rst @@ -1,4 +1,4 @@ -Tags +Tags ######### .. contents:: @@ -15,12 +15,12 @@ Tag types are used to group the types of tag that users want to manage in the pr i. Client: Users may be interested in tagging tasks, orders or order elements in relation to the client who requests them. ii. Area: Users may be interested in tagging tasks, orders or order elements in relation to the areas in which they are carried out. -The administration of tag types is managed from the “Administration” menu option. This is where users can edit tag types, create new tag types and add tags to tag types. Users can access the list of tags from this option. +The administration of tag types is managed from the "Administration" menu option. This is where users can edit tag types, create new tag types and add tags to tag types. Users can access the list of tags from this option. .. figure:: images/tag-types-list.png :scale: 50 - List of tag types + List of tag types From the list of tag types, users can: @@ -32,11 +32,11 @@ Editing and creating tags share the same form. From this form, the user can assi i. Select a tag to edit or click the create button for a new one. ii. The system shows a form with a text entry for the name and a list of text entries with existing and assigned tags. -iii. If users wish to add a new tag, they must click on the “New tag” button. +iii. If users wish to add a new tag, they must click on the "New tag" button. iv. The system shows a new row on the list with an empty text box that users must edit. v. Users enter a name for the tag. vi. The system adds the name to the list. -vii. Users click “Save” or “Save and continue” to continue editing the form. +vii. Users click "Save" or "Save and continue" to continue editing the form. .. figure:: images/tag-types-edition.png :scale: 50 diff --git a/doc/src/user/en/11-materiales.rst b/doc/src/user/en/11-materiales.rst index 7cde2f33c..8368a77ac 100644 --- a/doc/src/user/en/11-materiales.rst +++ b/doc/src/user/en/11-materiales.rst @@ -1,4 +1,4 @@ -Materials +Materials ########## .. _materiales: .. contents:: @@ -7,18 +7,18 @@ Administration of materials =========================== -Users can manage a basic database of materials, organised by categories. +Users can manage a basic database of materials, organised by categories. The categories are containers that can be assigned specific materials and also more categories. They are stored in a tree structure as the materials can belong to leaf or intermediary categories. Users have to do the following to manage categories: -* Go to the “Administration->Materials” option. +* Go to the "Administration->Materials" option. * The program shows a tree of categories. -* The user enters a category name in the text box and then clicks “Add”. +* The user enters a category name in the text box and then clicks "Add". * The program adds the category to the tree. -If users want to insert a category into the category tree, they have to first select the parent category in the tree and then click “Add”. +If users want to insert a category into the category tree, they have to first select the parent category in the tree and then click "Add". .. figure:: images/material.png :scale: 50 @@ -27,7 +27,7 @@ If users want to insert a category into the category tree, they have to first se Users have to do the following to manage materials: -* Select the category to which materials are to be included and click ”Add” to the right of “Materials”. +* Select the category to which materials are to be included and click "Add" to the right of "Materials". * The program adds a new empty row with fields to enter details about the material: * Code: Material type code (this can be the external code from an ERP). @@ -37,7 +37,7 @@ Users have to do the following to manage materials: * Category: Category to which it belongs. * Availability: Whether or not the material has been removed. -* Users complete the fields and click “Save”. +* Users complete the fields and click "Save". The assignment of materials to order elements is explained in the chapter on "Orders". diff --git a/doc/src/user/en/12-formularios-calidad.rst b/doc/src/user/en/12-formularios-calidad.rst index b79db1cea..6c6b33b9f 100644 --- a/doc/src/user/en/12-formularios-calidad.rst +++ b/doc/src/user/en/12-formularios-calidad.rst @@ -1,4 +1,4 @@ -Quality forms +Quality forms ###################### .. _calidad: @@ -19,8 +19,8 @@ Quality forms consist of a list of questions or sentences that indicate the task Users must carry out the following steps to manage the quality forms: -* From the “Administration” menu, access the “Quality forms” option. -* Click edit an existing form or “Create”. +* From the "Administration" menu, access the "Quality forms" option. +* Click edit an existing form or "Create". * The program shows a form with a name, description and type. * Select the type. * The program shows the fields that are allowed for each type: @@ -28,7 +28,7 @@ Users must carry out the following steps to manage the quality forms: * Percentage: question and percentage. * Item: question. -* Click “Save” or “Save and continue”. +* Click "Save" or "Save and continue". .. figure:: images/quality.png :scale: 50 diff --git a/doc/src/user/en/13-usuarios.rst b/doc/src/user/en/13-usuarios.rst index bd647590a..b44744c95 100644 --- a/doc/src/user/en/13-usuarios.rst +++ b/doc/src/user/en/13-usuarios.rst @@ -1,4 +1,4 @@ -Users +Users ######## .. _tareas: @@ -17,27 +17,27 @@ Managing users Roles are predefined in the system. A user profile consists of one or several roles. Users must have certain roles to carry out certain operations. Users have one or several profiles or one or several roles directly so that specific or generic authorisation can be assigned. - + It is necessary to carry out the following steps to manage users: -* Go to “Manage users” on the “Administration” menu. +* Go to "Manage users" on the "Administration" menu. * The program shows a form with a list of users. -* Click the editing button for the chosen user or click the “Create” button. +* Click the editing button for the chosen user or click the "Create" button. * A form will appear with the following fields: * User name. * Password * Authorised/unauthorised. * Email - * List of associated roles. Users need to search for one of the roles shown on the selection list and click “Assign” to add a new role. - * List of associated profiles. Users need to search for one of the profiles shown on the selection list and click “Assign” to add a new profile. + * List of associated roles. Users need to search for one of the roles shown on the selection list and click "Assign" to add a new role. + * List of associated profiles. Users need to search for one of the profiles shown on the selection list and click "Assign" to add a new profile. .. figure:: images/manage-user.png :scale: 50 Managing users -* Click “Save” or “Save and continue”. +* Click "Save" or "Save and continue". Managing profiles @@ -45,13 +45,13 @@ Managing profiles Users need to carry out the following steps to manage the program's profiles. -* Go to “Manage user profiles” on the “Administration” menu. +* Go to "Manage user profiles" on the "Administration" menu. * The program shows a list of profiles. -* Click the editing button for the chosen profile or click “Create”. +* Click the editing button for the chosen profile or click "Create". * A form appears in the program with the following fields: * Name - * List of roles (authorisations) associated with the profile. Users must select a role from the role list and click “Add” to add one that is associated with the profile. + * List of roles (authorisations) associated with the profile. Users must select a role from the role list and click "Add" to add one that is associated with the profile. .. figure:: images/manage-user-profile.png :scale: 50 diff --git a/doc/src/user/en/14-custos.rst b/doc/src/user/en/14-custos.rst index 5ee0a1f53..f7d237f3c 100644 --- a/doc/src/user/en/14-custos.rst +++ b/doc/src/user/en/14-custos.rst @@ -29,7 +29,7 @@ Managing hour types worked Users need to carry out the following steps to register hour types worked: -* Select “Manage hour types worked ” on the “Administration” menu. +* Select "Manage hour types worked" on the "Administration" menu. * The program shows a list of existing hour types. .. figure:: images/hour-type-list.png @@ -37,7 +37,7 @@ Users need to carry out the following steps to register hour types worked: Hour type list -* Click “Edit” or “Create”. +* Click "Edit" or "Create". * The program shows an hour type editing form. .. figure:: images/hour-type-edit.png @@ -52,22 +52,22 @@ Users need to carry out the following steps to register hour types worked: * The default rate. * Hour type activation/de-activation. -* Click “Save” or “Save and continue”. +* Click "Save" or "Save and continue". Cost categories ------------------- Users need to carry out the following steps to register cost categories: -* Select “Manage cost categories” on the “Administration” menu. +* Select "Manage cost categories" on the "Administration" menu. * The program shows a list of existing categories. .. figure:: images/category-cost-list.png :scale: 50 - Cost category list + Cost category list -* Click “Edit” or “Create” button. +* Click "Edit" or "Create" button. * The program shows a cost category editing form. .. figure:: images/category-cost-edit.png @@ -85,8 +85,8 @@ Users need to carry out the following steps to register cost categories: * Start and finish date (the latter is optional) for the period that applies to the cost category. * Hourly rate for this specific category -* Click “Save” or “Save and continue”. +* Click "Save" or "Save and continue". -The assignment of cost categories to resources is described in the chapter on resources. Go to the “Resources” section. +The assignment of cost categories to resources is described in the chapter on resources. Go to the "Resources" section. diff --git a/doc/src/user/en/15-informes.rst b/doc/src/user/en/15-informes.rst index 813187df9..f70a40df1 100644 --- a/doc/src/user/en/15-informes.rst +++ b/doc/src/user/en/15-informes.rst @@ -8,7 +8,7 @@ Reports ======= -“NavalPlan” is integrated with *JasperReports* to manage reports, which allows users to enter various reports to analyse available data in the program. +"NavalPlan" is integrated with *JasperReports* to manage reports, which allows users to enter various reports to analyse available data in the program. The defined reports are: diff --git a/doc/src/user/en/Makefile b/doc/src/user/en/Makefile index 7b3844c14..76106c230 100644 --- a/doc/src/user/en/Makefile +++ b/doc/src/user/en/Makefile @@ -4,7 +4,7 @@ # RST_HTML_FLAGS = --link-stylesheet --stylesheet-path=html/lsr.css -RST_TEX_FLAGS = --documentclass=igaliabk --font-encoding=OT1 +RST_TEX_FLAGS = --documentclass=igaliabk --font-encoding=OT1 --output-encoding=utf-8 OUTPUT_BASE = output rst_srcs := $(filter-out index.rst,$(wildcard *.rst)) diff --git a/doc/src/user/en/docinfo b/doc/src/user/en/docinfo index ebce3fa22..6ba3c98a3 100644 --- a/doc/src/user/en/docinfo +++ b/doc/src/user/en/docinfo @@ -1,16 +1,16 @@ -####################################### -Documentación de usuario da aplicación -####################################### +############################# +Navalplan: User documentation +############################# .. image:: images/logo.png :align: left -No seguinte documento proporciónase a documentación de axuda necesaria para utilizar a aplicación de xestión da produción do auxiliar do naval NavalPlan. +Following document contains necessary help for using Navalplan, the application for production and planning management for Galician ancillary naval industry. -Esta documentación estó organizada do seguinte modo: +This document is divided in three main sections: -En primeiro lugar descrébense os obxectivos fundamentais da aplicación e o comportamento global da mesma a modo introductorio e como contextualización xeral do uso da mesma. +First, global goals and behaviour. -A continuación introdúcense as entidades básicas que será necesario administrar para poder empregar todas as funcionalidades de NavalPlan e que se mencionarán nas seguintes seccións da documentación. +Second, basic entities to understand the minimum concepts to use Navalplan. -Posteriormente, detállanse os procesos completos de creación de pedidos e proxectos, planificación, asignación de recursos, imputación de avances e extración de resultados. +Finally, complete processes description to create orders, projects, project planning, resources assignment, advance assignment and result extraction. diff --git a/doc/src/user/es/docinfo b/doc/src/user/es/docinfo index ebce3fa22..d499f9204 100644 --- a/doc/src/user/es/docinfo +++ b/doc/src/user/es/docinfo @@ -5,12 +5,12 @@ Documentación de usuario da aplicación .. image:: images/logo.png :align: left -No seguinte documento proporciónase a documentación de axuda necesaria para utilizar a aplicación de xestión da produción do auxiliar do naval NavalPlan. +En el siguiente document se proporciona la documentación de ayuda necesaria para utilizar la aplicación de gestión de la producción y planificación del auxiliar del naval Navalplan. -Esta documentación estó organizada do seguinte modo: +Esta documentación está organizada del siguiente modo: -En primeiro lugar descrébense os obxectivos fundamentais da aplicación e o comportamento global da mesma a modo introductorio e como contextualización xeral do uso da mesma. +En primer lugar se describen los objetivos principales de la aplicación, comportamiento global y contextualización general del uso y necesidades de la misma. -A continuación introdúcense as entidades básicas que será necesario administrar para poder empregar todas as funcionalidades de NavalPlan e que se mencionarán nas seguintes seccións da documentación. +A continuación se introducen las entidades básicas que será necesario conocer para utilizar las funcionalidades de Navalplan. -Posteriormente, detállanse os procesos completos de creación de pedidos e proxectos, planificación, asignación de recursos, imputación de avances e extración de resultados. +Finalmente, se detallan los procesos completos de creación de pedidos, proyectos, planificación de proyectos, asignación de recursos, imputación de avances y extracción de resultados. diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/IChartVisibilityChangedListener.java b/ganttzk/src/main/java/org/zkoss/ganttz/IChartVisibilityChangedListener.java new file mode 100644 index 000000000..48c6f7d3b --- /dev/null +++ b/ganttzk/src/main/java/org/zkoss/ganttz/IChartVisibilityChangedListener.java @@ -0,0 +1,32 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.zkoss.ganttz; + +/** + * Listener for changes in the charts visibility on planner layout. + * + * @author Manuel Rego Casasnovas + */ +public interface IChartVisibilityChangedListener { + + public void chartVisibilityChanged(boolean visible); + +} diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java b/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java index ce6d300c3..fda8a090a 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/LeftTasksTreeRow.java @@ -372,6 +372,7 @@ public class LeftTasksTreeRow extends GenericForwardComposer { private void updateComponents() { getNameBox().setValue(task.getName()); + getNameBox().setDisabled(!canRenameTask()); getNameBox().setTooltiptext(task.getName()); getStartDateBox().setValue(task.getBeginDate()); @@ -396,6 +397,10 @@ public class LeftTasksTreeRow extends GenericForwardComposer { && task.canBeExplicitlyResized(); } + private boolean canRenameTask() { + return disabilityConfiguration.isRenamingTasksEnabled(); + } + private String asString(Date date) { return dateFormat.format(date); } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java index c534e48c2..2588fce4a 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/Planner.java @@ -49,7 +49,9 @@ import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.ComponentsFinder; import org.zkoss.ganttz.util.LongOperationFeedback; import org.zkoss.ganttz.util.OnZKDesktopRegistry; +import org.zkoss.ganttz.util.WeakReferencedListeners; import org.zkoss.ganttz.util.LongOperationFeedback.ILongOperation; +import org.zkoss.ganttz.util.WeakReferencedListeners.IListenerNotification; import org.zkoss.ganttz.util.script.IScriptsRegister; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; @@ -58,6 +60,7 @@ import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.Clients; +import org.zkoss.zkex.zul.api.South; import org.zkoss.zul.Button; import org.zkoss.zul.ListModel; import org.zkoss.zul.Listbox; @@ -138,6 +141,9 @@ public class Planner extends HtmlMacroComponent { private Listbox listZoomLevels = null; + private WeakReferencedListeners chartVisibilityListeners = WeakReferencedListeners + .create(); + public Planner() { registerNeededScripts(); } @@ -301,6 +307,8 @@ public class Planner extends HtmlMacroComponent { } listZoomLevels.setSelectedIndex(getZoomLevel().ordinal()); + this.visibleChart = configuration.isExpandPlanningViewCharts(); + ((South) getFellow("graphics")).setOpen(this.visibleChart); } private void resettingPreviousComponentsToNull() { @@ -450,6 +458,8 @@ public class Planner extends HtmlMacroComponent { private FilterAndParentExpandedPredicates predicate; + private boolean visibleChart; + public void showCriticalPath() { Button showCriticalPathButton = (Button) getFellow("showCriticalPath"); if (disabilityConfiguration.isCriticalPathEnabled()) { @@ -592,4 +602,25 @@ public class Planner extends HtmlMacroComponent { return predicate; } + public void changeChartVisibility(boolean visible) { + visibleChart = visible; + chartVisibilityListeners + .fireEvent(new IListenerNotification() { + @Override + public void doNotify( + IChartVisibilityChangedListener listener) { + listener.chartVisibilityChanged(visibleChart); + } + }); + } + + public boolean isVisibleChart() { + return visibleChart; + } + + public void addChartVisibilityListener( + IChartVisibilityChangedListener chartVisibilityChangedListener) { + chartVisibilityListeners.addListener(chartVisibilityChangedListener); + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IDisabilityConfiguration.java b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IDisabilityConfiguration.java index beb61b19b..350727b42 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IDisabilityConfiguration.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/IDisabilityConfiguration.java @@ -39,4 +39,8 @@ public interface IDisabilityConfiguration { public boolean isFlattenTreeEnabled(); + public boolean isRenamingTasksEnabled(); + + public boolean isExpandPlanningViewCharts(); + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java index ec5ccb59a..7de0c65a9 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/adapters/PlannerConfiguration.java @@ -134,6 +134,8 @@ public class PlannerConfiguration implements IDisabilityConfiguration { private boolean flattenTreeEnabled = true; + private boolean renamingTasksEnabled = true; + // private String identifier = null; private IDetailItemModificator firstLevelModificators = SeveralModificators @@ -146,6 +148,8 @@ public class PlannerConfiguration implements IDisabilityConfiguration { private IPrintAction printAction; + private boolean expandPlanningViewCharts; + public PlannerConfiguration(IAdapterToTaskFundamentalProperties adapter, IStructureNavigator navigator, List data) { this.adapter = adapter; @@ -298,6 +302,15 @@ public class PlannerConfiguration implements IDisabilityConfiguration { return flattenTreeEnabled; } + public void setRenamingTasksEnabled(boolean renamingTasksEnabled) { + this.renamingTasksEnabled = renamingTasksEnabled; + } + + @Override + public boolean isRenamingTasksEnabled() { + return renamingTasksEnabled; + } + public IDetailItemModificator getSecondLevelModificators() { return secondLevelModificators; } @@ -358,4 +371,13 @@ public class PlannerConfiguration implements IDisabilityConfiguration { printAction.doPrint(parameters, planner); } + public void setExpandPlanningViewCharts(boolean expandPlanningViewCharts) { + this.expandPlanningViewCharts = expandPlanningViewCharts; + } + + @Override + public boolean isExpandPlanningViewCharts() { + return expandPlanningViewCharts; + } + } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/data/limitingresource/QueueTask.java b/ganttzk/src/main/java/org/zkoss/ganttz/data/limitingresource/QueueTask.java new file mode 100644 index 000000000..a6a2f4946 --- /dev/null +++ b/ganttzk/src/main/java/org/zkoss/ganttz/data/limitingresource/QueueTask.java @@ -0,0 +1,128 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.zkoss.ganttz.data.limitingresource; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.apache.commons.lang.builder.ToStringBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.LocalDate; +import org.zkoss.ganttz.data.resourceload.LoadLevel; + +public class QueueTask { + + private static final Log LOG = LogFactory.getLog(QueueTask.class); + + private final LocalDate start; + + private final LocalDate end; + + private final LoadLevel loadLevel; + + private final int totalResourceWorkHours; + + private final int assignedHours; + + public QueueTask(LocalDate start, LocalDate end, + int totalResourceWorkHours, int assignedHours, LoadLevel loadLevel) { + Validate.notNull(start); + Validate.notNull(end); + Validate.notNull(loadLevel); + Validate.notNull(totalResourceWorkHours); + Validate.notNull(assignedHours); + Validate.isTrue(!start.isAfter(end)); + this.start = start; + this.end = end; + this.loadLevel = loadLevel; + this.totalResourceWorkHours = totalResourceWorkHours; + this.assignedHours = assignedHours; + } + + public LocalDate getStart() { + return start; + } + + public LocalDate getEnd() { + return end; + } + + // public boolean overlaps(QueueTask other) { + // return start.isBefore(other.end) && end.isAfter(other.start); + // } + + /** + * @param loadPeriods + * @return + * @throws IllegalArgumentException + * if some of the QueueTask overlaps + */ + public static List sort(List loadPeriods) + throws IllegalArgumentException { + ArrayList result = new ArrayList(loadPeriods); + // Collections.sort(result, new Comparator() { + + // @Override + // public int compare(QueueTask o1, QueueTask o2) { + // if (o1.overlaps(o2)) { + // LOG.warn(o1 + " overlaps with " + o2); + // throw new IllegalArgumentException(o1 + " overlaps with " + // + o2); + // } + // int comparison = compareLocalDates(o1.start, o2.start); + // if (comparison != 0) { + // return comparison; + // } + // return compareLocalDates(o1.end, o2.end); + // } + // }); + return result; + } + + private static int compareLocalDates(LocalDate l1, LocalDate l2) { + if (l1.isBefore(l2)) { + return -1; + } + if (l1.isAfter(l2)) { + return 1; + } + return 0; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public LoadLevel getLoadLevel() { + return loadLevel; + } + + public int getTotalResourceWorkHours() { + return totalResourceWorkHours; + } + + public int getAssignedHours() { + return assignedHours; + } +} diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java index 54e713795..32b1a4bbb 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourceLoadLeftPane.java @@ -222,7 +222,6 @@ MutableTreeModel modelForTree, Label label = new Label(); final String conceptName = principal.getConceptName(); label.setValue(conceptName); - limitValue(result, label, 40); result.appendChild(label); return result; } diff --git a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java index 2d92695a1..aec34ac31 100644 --- a/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java +++ b/ganttzk/src/main/java/org/zkoss/ganttz/resourceload/ResourcesLoadPanel.java @@ -44,9 +44,11 @@ import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zul.Button; +import org.zkoss.zul.Comboitem; import org.zkoss.zul.ListModel; import org.zkoss.zul.Separator; import org.zkoss.zul.SimpleListModel; +import org.zkoss.zul.api.Combobox; import org.zkoss.zul.api.Listbox; public class ResourcesLoadPanel extends HtmlMacroComponent { @@ -82,6 +84,10 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { private String feedBackMessage; private Boolean filterbyResources; + private boolean refreshNameFilter = true; + private int filterByNamePosition = 0; + private int numberOfGroupsByName = 10; + public ResourcesLoadPanel(List groups, TimeTracker timeTracker, Component componentOnWhichGiveFeedback) { this.componentOnWhichGiveFeedback = componentOnWhichGiveFeedback; @@ -112,6 +118,8 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { this.filterbyResources = false; this.feedBackMessage = _("showing criterions"); } + refreshNameFilter = true; + filterByNamePosition = 0; invalidatingChangeHappenedWithFeedback(); } @@ -229,7 +237,7 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { private MutableTreeModel createModelForTree() { MutableTreeModel result = MutableTreeModel .create(LoadTimeLine.class); - for (LoadTimeLine loadTimeLine : this.groups) { + for (LoadTimeLine loadTimeLine : this.getGroupsToShow()) { result.addToRoot(loadTimeLine); result = addNodes(result, loadTimeLine); } @@ -272,10 +280,19 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { TimeTrackerComponent timeTrackerHeader = createTimeTrackerHeader(); getFellow("insertionPointTimetracker").appendChild(timeTrackerHeader); + Component additionalFilter = (Component) getVariable("additionalFilter", true); + if(additionalFilter != null) { + getFellow("additionalFilterInsertionPoint").appendChild(additionalFilter); + } + timeTrackerHeader.afterCompose(); timeTrackerComponent.afterCompose(); listZoomLevels = (Listbox) getFellow("listZoomLevels"); listZoomLevels.setSelectedIndex(timeTracker.getDetailLevel().ordinal()); + + if(refreshNameFilter) { + setupNameFilter(); + } } public void clearComponents() { @@ -300,4 +317,74 @@ public class ResourcesLoadPanel extends HtmlMacroComponent { resourceLoadList.addSeeScheduledOfListener(seeScheduledOfListener); } + private void setupNameFilter() { + Combobox filterByNameCombo = (Combobox) getFellow("filterByNameCombo"); + filterByNameCombo.getChildren().clear(); + int size = groups.size(); + + if(size > numberOfGroupsByName) { + int position = 0; + while(position < size) { + String firstName = groups.get(position).getConceptName(); + String lastName; + int newPosition = position + numberOfGroupsByName; + if(newPosition - 1 < size) { + lastName = groups.get(newPosition - 1) + .getConceptName(); + } + else { + lastName = groups.get(size - 1) + .getConceptName(); + } + + Comboitem item = new Comboitem(); + item.setLabel(firstName.substring(0, 1) + " - " + lastName.substring(0, 1)); + item.setDescription(firstName + " - " + lastName); + item.setValue(new Integer(position)); + filterByNameCombo.appendChild(item); + position = newPosition; + } + } + + Comboitem lastItem = new Comboitem(); + lastItem.setLabel(_("All")); + lastItem.setDescription(_("Show all elements")); + lastItem.setValue(new Integer(-1)); + filterByNameCombo.appendChild(lastItem); + + filterByNameCombo.setSelectedIndex(0); + refreshNameFilter = false; + } + + /** + * Returns only the LoadTimeLine objects that have to be show + * according to the name filter. + * @return + */ + private List getGroupsToShow() { + if(filterByNamePosition == -1) { + return groups; + } + int endPosition = + (filterByNamePosition + numberOfGroupsByName < groups.size())? + filterByNamePosition + numberOfGroupsByName : + groups.size(); + return groups.subList(filterByNamePosition, endPosition); + } + + public void onSelectFilterByName(Integer filterByNamePosition) { + this.filterByNamePosition = filterByNamePosition.intValue(); + this.feedBackMessage = _("filtering by name"); + invalidatingChangeHappenedWithFeedback(); + } + + public void setNameFilterDisabled(boolean disabled) { + Combobox combo = ((Combobox) getFellow("filterByNameCombo")); + if(combo.isDisabled() != disabled) { + filterByNamePosition = disabled? -1 : + ((Integer)combo.getSelectedItemApi().getValue()).intValue(); + combo.setDisabled(disabled); + } + } + } \ No newline at end of file diff --git a/ganttzk/src/main/resources/metainfo/zk/lang-addon.xml b/ganttzk/src/main/resources/metainfo/zk/lang-addon.xml index db0359088..44f071ab3 100755 --- a/ganttzk/src/main/resources/metainfo/zk/lang-addon.xml +++ b/ganttzk/src/main/resources/metainfo/zk/lang-addon.xml @@ -48,6 +48,7 @@ ~./ganttz/resourceload/leftPane.zul + leftPane org.zkoss.ganttz.LeftPane @@ -79,6 +80,7 @@ + taskRow org.zkoss.ganttz.TaskRow @@ -145,6 +147,7 @@ + timetracker org.zkoss.ganttz.timetracker.TimeTrackerComponent diff --git a/ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul b/ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul index 427040b28..4f05784ae 100644 --- a/ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul +++ b/ganttzk/src/main/resources/web/ganttz/zul/plannerLayout.zul @@ -67,7 +67,8 @@ planner = self; - +
diff --git a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul index 01eb23f35..950b90d85 100644 --- a/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul +++ b/ganttzk/src/main/resources/web/ganttz/zul/resourcesLoadLayout.zul @@ -43,6 +43,12 @@ resourcesLoadPanel = self; model="${resourcesLoadPanel.filters}" onSelect="resourcesLoadPanel.setFilter(self.selectedItem.value);"> + + ${i18n:_('Show elements between')}: + + + diff --git a/ganttzk/src/main/resources/web/js/ganttz/limitingresources/limitingresourceslist.js b/ganttzk/src/main/resources/web/js/ganttz/limitingresources/limitingresourceslist.js new file mode 100644 index 000000000..60684ca4a --- /dev/null +++ b/ganttzk/src/main/resources/web/js/ganttz/limitingresources/limitingresourceslist.js @@ -0,0 +1,112 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +zkLimitingResourcesList = addLimitingResourcesListMethods( {}); + +function addLimitingResourcesListMethods(object) { + var scrollSync; + + function watermark() { + return document.getElementById('watermark'); + } + + function timetracker() { + return document.getElementById('timetracker'); + } + + function resourceloadlist() { + return YAHOO.util.Selector.query('.limitingresourceslist')[0]; + } + + function taskspanelgap() { + return YAHOO.util.Selector.query('.taskspanelgap')[0]; + } + + function resourcesloadgraph() { + return YAHOO.util.Selector.query('.resourcesloadgraph div')[0]; + } + + function scrolledpannel() { + return YAHOO.util.Selector.query('.rightpanellayout div')[0]; + } + + + function timetrackergap() { + return YAHOO.util.Selector.query('.timetrackergap')[0]; + } + + function leftpanel() { + return YAHOO.util.Selector.query('.leftpanelgap .z-tree-body')[0]; + } + + + object.init = function(cmp) { + this.adjustTimeTrackerSize(cmp); + YAHOO.util.Event.addListener(window, 'resize', + zkLimitingResourcesList.adjustTimeTrackerSize, cmp); + scrollSync = new ScrollSync(cmp); + scrollSync.synchXChangeTo(timetracker); + + listenToScroll(); + }; + + function listenToScroll() { + + var timetrackergap_ = timetrackergap(); + var scrolledpannel_ = scrolledpannel(); + var resourcesloadgraph_ = resourcesloadgraph(); + var leftpanel_ = leftpanel(); + + var onScroll = function() { + timetrackergap_.style["left"] = "-" + scrolledpannel_.scrollLeft + "px"; + leftpanel_.style["top"] = "-" + scrolledpannel_.scrollTop + "px"; + resourcesloadgraph_.scrollLeft = scrolledpannel_.scrollLeft; + + }; + + YAHOO.util.Selector.query('.rightpanellayout div')[0].onscroll = onScroll; + + } + + object.adjustTimeTrackerSize = function(cmp) { + watermark().style["height"] = cmp.clientHeight + "px"; + timetracker().style["width"] = cmp.clientWidth + "px"; + /* Set watermark width */ + YAHOO.util.Selector.query('.limitingresourceslist')[0].style["width"] = YAHOO.util.Selector + .query('.second_level_')[0].clientWidth + + "px"; + YAHOO.util.Selector.query('.rightpanellayout tr#watermark td')[0].style["height"] = + /* Calculate min : taskspanelgap().clientHeight + 120 + 'px'; ) */ + YAHOO.util.Selector.query('.limitingresourceslist')[0].clientHeight + 120 + + "px"; + }; + + object.adjustResourceLoadRows = function(cmp) { + YAHOO.util.Selector.query('.row_resourceload').each(function(node) { + node.style["width"] = cmp.clientWidth + "px"; + }); + }; + + object.adjustScrollHorizontalPosition = function(cmp, offsetInPx) { + cmp.scrollLeft = offsetInPx; + } + + return object; +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceMeasurement.java b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceMeasurement.java index 7a6889be3..62937f18e 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceMeasurement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/AdvanceMeasurement.java @@ -22,13 +22,16 @@ package org.navalplanner.business.advance.entities; import java.math.BigDecimal; import java.util.Date; +import java.util.HashSet; import java.util.Set; import org.hibernate.validator.AssertTrue; import org.hibernate.validator.NotNull; +import org.hibernate.validator.Valid; import org.joda.time.LocalDate; import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.orders.entities.OrderElement; +import org.navalplanner.business.planner.entities.consolidations.NonCalculatedConsolidatedValue; public class AdvanceMeasurement extends BaseEntity { @@ -53,6 +56,9 @@ public class AdvanceMeasurement extends BaseEntity { private Date communicationDate; + @Valid + private Set nonCalculatedConsolidatedValues = new HashSet(); + public AdvanceMeasurement() { } @@ -165,4 +171,13 @@ public class AdvanceMeasurement extends BaseEntity { } return false; } + + public void setNonCalculatedConsolidatedValues( + Set nonCalculatedConsolidatedValues) { + this.nonCalculatedConsolidatedValues = nonCalculatedConsolidatedValues; + } + + public Set getNonCalculatedConsolidatedValues() { + return nonCalculatedConsolidatedValues; + } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/DirectAdvanceAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/DirectAdvanceAssignment.java index eecd889b4..9980ce442 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/DirectAdvanceAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/DirectAdvanceAssignment.java @@ -22,7 +22,9 @@ package org.navalplanner.business.advance.entities; import java.math.BigDecimal; import java.math.RoundingMode; +import java.util.HashSet; import java.util.Iterator; +import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -31,6 +33,7 @@ import org.hibernate.validator.NotNull; import org.hibernate.validator.Valid; import org.joda.time.LocalDate; import org.navalplanner.business.orders.entities.OrderElement; +import org.navalplanner.business.planner.entities.consolidations.NonCalculatedConsolidation; /** * Represents an {@link AdvanceAssignment} that is own of this @@ -60,6 +63,9 @@ public class DirectAdvanceAssignment extends AdvanceAssignment { private SortedSet advanceMeasurements = new TreeSet( new AdvanceMeasurementComparator()); + @Valid + private Set nonCalculatedConsolidations = new HashSet(); + private boolean fake = false; public DirectAdvanceAssignment() { @@ -195,4 +201,13 @@ public class DirectAdvanceAssignment extends AdvanceAssignment { return true; } + public void setNonCalculatedConsolidation( + Set nonCalculatedConsolidation) { + this.nonCalculatedConsolidations = nonCalculatedConsolidation; + } + + public Set getNonCalculatedConsolidation() { + return nonCalculatedConsolidations; + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/IndirectAdvanceAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/IndirectAdvanceAssignment.java index 2d86aecaa..58ea998ee 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/IndirectAdvanceAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/advance/entities/IndirectAdvanceAssignment.java @@ -20,7 +20,12 @@ package org.navalplanner.business.advance.entities; +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.validator.Valid; import org.navalplanner.business.orders.entities.OrderLineGroup; +import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidation; /** * Represents an {@link AdvanceAssignment} that is defined in some of the @@ -30,6 +35,9 @@ import org.navalplanner.business.orders.entities.OrderLineGroup; */ public class IndirectAdvanceAssignment extends AdvanceAssignment { + @Valid + private Set calculatedConsolidations = new HashSet(); + public static IndirectAdvanceAssignment create() { IndirectAdvanceAssignment indirectAdvanceAssignment = new IndirectAdvanceAssignment(); indirectAdvanceAssignment.setNewObject(true); @@ -63,4 +71,13 @@ public class IndirectAdvanceAssignment extends AdvanceAssignment { super(reportGlobalAdvance); } + public void setCalculatedConsolidation( + Set calculatedConsolidations) { + this.calculatedConsolidations = calculatedConsolidations; + } + + public Set getCalculatedConsolidation() { + return calculatedConsolidations; + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java index d7d41479c..b1ca19605 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/BaseCalendar.java @@ -211,11 +211,6 @@ public class BaseCalendar extends IntegrationEntity implements IWorkHours { public void addExceptionDay(CalendarException day) throws IllegalArgumentException { - if (day.getDate().compareTo(new LocalDate()) <= 0) { - throw new IllegalArgumentException( - "You can not modify the past adding a new exception day"); - } - if (isExceptionDayAlreadyInExceptions(day)) { throw new IllegalArgumentException( "This day is already in the exception days"); @@ -230,11 +225,6 @@ public class BaseCalendar extends IntegrationEntity implements IWorkHours { public void removeExceptionDay(LocalDate date) throws IllegalArgumentException { - if (date.compareTo(new LocalDate()) <= 0) { - throw new IllegalArgumentException( - "You can not modify the past removing an exception day"); - } - CalendarException day = getOwnExceptionDay(date); if (day == null) { throw new IllegalArgumentException( @@ -440,11 +430,6 @@ public class BaseCalendar extends IntegrationEntity implements IWorkHours { * new calendar will be used from that date onwards. */ public void newVersion(LocalDate date) throws IllegalArgumentException { - if (date.compareTo(new LocalDate()) <= 0) { - throw new IllegalArgumentException( - "Date for new version must be greater than current date"); - } - CalendarData calendarData = getCalendarDataBeforeTheLastIfAny(); if ((calendarData.getExpiringDate() != null) && (date.compareTo(calendarData.getExpiringDate()) <= 0)) { @@ -650,11 +635,6 @@ public class BaseCalendar extends IntegrationEntity implements IWorkHours { + "because of this is the last version"); } - if (expiringDate.compareTo(new LocalDate()) <= 0) { - throw new IllegalArgumentException( - "This date must be greater than current date"); - } - Integer index = calendarDataVersions.indexOf(calendarData); if (index > 0) { CalendarData preivousCalendarData = calendarDataVersions @@ -707,12 +687,22 @@ public class BaseCalendar extends IntegrationEntity implements IWorkHours { return isLastVersion(new LocalDate(date)); } + public boolean isFirstVersion(Date date) { + return isFirstVersion(new LocalDate(date)); + } + public boolean isLastVersion(LocalDate date) { CalendarData calendarData = getCalendarData(date); Integer index = calendarDataVersions.indexOf(calendarData); return (index == (calendarDataVersions.size() - 1)); } + public boolean isFirstVersion(LocalDate date) { + CalendarData calendarData = getCalendarData(date); + Integer index = calendarDataVersions.indexOf(calendarData); + return (index == 0); + } + /** * Returns a set of non workable days (0 hours) for a specific period * depending on the calendar restrictions. @@ -759,13 +749,8 @@ public class BaseCalendar extends IntegrationEntity implements IWorkHours { throw new IllegalArgumentException( "You can not remove the current calendar data"); } - if (validFrom.compareTo(new LocalDate()) > 0) { - calendarDataVersions.remove(lastCalendarData); - getLastCalendarData().removeExpiringDate(); - } else { - throw new IllegalArgumentException( - "You can not modify the past removing a calendar data"); - } + calendarDataVersions.remove(lastCalendarData); + getLastCalendarData().removeExpiringDate(); } else { throw new IllegalArgumentException( "You just can remove the last calendar data"); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/CalendarExceptionType.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/CalendarExceptionType.java index f6cf68320..ed3ce782e 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/CalendarExceptionType.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/CalendarExceptionType.java @@ -37,6 +37,12 @@ public class CalendarExceptionType extends IntegrationEntity { return create(new CalendarExceptionType(name, color, notAssignable)); } + public static CalendarExceptionType create(String code, String name, + String color, Boolean notAssignable) { + return create(new CalendarExceptionType(name, color, notAssignable), + code); + } + private String name; private String color; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/PredefinedCalendarExceptionTypes.java b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/PredefinedCalendarExceptionTypes.java index bc1e59ca7..36375c261 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/PredefinedCalendarExceptionTypes.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/calendars/entities/PredefinedCalendarExceptionTypes.java @@ -37,7 +37,8 @@ public enum PredefinedCalendarExceptionTypes { private PredefinedCalendarExceptionTypes(String name, String color, Boolean notAssignable) { - calendarExceptionType = CalendarExceptionType.create(name, color, + // Using the name as code in order to be more human friendly + calendarExceptionType = CalendarExceptionType.create(name, name, color, notAssignable); } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java index 508e5ac23..62c6a9428 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/entities/Configuration.java @@ -55,6 +55,10 @@ public class Configuration extends BaseEntity { private Boolean generateCodeForUnitTypes = false; + private Boolean expandCompanyPlanningViewCharts = true; + + private Boolean expandOrderPlanningViewCharts = true; + public void setDefaultCalendar(BaseCalendar defaultCalendar) { this.defaultCalendar = defaultCalendar; } @@ -143,4 +147,22 @@ public class Configuration extends BaseEntity { return generateCodeForUnitTypes; } + public void setExpandCompanyPlanningViewCharts( + Boolean expandCompanyPlanningViewCharts) { + this.expandCompanyPlanningViewCharts = expandCompanyPlanningViewCharts; + } + + public Boolean isExpandCompanyPlanningViewCharts() { + return expandCompanyPlanningViewCharts; + } + + public void setExpandOrderPlanningViewCharts( + Boolean expandOrderPlanningViewCharts) { + this.expandOrderPlanningViewCharts = expandOrderPlanningViewCharts; + } + + public Boolean isExpandOrderPlanningViewCharts() { + return expandOrderPlanningViewCharts; + } + } \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ConsolidationDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ConsolidationDAO.java new file mode 100644 index 000000000..428533767 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ConsolidationDAO.java @@ -0,0 +1,38 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.daos; + +import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.planner.entities.consolidations.Consolidation; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Repository; + +/** + * DAO for {@Consolidation} + * @author Susana Montes Pedreira + */ +@Repository +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class ConsolidationDAO extends GenericDAOHibernate + implements IConsolidationDAO { + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IConsolidationDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IConsolidationDAO.java new file mode 100644 index 000000000..e554175ce --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/IConsolidationDAO.java @@ -0,0 +1,33 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.daos; + +import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.planner.entities.consolidations.Consolidation; + +/** + * DAO interface for {@link Consolidation} + * @author Susana Montes Pedreira + */ + +public interface IConsolidationDAO extends IGenericDAO { + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java new file mode 100644 index 000000000..d08cfd66d --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueDAO.java @@ -0,0 +1,34 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.daos; + +import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; + +/** + * DAO interface for {@link ILimitingResourceQueueDAO} + * + * @author Diego Pino García + */ +public interface ILimitingResourceQueueDAO extends + IGenericDAO { + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueElementDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueElementDAO.java new file mode 100644 index 000000000..cd856a097 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/ILimitingResourceQueueElementDAO.java @@ -0,0 +1,55 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.daos; + +import java.util.List; + +import org.navalplanner.business.common.daos.IGenericDAO; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; + +/** + * DAO interface for {@link ILimitingResourceQueueElementDAO} + * + * @author Diego Pino García + */ +public interface ILimitingResourceQueueElementDAO extends + IGenericDAO { + + List getAll(); + + /** + * Returns all {@link LimitingResourceQueueElement} that are assigned to a + * {@link LimitingResourceQueue} + * + * @return + */ + List getAssigned(); + + /** + * Returns all {@link LimitingResourceQueueElement} that have not been assigned to + * {@link LimitingResourceQueue} yet + * + * @return + */ + List getUnassigned(); + +} \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java new file mode 100644 index 000000000..f64eb94ef --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueDAO.java @@ -0,0 +1,40 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.daos; + +import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Repository; + +/** + * DAO for {@LimitingResourceQueueDAO} + * + * @author Diego Pino García + */ +@Repository +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class LimitingResourceQueueDAO extends + GenericDAOHibernate implements + ILimitingResourceQueueDAO { + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueElementDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueElementDAO.java new file mode 100644 index 000000000..1ec0d5d93 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/daos/LimitingResourceQueueElementDAO.java @@ -0,0 +1,68 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.daos; + +import java.util.List; + +import org.hibernate.Criteria; +import org.hibernate.criterion.Order; +import org.hibernate.criterion.Restrictions; +import org.navalplanner.business.common.daos.GenericDAOHibernate; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Repository; + +/** + * DAO for {@LimitingResourceQueueElementDAO} + * + * @author Diego Pino García + */ +@Repository +@Scope(BeanDefinition.SCOPE_SINGLETON) +public class LimitingResourceQueueElementDAO extends + GenericDAOHibernate implements + ILimitingResourceQueueElementDAO { + + @Override + public List getAll() { + return list(LimitingResourceQueueElement.class); + } + + @SuppressWarnings("unchecked") + @Override + public List getAssigned() { + Criteria criteria = getSession().createCriteria(LimitingResourceQueueElement.class); + criteria.add(Restrictions.isNotNull("limitingResourceQueue")); + criteria.addOrder(Order.asc("creationTimestamp")); + return criteria.list(); + } + + @SuppressWarnings("unchecked") + @Override + public List getUnassigned() { + Criteria criteria = getSession().createCriteria(LimitingResourceQueueElement.class); + criteria.add(Restrictions.isNull("limitingResourceQueue")); + criteria.addOrder(Order.asc("creationTimestamp")); + return criteria.list(); + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DayAssignment.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DayAssignment.java index 387af1030..e2e86792d 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DayAssignment.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/DayAssignment.java @@ -155,6 +155,8 @@ public abstract class DayAssignment extends BaseEntity { @OnCopy(Strategy.SHARE) private Resource resource; + private Boolean consolidated; + protected DayAssignment() { } @@ -180,6 +182,14 @@ public abstract class DayAssignment extends BaseEntity { return day; } + public void setConsolidated(Boolean consolidated) { + this.consolidated = consolidated; + } + + public Boolean isConsolidated() { + return consolidated == null ? false : consolidated; + } + public static Comparator byDayComparator() { return new Comparator() { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java index e79b41db8..f60349695 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/LimitingResourceQueueElement.java @@ -22,6 +22,7 @@ package org.navalplanner.business.planner.entities; import java.util.Date; +import org.joda.time.LocalDate; import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.resources.entities.LimitingResourceQueue; @@ -38,21 +39,18 @@ public class LimitingResourceQueueElement extends BaseEntity { private Date earlierStartDateBecauseOfGantt; - public Date getEarlierStartDateBecauseOfGantt() { - return earlierStartDateBecauseOfGantt; - } + private QueuePosition startQueuePosition; - public void setEarlierStartDateBecauseOfGantt( - Date earlierStartDateBecauseOfGantt) { - this.earlierStartDateBecauseOfGantt = earlierStartDateBecauseOfGantt; - } + private QueuePosition endQueuePosition; -public static LimitingResourceQueueElement create() { + private long creationTimestamp; + + public static LimitingResourceQueueElement create() { return create(new LimitingResourceQueueElement()); } protected LimitingResourceQueueElement() { - + creationTimestamp = (new Date()).getTime(); } public ResourceAllocation getResourceAllocation() { @@ -71,4 +69,53 @@ public static LimitingResourceQueueElement create() { this.limitingResourceQueue = limitingResourceQueue; } + public LocalDate getStartDate() { + return startQueuePosition.getDate(); + } + + public void setStartDate(LocalDate date) { + startQueuePosition.setDate(date); + } + + public int getStartHour() { + return startQueuePosition.getHour(); + } + + public void setStartHour(int hour) { + startQueuePosition.setHour(hour); + } + + public LocalDate getEndDate() { + return endQueuePosition.getDate(); + } + + public void setEndDate(LocalDate date) { + endQueuePosition.setDate(date); + } + + public int getEndHour() { + return endQueuePosition.getHour(); + } + + public void setEndHour(int hour) { + endQueuePosition.setHour(hour); + } + + public Date getEarlierStartDateBecauseOfGantt() { + return earlierStartDateBecauseOfGantt; + } + + public void setEarlierStartDateBecauseOfGantt( + Date earlierStartDateBecauseOfGantt) { + this.earlierStartDateBecauseOfGantt = earlierStartDateBecauseOfGantt; + } + + public long getCreationTimestamp() { + return creationTimestamp; + } + + public void setCreationTimestamp(long creationTimestamp) { + this.creationTimestamp = creationTimestamp; + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/QueuePosition.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/QueuePosition.java new file mode 100644 index 000000000..1ab54b032 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/QueuePosition.java @@ -0,0 +1,56 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities; + +import static org.navalplanner.business.i18n.I18nHelper._; + +import org.apache.commons.lang.Validate; +import org.joda.time.LocalDate; + +/** + * + * @author Diego Pino Garcia + * + */ +public class QueuePosition { + + private LocalDate date; + + private int hour = 0; + + public LocalDate getDate() { + return date; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public int getHour() { + return hour; + } + + public void setHour(int hour) { + Validate.isTrue(hour >= 0 && hour <= 23, _("Hour should be a value between 0 and 23")); + this.hour = hour; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java index 7ca822210..b2b0a4719 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/ResourceAllocation.java @@ -33,6 +33,7 @@ import java.util.Set; import java.util.Map.Entry; import org.apache.commons.lang.Validate; +import org.hibernate.validator.Min; import org.hibernate.validator.NotNull; import org.joda.time.LocalDate; import org.navalplanner.business.calendars.entities.AvailabilityTimeLine; @@ -300,9 +301,14 @@ public abstract class ResourceAllocation extends @OnCopy(Strategy.SHARE) private ResourcesPerDay resourcesPerDay; + private Integer intendedTotalHours; + private Set derivedAllocations = new HashSet(); - private LimitingResourceQueueElement limitingResourceQueueElement; + private Set limitingResourceQueueElements = new HashSet(); + + @Min(0) + private int originalTotalAssignment = 0; /** * Constructor for hibernate. Do not use! @@ -361,6 +367,14 @@ public abstract class ResourceAllocation extends return task; } + public void setOriginalTotalAssigment(int originalTotalAssigment) { + this.originalTotalAssignment = originalTotalAssigment; + } + + public int getOriginalTotalAssigment() { + return originalTotalAssignment; + } + public abstract ResourcesPerDayModification withDesiredResourcesPerDay( ResourcesPerDay resourcesPerDay); @@ -477,6 +491,7 @@ public abstract class ResourceAllocation extends removingAssignments(getAssignments(startInclusive, endExclusive)); addingAssignments(assignmentsCreated); setResourcesPerDay(calculateResourcesPerDayFromAssignments()); + setOriginalTotalAssigment(getAssignedHours()); } protected abstract AvailabilityTimeLine getResourcesAvailability(); @@ -557,6 +572,10 @@ public abstract class ResourceAllocation extends assert isUnsatisfied(); } + public boolean isLimiting() { + return getLimitingResourceQueueElement() != null; + } + public boolean isSatisfied() { return hasAssignments(); } @@ -568,6 +587,7 @@ public abstract class ResourceAllocation extends private void resetAssignmentsTo(List assignments) { removingAssignments(getAssignments()); addingAssignments(assignments); + setOriginalTotalAssigment(getAssignedHours()); } protected final void addingAssignments(Collection assignments) { @@ -665,6 +685,7 @@ public abstract class ResourceAllocation extends Validate.notNull(scenario); ResourceAllocation copy = createCopy(scenario); copy.resourcesPerDay = resourcesPerDay; + copy.originalTotalAssignment = originalTotalAssignment; copy.task = task; copy.assignmentFunction = assignmentFunction; return copy; @@ -943,6 +964,7 @@ public abstract class ResourceAllocation extends switchToScenario(scenario); mergeAssignments(modifications); setResourcesPerDay(modifications.getResourcesPerDay()); + setOriginalTotalAssigment(modifications.getOriginalTotalAssigment()); setWithoutApply(modifications.getAssignmentFunction()); mergeDerivedAllocations(scenario, modifications.getDerivedAllocations()); } @@ -989,12 +1011,21 @@ public abstract class ResourceAllocation extends } public LimitingResourceQueueElement getLimitingResourceQueueElement() { - return limitingResourceQueueElement; + return (!limitingResourceQueueElements.isEmpty()) ? (LimitingResourceQueueElement) limitingResourceQueueElements.iterator().next() : null; } - public void setLimitingResourceQueueElement( - LimitingResourceQueueElement limitingResourceQueueElement) { - this.limitingResourceQueueElement = limitingResourceQueueElement; + public void setLimitingResourceQueueElement(LimitingResourceQueueElement element) { + limitingResourceQueueElements.clear(); + element.setResourceAllocation(this); + limitingResourceQueueElements.add(element); + } + + public Integer getIntendedTotalHours() { + return intendedTotalHours; + } + + public void setIntendedTotalHours(Integer intendedTotalHours) { + this.intendedTotalHours = intendedTotalHours; } /** diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java index f5dad2853..21a2630de 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/SpecificResourceAllocation.java @@ -58,6 +58,13 @@ public class SpecificResourceAllocation extends task)); } + public static SpecificResourceAllocation create(Resource resource, Task task) { + SpecificResourceAllocation result = create(new SpecificResourceAllocation( + task)); + result.setResource(resource); + return result; + } + @NotNull @OnCopy(Strategy.SHARE) private Resource resource; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java index 4d2755973..fc74291e4 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/Task.java @@ -25,6 +25,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; @@ -43,6 +44,7 @@ import org.navalplanner.business.orders.entities.TaskSource; import org.navalplanner.business.planner.entities.DerivedAllocationGenerator.IWorkerFinder; import org.navalplanner.business.planner.entities.allocationalgorithms.HoursModification; import org.navalplanner.business.planner.entities.allocationalgorithms.ResourcesPerDayModification; +import org.navalplanner.business.planner.entities.consolidations.Consolidation; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.Resource; @@ -90,6 +92,9 @@ public class Task extends TaskElement { private Integer priority; + @Valid + private Consolidation consolidation; + /** * Constructor for hibernate. Do not use! */ @@ -122,6 +127,10 @@ public class Task extends TaskElement { .getTotalHours(); } + public int getTotalHours() { + return (getTaskSource() != null) ? getTaskSource().getTotalHours() : 0; + } + @Override public boolean isLeaf() { return true; @@ -133,10 +142,15 @@ public class Task extends TaskElement { } public Set> getSatisfiedResourceAllocations() { - List> filtered = ResourceAllocation - .getSatisfied(resourceAllocations); - return Collections.unmodifiableSet(new HashSet>( - filtered)); + Set> result = new HashSet>(); + + if (isLimiting()) { + result.addAll(getLimitingResourceAllocations()); + } else { + result.addAll(ResourceAllocation + .getSatisfied(resourceAllocations)); + } + return Collections.unmodifiableSet(result); } @Override @@ -144,19 +158,44 @@ public class Task extends TaskElement { return Collections.unmodifiableSet(resourceAllocations); } + public Set> getLimitingResourceAllocations() { + Set> result = new HashSet>(); + for (ResourceAllocation each: resourceAllocations) { + if (each.isLimiting()) { + result.add(each); + } + } + return Collections.unmodifiableSet(result); + } + + public Set> getNonLimitingResourceAllocations() { + Set> result = new HashSet>(); + for (ResourceAllocation each: resourceAllocations) { + if (!each.isLimiting()) { + result.add(each); + } + } + return Collections.unmodifiableSet(result); + } + public boolean isLimiting() { - // FIXME: Task is limiting if its resourceAllocation is associated with - // a LimitingResourceQueueElement - return false; + return !(getLimitingResourceAllocations().isEmpty()); } public void addResourceAllocation(ResourceAllocation resourceAllocation) { + addResourceAllocation(resourceAllocation, true); + } + + public void addResourceAllocation(ResourceAllocation resourceAllocation, + boolean generateDayAssignments) { if (!resourceAllocation.getTask().equals(this)) { throw new IllegalArgumentException( "the resourceAllocation's task must be this task"); } resourceAllocations.add(resourceAllocation); - resourceAllocation.associateAssignmentsToResource(); + if (generateDayAssignments) { + resourceAllocation.associateAssignmentsToResource(); + } } public void removeResourceAllocation( @@ -395,6 +434,14 @@ public class Task extends TaskElement { private void reassign(Scenario onScenario, AllocationModificationStrategy strategy) { + if (isLimiting()) { + Set> resourceAllocations = getSatisfiedResourceAllocations(); + ResourceAllocation resourceAlloation = resourceAllocations + .iterator().next(); + resourceAlloation.getLimitingResourceQueueElement() + .setEarlierStartDateBecauseOfGantt(getStartDate()); + return; + } List copied = ModifiedAllocation.copy(onScenario, getSatisfiedResourceAllocations()); List> toBeModified = ModifiedAllocation @@ -486,13 +533,21 @@ public class Task extends TaskElement { return subcontractedTaskData; } - public void removeAllResourceAllocations() { + public void removeAllSatisfiedResourceAllocations() { Set> resourceAllocations = getSatisfiedResourceAllocations(); for (ResourceAllocation resourceAllocation : resourceAllocations) { removeResourceAllocation(resourceAllocation); } } + public void removeAllResourceAllocations() { + for (Iterator> i = resourceAllocations.iterator(); i + .hasNext();) { + ResourceAllocation each = i.next(); + removeResourceAllocation(each); + } + } + public boolean isSubcontracted() { return (subcontractedTaskData != null); } @@ -534,4 +589,12 @@ public class Task extends TaskElement { this.priority = priority; } + public void setConsolidation(Consolidation consolidation) { + this.consolidation = consolidation; + } + + public Consolidation getConsolidation() { + return consolidation; + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java index c9aa4a623..147a50ac1 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/TaskElement.java @@ -438,7 +438,8 @@ public abstract class TaskElement extends BaseEntity { return "unassigned"; } for (ResourceAllocation resourceAllocation : getSatisfiedResourceAllocations()) { - if (resourceAllocation.getResourcesPerDay().isZero()) { + final ResourcesPerDay resourcesPerDay = resourceAllocation.getResourcesPerDay(); + if (resourcesPerDay != null && resourcesPerDay.isZero()) { return "partially-assigned"; } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidatedValue.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidatedValue.java new file mode 100644 index 000000000..d23e54224 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidatedValue.java @@ -0,0 +1,69 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities.consolidations; + +import java.math.BigDecimal; +import java.util.Set; + +import org.joda.time.LocalDate; + +/** + * @author Susana Montes Pedreira + */ + +public class CalculatedConsolidatedValue extends ConsolidatedValue { + + private CalculatedConsolidation consolidation; + + public static CalculatedConsolidatedValue create() { + return create(new CalculatedConsolidatedValue()); + } + + public static CalculatedConsolidatedValue create(LocalDate date, + BigDecimal value, + Set pendingConsolidatedHours) { + return create(new CalculatedConsolidatedValue(date, value, + pendingConsolidatedHours)); + } + + protected CalculatedConsolidatedValue( + LocalDate date, + BigDecimal value, + Set pendingConsolidatedHours) { + super(date, value, pendingConsolidatedHours); + } + + protected CalculatedConsolidatedValue() { + } + + public void setConsolidation(CalculatedConsolidation consolidation) { + this.consolidation = consolidation; + } + + public CalculatedConsolidation getConsolidation() { + return consolidation; + } + + @Override + public boolean isCalculated() { + return true; + } +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidation.java new file mode 100644 index 000000000..f3992ed9b --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/CalculatedConsolidation.java @@ -0,0 +1,106 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities.consolidations; + +import java.util.SortedSet; +import java.util.TreeSet; + +import org.navalplanner.business.advance.entities.IndirectAdvanceAssignment; +import org.navalplanner.business.planner.entities.Task; + +/** + * @author Susana Montes Pedreira + */ + +public class CalculatedConsolidation extends Consolidation { + + private SortedSet consolidatedValues = new TreeSet( + new ConsolidatedValueComparator()); + + private IndirectAdvanceAssignment indirectAdvanceAssignment; + + public static CalculatedConsolidation create(Task task, + IndirectAdvanceAssignment indirectAdvanceAssignment) { + return create(new CalculatedConsolidation(task, + indirectAdvanceAssignment)); + } + + public static CalculatedConsolidation create(Task task, + IndirectAdvanceAssignment indirectAdvanceAssignment, + SortedSet consolidatedValues) { + return create(new CalculatedConsolidation(task, + indirectAdvanceAssignment, + consolidatedValues)); + } + + protected CalculatedConsolidation() { + + } + + protected CalculatedConsolidation(Task task, + IndirectAdvanceAssignment indirectAdvanceAssignment, + SortedSet consolidatedValues) { + this(task, indirectAdvanceAssignment); + this.setConsolidatedValues(consolidatedValues); + } + + public CalculatedConsolidation(Task task, + IndirectAdvanceAssignment indirectAdvanceAssignment) { + super(task); + this.indirectAdvanceAssignment = indirectAdvanceAssignment; + } + + @Override + public SortedSet getConsolidatedValues() { + return new TreeSet(consolidatedValues); + } + + public SortedSet getCalculatedConsolidatedValues() { + return consolidatedValues; + } + + public void setConsolidatedValues( + SortedSet consolidatedValues) { + this.consolidatedValues = consolidatedValues; + } + + public void setIndirectAdvanceAssignment( + IndirectAdvanceAssignment indirectAdvanceAssignment) { + this.indirectAdvanceAssignment = indirectAdvanceAssignment; + } + + public IndirectAdvanceAssignment getIndirectAdvanceAssignment() { + return indirectAdvanceAssignment; + } + + public void addConsolidatedValue(CalculatedConsolidatedValue value) { + if (!consolidatedValues.contains(value)) { + value.setConsolidation(this); + this.consolidatedValues.add(value); + } + } + + @Override + public boolean isCalculated() { + return true; + } + +} \ No newline at end of file diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValue.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValue.java new file mode 100644 index 000000000..f1d05770e --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValue.java @@ -0,0 +1,93 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities.consolidations; + +import java.math.BigDecimal; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.planner.entities.ResourceAllocation; + +/** + * @author Susana Montes Pedreira + */ + +public abstract class ConsolidatedValue extends BaseEntity { + + private LocalDate date; + private BigDecimal value; + private Set pendingConsolidatedHours = new HashSet(); + + public abstract boolean isCalculated(); + + protected ConsolidatedValue() { + + } + + protected ConsolidatedValue( + LocalDate date, + BigDecimal value, + Set pendingConsolidatedHours) { + this.date = date; + this.value = value; + this.pendingConsolidatedHours = pendingConsolidatedHours; + } + + public void setValue(BigDecimal value) { + this.value = value; + } + + public BigDecimal getValue() { + return value; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public LocalDate getDate() { + return date; + } + + public void setPendingConsolidatedHours(Set pendingConsolidatedHours) { + this.pendingConsolidatedHours = pendingConsolidatedHours; + } + + public Set getPendingConsolidatedHours() { + return pendingConsolidatedHours; + } + + public static Set createPendingConsolidatedHours( + LocalDate consolidatedDate, + Collection allocations) { + Set pendingConsolidatedHours = new HashSet(); + for (ResourceAllocation allocation : allocations) { + pendingConsolidatedHours + .add(PendingConsolidatedHoursPerResourceAllocation.create( + consolidatedDate, allocation)); + } + return pendingConsolidatedHours; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValueComparator.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValueComparator.java new file mode 100644 index 000000000..f3fd4e057 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/ConsolidatedValueComparator.java @@ -0,0 +1,47 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities.consolidations; + +import java.util.Comparator; + +/** + * @author Susana Montes Pedreira + */ +public class ConsolidatedValueComparator implements + Comparator { + + public ConsolidatedValueComparator() { + } + + @Override + public int compare(ConsolidatedValue arg0, ConsolidatedValue arg1) { + if (arg1.getDate() == arg0.getDate()) { + return 0; + } + if (arg1.getDate() == null) { + return -1; + } + if (arg0.getDate() == null) { + return 1; + } + return arg0.getDate().compareTo(arg1.getDate()); + } +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/Consolidation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/Consolidation.java new file mode 100644 index 000000000..03c822d34 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/Consolidation.java @@ -0,0 +1,55 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities.consolidations; + +import java.util.SortedSet; + +import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.planner.entities.Task; + +/** + * @author Susana Montes Pedreira + */ +public abstract class Consolidation extends BaseEntity { + + public abstract SortedSet getConsolidatedValues(); + + public abstract boolean isCalculated(); + + private Task task; + + protected Consolidation() { + + } + + protected Consolidation(Task task) { + this.task = task; + } + + public void setTask(Task task) { + this.task = task; + } + + public Task getTask() { + return task; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidatedValue.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidatedValue.java new file mode 100644 index 000000000..bc03cd5fc --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidatedValue.java @@ -0,0 +1,95 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities.consolidations; + +import java.math.BigDecimal; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.navalplanner.business.advance.entities.AdvanceMeasurement; + +/** + * @author Susana Montes Pedreira + */ + +public class NonCalculatedConsolidatedValue extends ConsolidatedValue { + + private NonCalculatedConsolidation consolidation; + + private AdvanceMeasurement advanceMeasurement; + + public static NonCalculatedConsolidatedValue create() { + return create(new NonCalculatedConsolidatedValue()); + } + + public static NonCalculatedConsolidatedValue create(LocalDate date, + BigDecimal value, + Set pendingConsolidatedHours) { + return create(new NonCalculatedConsolidatedValue(date, value, + pendingConsolidatedHours)); + } + + public static NonCalculatedConsolidatedValue create(LocalDate date, + BigDecimal value, + AdvanceMeasurement advanceMeasurement, + Set pendingConsolidatedHours) { + return create(new NonCalculatedConsolidatedValue(date, value, + advanceMeasurement, pendingConsolidatedHours)); + } + + protected NonCalculatedConsolidatedValue(LocalDate date, BigDecimal value, + AdvanceMeasurement advanceMeasurement, + Set pendingConsolidatedHours) { + this(date, value, pendingConsolidatedHours); + this.advanceMeasurement = advanceMeasurement; + } + + protected NonCalculatedConsolidatedValue( + LocalDate date, + BigDecimal value, + Set pendingConsolidatedHours) { + super(date, value, pendingConsolidatedHours); + } + + protected NonCalculatedConsolidatedValue() { + } + + public void setAdvanceMeasurement(AdvanceMeasurement advanceMeasurement) { + this.advanceMeasurement = advanceMeasurement; + } + + public AdvanceMeasurement getAdvanceMeasurement() { + return advanceMeasurement; + } + + public void setConsolidation(NonCalculatedConsolidation consolidation) { + this.consolidation = consolidation; + } + + public NonCalculatedConsolidation getConsolidation() { + return consolidation; + } + + @Override + public boolean isCalculated() { + return false; + } +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidation.java new file mode 100644 index 000000000..c143d6b3e --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/NonCalculatedConsolidation.java @@ -0,0 +1,107 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities.consolidations; + +import java.util.SortedSet; +import java.util.TreeSet; + +import org.navalplanner.business.advance.entities.DirectAdvanceAssignment; +import org.navalplanner.business.planner.entities.Task; + + +/** + * @author Susana Montes Pedreira + */ + +public class NonCalculatedConsolidation extends Consolidation { + + private SortedSet consolidatedValues = new TreeSet( + new ConsolidatedValueComparator()); + + private DirectAdvanceAssignment directAdvanceAssignment; + + public static NonCalculatedConsolidation create(Task task, + DirectAdvanceAssignment directAdvanceAssignment) { + return create(new NonCalculatedConsolidation(task, + directAdvanceAssignment)); + } + + public static NonCalculatedConsolidation create(Task task, + DirectAdvanceAssignment directAdvanceAssignment, + SortedSet consolidatedValues) { + return create(new NonCalculatedConsolidation(task, + directAdvanceAssignment, + consolidatedValues)); + } + + protected NonCalculatedConsolidation() { + + } + + protected NonCalculatedConsolidation(Task task, + DirectAdvanceAssignment directAdvanceAssignment, + SortedSet consolidatedValues) { + this(task, directAdvanceAssignment); + this.setConsolidatedValues(consolidatedValues); + } + + public NonCalculatedConsolidation(Task task, + DirectAdvanceAssignment directAdvanceAssignment) { + super(task); + this.directAdvanceAssignment = directAdvanceAssignment; + } + + @Override + public SortedSet getConsolidatedValues() { + return new TreeSet(consolidatedValues); + } + + public SortedSet getNonCalculatedConsolidatedValues() { + return consolidatedValues; + } + + public void setConsolidatedValues( + SortedSet consolidatedValues) { + this.consolidatedValues = consolidatedValues; + } + + public void setDirectAdvanceAssignment( + DirectAdvanceAssignment directAdvanceAssignment) { + this.directAdvanceAssignment = directAdvanceAssignment; + } + + public DirectAdvanceAssignment getDirectAdvanceAssignment() { + return directAdvanceAssignment; + } + + public void addConsolidatedValue(NonCalculatedConsolidatedValue value) { + if (!consolidatedValues.contains(value)) { + value.setConsolidation(this); + this.consolidatedValues.add(value); + } + } + + @Override + public boolean isCalculated() { + return false; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/PendingConsolidatedHoursPerResourceAllocation.java b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/PendingConsolidatedHoursPerResourceAllocation.java new file mode 100644 index 000000000..bf2b3d81b --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/planner/entities/consolidations/PendingConsolidatedHoursPerResourceAllocation.java @@ -0,0 +1,109 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.business.planner.entities.consolidations; + +import java.util.Collection; + +import org.hibernate.validator.NotNull; +import org.joda.time.LocalDate; +import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.ResourceAllocation; + +/** + * Represents the number of hours per {@link ResourceAllocation} that are not + * consolidated. + * @author Susana Montes Pedreira + */ + +public class PendingConsolidatedHoursPerResourceAllocation extends BaseEntity { + + private Integer pendingConsolidatedHours; + + private ResourceAllocation resourceAllocation; + + public static PendingConsolidatedHoursPerResourceAllocation create( + LocalDate consolidatedDate, + ResourceAllocation resourceAllocation) { + return create(new PendingConsolidatedHoursPerResourceAllocation( + consolidatedDate, + resourceAllocation)); + } + + public static PendingConsolidatedHoursPerResourceAllocation create( + Integer pendingConsolidatedHours, + ResourceAllocation resourceAllocation) { + return create(new PendingConsolidatedHoursPerResourceAllocation( + pendingConsolidatedHours, resourceAllocation)); + } + + protected PendingConsolidatedHoursPerResourceAllocation( + LocalDate consolidatedDate, + ResourceAllocation resourceAllocation) { + this.setPendingConsolidatedHours(calculatePendingConsolidatedHours( + consolidatedDate, resourceAllocation + .getAssignments())); + this.setResourceAllocation(resourceAllocation); + } + + protected PendingConsolidatedHoursPerResourceAllocation( + Integer pendingConsolidatedHours, + ResourceAllocation resourceAllocation) { + this.setPendingConsolidatedHours(pendingConsolidatedHours); + this.setResourceAllocation(resourceAllocation); + } + + protected PendingConsolidatedHoursPerResourceAllocation() { + + } + + private Integer calculatePendingConsolidatedHours(LocalDate consolidatedDate, + Collection assignments) { + int result = 0; + for (DayAssignment dayAssignment : assignments) { + if ((dayAssignment.getDay().toDateTimeAtStartOfDay() + .compareTo(consolidatedDate.toDateTimeAtStartOfDay())) > 0) { + dayAssignment.setConsolidated(true); + result += dayAssignment.getHours(); + } + } + return new Integer(result); + } + + public void setPendingConsolidatedHours(Integer pendingConsolidatedHours) { + this.pendingConsolidatedHours = pendingConsolidatedHours; + } + + @NotNull(message = "pending consolidated hours not specified") + public Integer getPendingConsolidatedHours() { + return pendingConsolidatedHours; + } + + public void setResourceAllocation(ResourceAllocation resourceAllocation) { + this.resourceAllocation = resourceAllocation; + } + + @NotNull(message = "resource allocation not specified") + public ResourceAllocation getResourceAllocation() { + return resourceAllocation; + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ResourceDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ResourceDAO.java index 1b3c02ec4..e912f7a59 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ResourceDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ResourceDAO.java @@ -157,8 +157,10 @@ public class ResourceDAO extends IntegrationEntityDAO implements private List findRelatedToSpecific(List taskElements) { List list = getSession() .createQuery( - "SELECT DISTINCT specificAllocation.resource FROM SpecificResourceAllocation specificAllocation " - + " WHERE specificAllocation.task IN(:taskElements)") + "SELECT DISTINCT specificAllocation.resource " + + "FROM SpecificResourceAllocation specificAllocation " + + "WHERE specificAllocation.task IN(:taskElements) " + + "and specificAllocation.specificDaysAssignment IS NOT EMPTY") .setParameterList( "taskElements", taskElements).list(); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java index 8b0b220b9..d241bf0dd 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Criterion.java @@ -84,6 +84,26 @@ public class Criterion extends IntegrationEntity implements ICriterion { return result; } + public static List sortByTypeAndName(Collection criterions) { + List result = new ArrayList(criterions); + Collections.sort(result, new Comparator() { + + @Override + public int compare(Criterion o1, Criterion o2) { + if (o1.getName() == null || o1.getType().getName() == null) { + return 1; + } + if (o2.getName() == null || o2.getType().getName() == null) { + return -1; + } + String name1 = o1.getType().getName() + " " + o1.getName(); + String name2 = o2.getType().getName() + " " + o2.getName(); + return name1.compareTo(name2); + } + }); + return result; + } + public void updateUnvalidated(String name, Boolean active) { if (!StringUtils.isBlank(name)) { diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueue.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueue.java index 33a9857cd..7d8843323 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueue.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueue.java @@ -21,8 +21,8 @@ package org.navalplanner.business.resources.entities; import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; import org.navalplanner.business.common.BaseEntity; import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; @@ -36,8 +36,8 @@ public class LimitingResourceQueue extends BaseEntity { private Resource resource; - private Set limitingResourceQueueElements = - new HashSet(); + private SortedSet limitingResourceQueueElements = + new TreeSet(new LimitingResourceQueueElementComparator()); public static LimitingResourceQueue create() { return create(new LimitingResourceQueue()); @@ -64,8 +64,8 @@ public class LimitingResourceQueue extends BaseEntity { limitingResourceQueueElements.remove(element); } - public Set getLimitingResourceQueueElements() { - return Collections.unmodifiableSet(limitingResourceQueueElements); + public SortedSet getLimitingResourceQueueElements() { + return Collections.unmodifiableSortedSet(limitingResourceQueueElements); } } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java new file mode 100644 index 000000000..fdc3a4332 --- /dev/null +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/LimitingResourceQueueElementComparator.java @@ -0,0 +1,50 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.navalplanner.business.resources.entities; + +import java.util.Comparator; + +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; + +/** + * + * @author Diego Pino Garcia + * + */ +public class LimitingResourceQueueElementComparator implements + Comparator { + + @Override + public int compare(LimitingResourceQueueElement arg0, + LimitingResourceQueueElement arg1) { + final int deltaHour = arg0.getStartHour() - arg1.getStartHour(); + if (deltaHour != 0) { + return deltaHour / Math.abs(deltaHour); + } + if (arg0.getStartDate() == null) { + return -1; + } + if (arg1.getStartDate() == null) { + return 1; + } + return arg0.getStartDate().compareTo(arg1.getStartDate()); + } + +} diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java index bab974938..ddc5403ea 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java @@ -122,7 +122,7 @@ public class Worker extends Resource { } public String getName() { - return firstName + " " + surname; + return getSurname() + ", " + getFirstName(); } @NotEmpty(message="worker's NIF not specified") diff --git a/navalplanner-business/src/main/resources/navalplanner-business-spring-config.xml b/navalplanner-business/src/main/resources/navalplanner-business-spring-config.xml index a1d12b551..8970e7655 100644 --- a/navalplanner-business/src/main/resources/navalplanner-business-spring-config.xml +++ b/navalplanner-business/src/main/resources/navalplanner-business-spring-config.xml @@ -76,6 +76,9 @@ org/navalplanner/business/externalcompanies/entities/ExternalCompanies.hbm.xml + + org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml + org/navalplanner/business/scenarios/entities/Scenarios.hbm.xml diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/advance/entities/Advance.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/advance/entities/Advance.hbm.xml index ce82461b8..be7423354 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/advance/entities/Advance.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/advance/entities/Advance.hbm.xml @@ -50,12 +50,30 @@ + + + + + + + + + + + + @@ -73,6 +91,12 @@ + + + + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml index 865e5f57e..0fb93f284 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/common/entities/Configuration.hbm.xml @@ -24,6 +24,8 @@ + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml new file mode 100644 index 000000000..b8cf8d21e --- /dev/null +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml @@ -0,0 +1,81 @@ + + + + + + + + 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + task + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml index 713110491..7f76e6d61 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/ResourceAllocations.hbm.xml @@ -14,6 +14,10 @@ + + + + - + + + + @@ -67,8 +74,8 @@ natural-id could be used for that but was discarded due to: https://forum.hibernate.org/viewtopic.php?p=2372348 --> - - + + @@ -87,8 +94,8 @@ natural-id could be used for that but was discarded due to: https://forum.hibernate.org/viewtopic.php?p=2372348 --> - - + + @@ -115,6 +122,7 @@ + @@ -129,6 +137,18 @@ + + + + + + + + + + + + @@ -144,6 +164,8 @@ + + - + - + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml index fadfe0827..b9054c087 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/planner/entities/Tasks.hbm.xml @@ -47,6 +47,10 @@ + + + diff --git a/navalplanner-business/src/main/resources/org/navalplanner/business/resources/entities/Resources.hbm.xml b/navalplanner-business/src/main/resources/org/navalplanner/business/resources/entities/Resources.hbm.xml index 39ec60487..00f81e824 100644 --- a/navalplanner-business/src/main/resources/org/navalplanner/business/resources/entities/Resources.hbm.xml +++ b/navalplanner-business/src/main/resources/org/navalplanner/business/resources/entities/Resources.hbm.xml @@ -78,7 +78,8 @@ - + diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java index 17d03c127..851c86314 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/calendars/entities/BaseCalendarTest.java @@ -26,7 +26,6 @@ import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.util.Set; @@ -608,8 +607,8 @@ public class BaseCalendarTest { assertThat(calendar.getOwnExceptions().size(), equalTo(2)); } - @Test(expected = IllegalArgumentException.class) - public void testNotAllowCreateExceptionsInThePast() { + @Test + public void testAllowCreateExceptionsInThePast() { BaseCalendar calendar = createBasicCalendar(); LocalDate pastMonth = (new LocalDate()).minusMonths(1); @@ -619,28 +618,26 @@ public class BaseCalendarTest { calendar.addExceptionDay(exceptionDay); } - @Test(expected = IllegalArgumentException.class) - public void testNotAllowRemoveExceptionsInThePast() { + @Test + public void testAllowRemoveExceptionsInThePast() { BaseCalendar calendar = createBasicCalendar(); LocalDate pastMonth = (new LocalDate()).minusMonths(1); + CalendarException exceptionDay = CalendarException.create(pastMonth, 0, + createCalendarExceptionType()); + calendar.addExceptionDay(exceptionDay); calendar.removeExceptionDay(pastMonth); } @Test - public void testNotAllowSetExpiringDateInThePast() { + public void testAllowSetExpiringDateInThePast() { BaseCalendar calendar = createBasicCalendar(); calendar.newVersion((new LocalDate()).plusDays(1)); LocalDate pastWeek = (new LocalDate()).minusWeeks(1); - try { - calendar.setExpiringDate(pastWeek); - fail("It should throw an exception"); - } catch (IllegalArgumentException e) { - - } + calendar.setExpiringDate(pastWeek); } @Test @@ -663,8 +660,8 @@ public class BaseCalendarTest { nullValue()); } - @Test(expected = IllegalArgumentException.class) - public void testNotAllowNewVersionOnCurrentDate() { + @Test + public void testAllowNewVersionOnCurrentDate() { BaseCalendar calendar = createBasicCalendar(); calendar.newVersion(new LocalDate()); @@ -697,8 +694,8 @@ public class BaseCalendarTest { equalTo(currentDate.plusWeeks(2))); } - @Test(expected = IllegalArgumentException.class) - public void testNotAllowSetValidFromInThePast() { + @Test + public void testAllowSetValidFromInThePast() { BaseCalendar calendar = createBasicCalendar(); LocalDate currentDate = new LocalDate(); diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/LimitingResourceQueueElementDAOTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/LimitingResourceQueueElementDAOTest.java new file mode 100644 index 000000000..f4dd6cb30 --- /dev/null +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/daos/LimitingResourceQueueElementDAOTest.java @@ -0,0 +1,112 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.navalplanner.business.test.planner.daos; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; +import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; + +import java.util.List; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.planner.daos.ILimitingResourceQueueDAO; +import org.navalplanner.business.planner.daos.ILimitingResourceQueueElementDAO; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE, + BUSINESS_SPRING_CONFIG_TEST_FILE }) +/** + * + * @author Diego Pino Garcia + * + */ +@Transactional +public class LimitingResourceQueueElementDAOTest { + + @Autowired + private ILimitingResourceQueueDAO limitingResourceQueueDAO; + + @Autowired + private ILimitingResourceQueueElementDAO limitingResourceQueueElementDAO; + + @Test + public void testInSpringContainer() { + assertNotNull(limitingResourceQueueDAO); + } + + private LimitingResourceQueueElement createValidLimitingResourceQueueElement() { + return LimitingResourceQueueElement.create(); + } + + private LimitingResourceQueue createValidLimitingResourceQueue() { + return LimitingResourceQueue.create(); + } + + @Test + public void testSaveLimitingResourceQueue() { + LimitingResourceQueue limitingResourceQueue = createValidLimitingResourceQueue(); + limitingResourceQueueDAO.save(limitingResourceQueue); + assertTrue(limitingResourceQueueDAO.exists(limitingResourceQueue.getId())); + } + + @Test + public void testRemoveLimitingResourceQueue() throws InstanceNotFoundException { + LimitingResourceQueue limitingResourceQueue = createValidLimitingResourceQueue(); + limitingResourceQueueDAO.save(limitingResourceQueue); + limitingResourceQueueDAO.remove(limitingResourceQueue.getId()); + assertFalse(limitingResourceQueueDAO.exists(limitingResourceQueue.getId())); + } + + @Test + public void testListLimitingResourceQueue() { + int previous = limitingResourceQueueDAO.list(LimitingResourceQueue.class).size(); + + LimitingResourceQueue limitingResourceQueue1 = createValidLimitingResourceQueue(); + limitingResourceQueueDAO.save(limitingResourceQueue1); + LimitingResourceQueue limitingResourceQueue2 = createValidLimitingResourceQueue(); + limitingResourceQueueDAO.save(limitingResourceQueue1); + limitingResourceQueueDAO.save(limitingResourceQueue2); + + List list = limitingResourceQueueDAO + .list(LimitingResourceQueue.class); + assertEquals(previous + 2, list.size()); + } + + @Test + public void testLimitingResourceQueueHasElements() { + LimitingResourceQueueElement element = createValidLimitingResourceQueueElement(); + LimitingResourceQueue queue = createValidLimitingResourceQueue(); + queue.addLimitingResourceQueueElement(element); + limitingResourceQueueDAO.save(queue); + assertTrue(!limitingResourceQueueElementDAO.getAssigned().isEmpty()); + } + +} diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java index 741db39ec..f92f44923 100644 --- a/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java +++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/planner/entities/TaskTest.java @@ -28,27 +28,43 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE; +import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; import java.util.Arrays; import java.util.Date; import org.joda.time.DateTime; import org.junit.Test; +import org.junit.runner.RunWith; import org.navalplanner.business.orders.entities.HoursGroup; import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.orders.entities.OrderLine; import org.navalplanner.business.orders.entities.SchedulingDataForVersion; import org.navalplanner.business.orders.entities.TaskSource; +import org.navalplanner.business.planner.daos.ITaskElementDAO; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; import org.navalplanner.business.planner.entities.SpecificResourceAllocation; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.scenarios.entities.OrderVersion; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Transactional; /** * @author Óscar González Fernández */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { BUSINESS_SPRING_CONFIG_FILE, + BUSINESS_SPRING_CONFIG_TEST_FILE }) +@Transactional public class TaskTest { + @Autowired + private ITaskElementDAO taskElementDAO; + private static final OrderVersion mockedOrderVersion = mockOrderVersion(); public static final OrderVersion mockOrderVersion() { @@ -58,6 +74,7 @@ public class TaskTest { } private Task task; + private HoursGroup hoursGroup; public TaskTest() { @@ -165,6 +182,11 @@ public class TaskTest { assertThat(task.getDaysDuration(), equalTo(1)); } + /** + * @param task + * @param hours + * @return + */ private SpecificResourceAllocation stubResourceAllocationWithAssignedHours( Task task, int hours) { @@ -180,4 +202,26 @@ public class TaskTest { return resourceAllocation; } + @Test + public void testIsLimiting() { + LimitingResourceQueueElement element = LimitingResourceQueueElement.create(); + Task task = createValidTask(); + SpecificResourceAllocation resourceAllocation = SpecificResourceAllocation.create(task); + resourceAllocation.setLimitingResourceQueueElement(element); + task.addResourceAllocation(resourceAllocation); + taskElementDAO.save(task); + + assertTrue(task.getLimitingResourceAllocations().size() == 1); + } + + @Test + public void testIsNonLimiting() { + Task task = createValidTask(); + SpecificResourceAllocation resourceAllocation = SpecificResourceAllocation.create(task); + task.addResourceAllocation(resourceAllocation); + taskElementDAO.save(task); + + assertTrue(task.getNonLimitingResourceAllocations().size() == 1); + } + } \ No newline at end of file diff --git a/navalplanner-business/src/test/resources/navalplanner-business-spring-config-test.xml b/navalplanner-business/src/test/resources/navalplanner-business-spring-config-test.xml index cd74ea9e7..109c94173 100644 --- a/navalplanner-business/src/test/resources/navalplanner-business-spring-config-test.xml +++ b/navalplanner-business/src/test/resources/navalplanner-business-spring-config-test.xml @@ -84,6 +84,9 @@ org/navalplanner/business/externalcompanies/entities/ExternalCompanies.hbm.xml + + org/navalplanner/business/planner/entities/AdvanceConsolidations.hbm.xml + org/navalplanner/business/scenarios/entities/Scenarios.hbm.xml diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarEditionController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarEditionController.java index 1d99772b9..0bf208c4f 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarEditionController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarEditionController.java @@ -196,19 +196,6 @@ public abstract class BaseCalendarEditionController extends return baseCalendarModel.isDerived(); } - public boolean isDateValidFromPast() { - if (!isEditing()) { - return false; - } - - Date dateValidFrom = baseCalendarModel.getDateValidFrom(); - if (dateValidFrom != null) { - return isPast(dateValidFrom); - } - - return true; - } - private boolean isPast(Date date) { LocalDate localDate = new LocalDate(date); LocalDate currentLocalDate = new LocalDate(); @@ -262,9 +249,7 @@ public abstract class BaseCalendarEditionController extends }); - if (isDateValidFromPast()) { - hoursIntbox.setDisabled(true); - } else if (baseCalendarModel.isDerived() + if (baseCalendarModel.isDerived() && baseCalendarModel.isDefault(day)) { hoursIntbox.setDisabled(true); } @@ -302,10 +287,6 @@ public abstract class BaseCalendarEditionController extends }); - if (isDateValidFromPast()) { - defaultCheckbox.setDisabled(true); - } - defaultListcell.appendChild(defaultCheckbox); item.appendChild(defaultListcell); } @@ -474,19 +455,6 @@ public abstract class BaseCalendarEditionController extends return baseCalendarModel.getHoursOfDay(); } - public boolean isSelectedDateFromPast() { - Date selectedDay = baseCalendarModel.getSelectedDay(); - if (selectedDay != null) { - return isPast(selectedDay); - } - - return true; - } - - public boolean isNotSelectedDateFromPast() { - return !isSelectedDateFromPast(); - } - public void createException() { Combobox exceptionTypes = (Combobox) window.getFellow("exceptionTypes"); CalendarExceptionType type = (CalendarExceptionType) exceptionTypes @@ -515,13 +483,6 @@ public abstract class BaseCalendarEditionController extends } else { Clients.closeErrorBox(dateboxEndDate); } - if (startDate.compareTo(new Date()) <= 0) { - throw new WrongValueException( - dateboxStartDate, - _("Exception start date should be greater than current date")); - } else { - Clients.closeErrorBox(dateboxStartDate); - } if (startDate.compareTo(endDate) > 0) { throw new WrongValueException( dateboxEndDate, @@ -694,9 +655,7 @@ public abstract class BaseCalendarEditionController extends LocalDate validFrom = baseCalendarModel.getValidFrom(calendarData); if ((validFrom == null) || (!baseCalendarModel.getLastCalendarData().equals( - calendarData)) - || (validFrom.compareTo( - new LocalDate()) <= 0)) { + calendarData))) { result.setDisabled(true); } return result; @@ -743,6 +702,10 @@ public abstract class BaseCalendarEditionController extends return baseCalendarModel.isLastVersion(); } + public boolean isFirstVersion() { + return baseCalendarModel.isFirstVersion(); + } + public void goToDate(Date date) { setSelectedDay(date); @@ -903,9 +866,6 @@ public abstract class BaseCalendarEditionController extends reloadDayInformation(); } }); - if (isSelectedDateFromPast()) { - result.setDisabled(true); - } return result; } @@ -956,13 +916,6 @@ public abstract class BaseCalendarEditionController extends } else { Clients.closeErrorBox(dateboxEndDate); } - if (startDate.compareTo(new Date()) <= 0) { - throw new WrongValueException( - dateboxStartDate, - _("Exception start date should be greater than current date")); - } else { - Clients.closeErrorBox(dateboxStartDate); - } if (startDate.compareTo(endDate) > 0) { throw new WrongValueException( dateboxEndDate, diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarModel.java index 7ba092c56..89df6e7ba 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarModel.java @@ -432,6 +432,14 @@ public class BaseCalendarModel implements IBaseCalendarModel { return false; } + @Override + public boolean isFirstVersion() { + if (getBaseCalendar() != null) { + return getBaseCalendar().isFirstVersion(selectedDate); + } + return false; + } + @Override public String getName() { if (getBaseCalendar() != null) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarsTreeModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarsTreeModel.java index ff854af94..56400986b 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarsTreeModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/BaseCalendarsTreeModel.java @@ -36,8 +36,6 @@ import org.zkoss.zul.SimpleTreeNode; */ public class BaseCalendarsTreeModel extends SimpleTreeModel { - private static Map> relationParentChildren = new HashMap>(); - public BaseCalendarsTreeModel(BaseCalendarTreeRoot root) { super(createRootNodeAndDescendants(root, root.getRootCalendars(), root .getDerivedCalendars())); @@ -47,65 +45,55 @@ public class BaseCalendarsTreeModel extends SimpleTreeModel { BaseCalendarTreeRoot root, List rootCalendars, List derivedCalendars) { - fillHashParentChildren(rootCalendars, derivedCalendars); - - return new SimpleTreeNode(root, asNodes(rootCalendars)); + Map> parentChildren = createRelationParentChildren( + rootCalendars, derivedCalendars); + return new SimpleTreeNode(root, asNodes(parentChildren, rootCalendars)); } - private static List asNodes(List baseCalendars) { + private static List asNodes( + Map> relationParentChildren, + List baseCalendars) { if (baseCalendars == null) { return new ArrayList(); } ArrayList result = new ArrayList(); for (BaseCalendar baseCalendar : baseCalendars) { - result.add(asNode(baseCalendar)); + result.add(asNode(relationParentChildren, baseCalendar)); } return result; } - private static SimpleTreeNode asNode(BaseCalendar baseCalendar) { + private static SimpleTreeNode asNode( + Map> relationParentChildren, + BaseCalendar baseCalendar) { List children = relationParentChildren.get(baseCalendar); - return new SimpleTreeNode(baseCalendar, asNodes(children)); + return new SimpleTreeNode(baseCalendar, asNodes(relationParentChildren, + children)); } - private static void fillHashParentChildren( + private static Map> createRelationParentChildren( List rootCalendars, List derivedCalendars) { + Map> result = new HashMap>(); for (BaseCalendar root : rootCalendars) { - relationParentChildren.put(root, new ArrayList()); + result.put(root, new ArrayList()); } for (BaseCalendar derived : derivedCalendars) { BaseCalendar parent = derived.getParent(); - List siblings = relationParentChildren.get(parent); + List siblings = result.get(parent); if (siblings == null) { siblings = new ArrayList(); siblings.add(derived); - relationParentChildren.put(parent, siblings); + result.put(parent, siblings); } else { siblings.add(derived); } } - } - - @Override - public boolean isLeaf(Object node) { - if (node == null) { - return true; - } - - SimpleTreeNode simpleTreeNode = (SimpleTreeNode) node; - BaseCalendar baseCalendar = (BaseCalendar) simpleTreeNode.getData(); - - List children = relationParentChildren.get(baseCalendar); - if (children == null) { - return true; - } - - return children.isEmpty(); + return result; } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/IBaseCalendarModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/IBaseCalendarModel.java index 7050cccc6..16b05ef16 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/IBaseCalendarModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/calendars/IBaseCalendarModel.java @@ -150,6 +150,8 @@ public interface IBaseCalendarModel { boolean isLastVersion(); + boolean isFirstVersion(); + String getName(); LocalDate getValidFrom(CalendarData calendarData); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java index f1952d696..12d0317fb 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationController.java @@ -346,4 +346,24 @@ public class ConfigurationController extends GenericForwardComposer { } + public void setExpandCompanyPlanningViewCharts( + Boolean expandCompanyPlanningViewCharts) { + configurationModel + .setExpandCompanyPlanningViewCharts(expandCompanyPlanningViewCharts); + } + + public Boolean isExpandCompanyPlanningViewCharts() { + return configurationModel.isExpandCompanyPlanningViewCharts(); + } + + public void setExpandOrderPlanningViewCharts( + Boolean expandOrderPlanningViewCharts) { + configurationModel + .setExpandOrderPlanningViewCharts(expandOrderPlanningViewCharts); + } + + public Boolean isExpandOrderPlanningViewCharts() { + return configurationModel.isExpandOrderPlanningViewCharts(); + } + } \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationModel.java index bfe1f8707..6cedbf46b 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/ConfigurationModel.java @@ -339,4 +339,38 @@ public class ConfigurationModel implements IConfigurationModel { orderSequences.remove(orderSequence); } + @Override + public void setExpandCompanyPlanningViewCharts( + Boolean expandCompanyPlanningViewCharts) { + if (configuration != null) { + configuration + .setExpandCompanyPlanningViewCharts(expandCompanyPlanningViewCharts); + } + } + + @Override + public Boolean isExpandCompanyPlanningViewCharts() { + if (configuration == null) { + return null; + } + return configuration.isExpandCompanyPlanningViewCharts(); + } + + @Override + public void setExpandOrderPlanningViewCharts( + Boolean expandOrderPlanningViewCharts) { + if (configuration != null) { + configuration + .setExpandOrderPlanningViewCharts(expandOrderPlanningViewCharts); + } + } + + @Override + public Boolean isExpandOrderPlanningViewCharts() { + if (configuration == null) { + return null; + } + return configuration.isExpandOrderPlanningViewCharts(); + } + } \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java index ec553b57d..86db2e3f4 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/CustomMenuController.java @@ -197,11 +197,12 @@ public class CustomMenuController extends Div implements IMenuItemsRegister { subItem(_("Company view"), "/planner/index.zul;company_scheduling","01-introducion.html"), subItem(_("General resource allocation"),"/planner/index.zul;company_load","01-introducion.html#id1"), subItem(_("Orders list"), "/planner/index.zul;orders_list","01-introducion.html#id2"), + // FIX: Temporary hidden in main menu + // subItem(_("Limiting resources"),"/planner/index.zul;limiting_resources","01-introducion.html"), subItem(_("Templates list"), "/templates/templates.zul", ""), subItem(_("Subcontracted tasks list"), "/subcontract/subcontractedTasks.zul", ""), subItem(_("Report advances"), "/subcontract/reportAdvances.zul", ""), subItem(_("Transfer orders between scenarios"), "/scenarios/transferOrders.zul", "")); - topItem(_("Resources"), "/resources/worker/worker.zul", "", subItem(_("Workers List"), "/resources/worker/worker.zul","05-recursos.html#xesti-n-de-traballadores"), subItem(_("Machines List"), "/resources/machine/machines.zul","05-recursos.html#xesti-n-de-m-quinas"), diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IConfigurationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IConfigurationModel.java index e7a1eee00..72a45bf17 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IConfigurationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IConfigurationModel.java @@ -76,6 +76,15 @@ public interface IConfigurationModel { void removeOrderSequence(OrderSequence orderSequence) throws IllegalArgumentException; + void setExpandCompanyPlanningViewCharts( + Boolean expandCompanyPlanningViewCharts); + + Boolean isExpandCompanyPlanningViewCharts(); + + void setExpandOrderPlanningViewCharts(Boolean expandOrderPlanningViewCharts); + + Boolean isExpandOrderPlanningViewCharts(); + /* * Final conversation steps */ diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/bandboxsearch/BandboxMultipleSearch.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/bandboxsearch/BandboxMultipleSearch.java index 6ac046dd1..e62c14d08 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/bandboxsearch/BandboxMultipleSearch.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/bandboxsearch/BandboxMultipleSearch.java @@ -24,6 +24,7 @@ import static org.navalplanner.web.I18nHelper._; import java.util.ArrayList; import java.util.List; +import java.util.NoSuchElementException; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; @@ -134,10 +135,13 @@ public class BandboxMultipleSearch extends HtmlMacroComponent { } private void pickElementFromListAndCloseBandbox() { - final Object object = getSelectedItem().getValue(); - if (multipleFiltersFinder.isValidNewFilter(object)) { - addSelectedElement(object); - clearListbox(); + if(getSelectedItem() != null) { + final Object object = getSelectedItem().getValue(); + if (multipleFiltersFinder.isValidNewFilter(object)) { + addSelectedElement(object); + clearListbox(); + listbox.setModel(getSubModel()); + } } bandbox.close(); } @@ -238,7 +242,12 @@ public class BandboxMultipleSearch extends HtmlMacroComponent { } private Listitem getSelectedItem() { - return (Listitem) listbox.getSelectedItems().iterator().next(); + try { + return (Listitem) listbox.getSelectedItems().iterator().next(); + } + catch (NoSuchElementException e) { + return null; + } } public void setDisabled(boolean disabled) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/finders/CriterionMultipleFiltersFinder.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/finders/CriterionMultipleFiltersFinder.java new file mode 100644 index 000000000..313f42cfc --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/finders/CriterionMultipleFiltersFinder.java @@ -0,0 +1,106 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.common.components.finders; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.navalplanner.business.common.IOnTransaction; +import org.navalplanner.business.resources.daos.ICriterionDAO; +import org.navalplanner.business.resources.entities.Criterion; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +public class CriterionMultipleFiltersFinder extends MultipleFiltersFinder { + + @Autowired + private ICriterionDAO criterionDAO; + + private static final List criterionList = new ArrayList(); + + private IFilterEnum criterionFilterEnum = new IFilterEnum() { + @Override + public String toString() { + return _("criterion"); + } + }; + + @Transactional(readOnly = true) + public void init() { + getAdHocTransactionService() + .runOnReadOnlyTransaction(new IOnTransaction() { + @Override + public Void execute() { + loadCriteria(); + return null; + } + }); + } + + @Transactional(readOnly = true) + private void loadCriteria() { + criterionList.clear(); + criterionList.addAll(criterionDAO.getAll()); + } + + @Override + public List getFirstTenFilters() { + Iterator iteratorCriterion = criterionList.iterator(); + while(iteratorCriterion.hasNext() && getListMatching().size() < 10) { + Criterion criterion = iteratorCriterion.next(); + getListMatching().add(new FilterPair( + criterionFilterEnum, criterion.getName(), criterion)); + } + return getListMatching(); + } + + @Override + public List getMatching(String filter) { + getListMatching().clear(); + if ((filter != null) && (!filter.isEmpty())) { + filter = StringUtils.deleteWhitespace(filter.toLowerCase()); + searchInCriteria(filter); + } + addNoneFilter(); + return getListMatching(); + + } + private void searchInCriteria(String filter) { + for(Criterion criterion : criterionList) { + String name = StringUtils.deleteWhitespace( + criterion.getName().toLowerCase()); + if(name.contains(filter)) { + getListMatching().add(new FilterPair( + criterionFilterEnum, criterion.getName(), criterion)); + } + } + } + + private void addNoneFilter() { + getListMatching().add( + new FilterPair(OrderElementFilterEnum.None, + OrderElementFilterEnum.None.toString(), null)); + } +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/finders/WorkerMultipleFiltersFinder.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/finders/WorkerMultipleFiltersFinder.java new file mode 100644 index 000000000..4203b640f --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/components/finders/WorkerMultipleFiltersFinder.java @@ -0,0 +1,107 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.common.components.finders; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.navalplanner.business.common.IOnTransaction; +import org.navalplanner.business.resources.daos.IWorkerDAO; +import org.navalplanner.business.resources.entities.Worker; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +public class WorkerMultipleFiltersFinder extends MultipleFiltersFinder { + + @Autowired + private IWorkerDAO workerDAO; + + private static final List workerList = new ArrayList(); + + private IFilterEnum workerFilterEnum = new IFilterEnum() { + @Override + public String toString() { + return _("worker"); + } + }; + + @Transactional(readOnly = true) + public void init() { + getAdHocTransactionService() + .runOnReadOnlyTransaction(new IOnTransaction() { + @Override + public Void execute() { + loadWorkers(); + return null; + } + }); + } + + @Transactional(readOnly = true) + private void loadWorkers() { + workerList.clear(); + workerList.addAll(workerDAO.getAll()); + } + + @Override + public List getFirstTenFilters() { + Iterator iteratorWorker = workerList.iterator(); + while(iteratorWorker.hasNext() && getListMatching().size() < 10) { + Worker worker = iteratorWorker.next(); + getListMatching().add(new FilterPair( + workerFilterEnum, worker.getShortDescription(), worker)); + } + return getListMatching(); + } + + @Override + public List getMatching(String filter) { + getListMatching().clear(); + if ((filter != null) && (!filter.isEmpty())) { + filter = StringUtils.deleteWhitespace(filter.toLowerCase()); + searchInWorkers(filter); + } + addNoneFilter(); + return getListMatching(); + + } + private void searchInWorkers(String filter) { + boolean limited = (filter.length() < 3); + for(Worker worker : workerList) { + String name = StringUtils.deleteWhitespace( + worker.getShortDescription().toLowerCase()); + if(name.contains(filter)) { + getListMatching().add(new FilterPair( + workerFilterEnum, worker.getShortDescription(), worker)); + } + } + } + + private void addNoneFilter() { + getListMatching().add( + new FilterPair(OrderElementFilterEnum.None, + OrderElementFilterEnum.None.toString(), null)); + } +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java new file mode 100644 index 000000000..4fe25b5f1 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/ILimitingResourceQueueModel.java @@ -0,0 +1,50 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import java.util.List; + +import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.ganttz.util.Interval; + +public interface ILimitingResourceQueueModel { + + void initGlobalView(boolean filterByResources); + + void initGlobalView(Order filterBy, boolean filterByResources); + + List getLimitingResourceQueues(); + + Interval getViewInterval(); + + ZoomLevel calculateInitialZoomLevel(); + + Order getOrderByTask(TaskElement task); + + boolean userCanRead(Order order, String loginName); + + List getUnassignedLimitingResourceQueueElements(); + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyComponent.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyComponent.java new file mode 100644 index 000000000..d566ceb2a --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyComponent.java @@ -0,0 +1,157 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Date; + +import org.apache.commons.lang.Validate; +import org.zkoss.ganttz.TaskComponent; +import org.zkoss.ganttz.data.Dependency; +import org.zkoss.ganttz.data.DependencyType; +import org.zkoss.ganttz.data.Task; +import org.zkoss.ganttz.data.constraint.Constraint; +import org.zkoss.ganttz.data.constraint.Constraint.IConstraintViolationListener; +import org.zkoss.zk.au.out.AuInvoke; +import org.zkoss.zk.ui.ext.AfterCompose; +import org.zkoss.zul.Div; +import org.zkoss.zul.impl.XulElement; + +/** + * + * @author Francisco Javier Moran Rúa + * @author Lorenzo Tilve Álvaro + */ +public class LimitingDependencyComponent extends XulElement implements + AfterCompose { + + private Div source; + + private Div destination; + + private DependencyType type; + + // private Dependency dependency; + + private IConstraintViolationListener violationListener; + + public LimitingDependencyComponent(Div source, Div destination) { + Validate.notNull(source); + Validate.notNull(destination); + // Validate.isTrue(source.getTask() == dependency.getSource()); + // Validate.isTrue(destination.getTask() == + // dependency.getDestination()); + // this.type = dependency.getType(); + this.source = source; + this.destination = destination; + // this.dependency = dependency; + violationListener = new IConstraintViolationListener() { + + @Override + public void constraintViolated(Constraint constraint, + Date value) { + // TODO mark graphically dependency as violated + } + }; + // this.dependency.addConstraintViolationListener(violationListener); + } + + @Override + public void afterCompose() { + PropertyChangeListener listener = new PropertyChangeListener() { + + @Override + public void propertyChange(PropertyChangeEvent evt) { + redrawDependency(); + } + }; + // this.source.getTask().addFundamentalPropertiesChangeListener(listener); + // this.destination.getTask().addFundamentalPropertiesChangeListener(listener); + } + + /** + * @return the idTaskOrig + */ + public String getIdTaskOrig() { + return source.getUuid(); + } + + public void setIdTaskOrig(String idTaskOrig) { + this.source = findTaskComponent(idTaskOrig); + + } + + private TaskComponent findTaskComponent(String idTaskOrig) { + return (TaskComponent) getFellow(idTaskOrig); + } + + /** + * @return the idTaskEnd + */ + public String getIdTaskEnd() { + return destination.getUuid(); + } + + public void setIdTaskEnd(String idTaskEnd) { + this.destination = findTaskComponent(idTaskEnd); + } + + public void zoomChanged() { + redrawDependency(); + } + + public void redrawDependency() { + response("zoomChanged", new AuInvoke(this, "draw")); + } + + public boolean contains(Task task) { + return false; + // Task sourceTask = getSource().getTask(); + // Task destinationTask = getDestination().getTask(); + // return task.equals(sourceTask) || task.equals(destinationTask); + } + + public Div getSource() { + return source; + } + + public Div getDestination() { + return destination; + } + + // public Dependency getDependency() { + // return dependency; + // } + + public DependencyType getDependencyType() { + return type; + } + + public boolean hasSameSourceAndDestination(Dependency dependency) { + return false; + // Task sourceTask = source.getTask(); + // Task destinationTask = destination.getTask(); + // return sourceTask.equals(dependency.getSource()) + // && destinationTask.equals(dependency.getDestination()); + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyList.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyList.java new file mode 100644 index 000000000..88ba483bc --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingDependencyList.java @@ -0,0 +1,281 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import static org.zkoss.ganttz.i18n.I18nHelper._; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.zkoss.ganttz.DependencyList; +import org.zkoss.ganttz.FunctionalityExposedForExtensions; +import org.zkoss.ganttz.GanttPanel; +import org.zkoss.ganttz.TaskComponent; +import org.zkoss.ganttz.data.Dependency; +import org.zkoss.ganttz.data.DependencyType; +import org.zkoss.ganttz.data.Task; +import org.zkoss.ganttz.timetracker.TimeTracker; +import org.zkoss.ganttz.timetracker.TimeTrackerComponent; +import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; +import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.ganttz.util.ComponentsFinder; +import org.zkoss.ganttz.util.MenuBuilder; +import org.zkoss.ganttz.util.MenuBuilder.ItemAction; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.ext.AfterCompose; +import org.zkoss.zul.Div; +import org.zkoss.zul.Menupopup; +import org.zkoss.zul.impl.XulElement; + +/** + * @author Lorenzo Tilve Álvaro + */ +public class LimitingDependencyList extends XulElement implements AfterCompose { + + private final class ChangeTypeAction implements + ItemAction { + private final DependencyType type; + + private ChangeTypeAction(DependencyType type) { + this.type = type; + } + + @Override + public void onEvent(final LimitingDependencyComponent choosen, + Event event) { + // context.changeType(choosen.getDependency(), type); + } + } + + private final class DependencyVisibilityToggler implements + PropertyChangeListener { + private final Task source; + private final Task destination; + private final LimitingDependencyComponent dependencyComponent; + + private DependencyVisibilityToggler(Task source, Task destination, + LimitingDependencyComponent dependencyComponent) { + this.source = source; + this.destination = destination; + this.dependencyComponent = dependencyComponent; + } + + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (!evt.getPropertyName().equals("visible")) { + return; + } + if (dependencyMustBeVisible() != isDependencyNowVisible()) { + toggleDependencyExistence(dependencyMustBeVisible()); + } + } + + void toggleDependencyExistence(boolean visible) { + if (visible) { + appendChild(dependencyComponent); + addContextMenu(dependencyComponent); + } else { + removeChild(dependencyComponent); + } + } + + boolean isDependencyNowVisible() { + return dependencyComponent.getParent() != null; + } + + boolean dependencyMustBeVisible() { + return source.isVisible() && destination.isVisible(); + } + } + + private static final Log LOG = LogFactory.getLog(DependencyList.class); + + private transient IZoomLevelChangedListener listener; + + private final FunctionalityExposedForExtensions context; + + public LimitingDependencyList(FunctionalityExposedForExtensions context) { + this.context = context; + } + + private List getLimitingDependencyComponents() { + List children = getChildren(); + return ComponentsFinder.findComponentsOfType( + LimitingDependencyComponent.class, children); + } + + void addDependencyComponent( + final LimitingDependencyComponent dependencyComponent) { + Div source = dependencyComponent.getSource(); + Div destination = dependencyComponent.getDestination(); + // DependencyVisibilityToggler visibilityToggler = new + // DependencyVisibilityToggler( + // source.getTask(), destination.getTask(), dependencyComponent); + // source.getTask().addVisibilityPropertiesChangeListener( + // visibilityToggler); + // destination.getTask().addVisibilityPropertiesChangeListener( + // visibilityToggler); + // boolean dependencyMustBeVisible = visibilityToggler + // .dependencyMustBeVisible(); + // visibilityToggler.toggleDependencyExistence(dependencyMustBeVisible); + // if (dependencyMustBeVisible) { + // dependencyComponent.redrawDependency(); + // } + } + + private void addContextMenu(LimitingDependencyComponent dependencyComponent) { + dependencyComponent.setContext(getContextMenu()); + } + + private GanttPanel getGanttPanel() { + return (GanttPanel) getParent(); + } + + public void setDependencyComponents( + List dependencyComponents) { + for (LimitingDependencyComponent dependencyComponent : dependencyComponents) { + addDependencyComponent(dependencyComponent); + } + } + + @Override + public void afterCompose() { + if (listener == null) { + listener = new IZoomLevelChangedListener() { + @Override + public void zoomLevelChanged(ZoomLevel detailLevel) { + if (!isInPage()) { + return; + } + for (LimitingDependencyComponent dependencyComponent : getLimitingDependencyComponents()) { + dependencyComponent.zoomChanged(); + } + } + }; + // getTimeTracker().addZoomListener(listener); + } + // addContextMenu(); + } + + private boolean isInPage() { + return getParent() != null && getGanttPanel() != null + && getGanttPanel().getParent() != null; + } + + private TimeTracker getTimeTracker() { + return getTimeTrackerComponent().getTimeTracker(); + } + + private void addContextMenu() { + for (LimitingDependencyComponent dependencyComponent : getLimitingDependencyComponents()) { + addContextMenu(dependencyComponent); + } + } + + private Menupopup contextMenu; + + private Menupopup getContextMenu() { + if (contextMenu == null) { + MenuBuilder contextMenuBuilder = MenuBuilder + .on(getPage(), getLimitingDependencyComponents()).item(_("Erase"), + "/common/img/ico_borrar.png", + new ItemAction() { + @Override + public void onEvent( + final LimitingDependencyComponent choosen, + Event event) { +// context +// .removeDependency(choosen.getDependency()); + } + }); + contextMenuBuilder.item(_("Set End-Start"), null, + new ChangeTypeAction( + DependencyType.END_START)); + + contextMenuBuilder.item(_("Set Start-Start"), null, + new ChangeTypeAction( + DependencyType.START_START)); + + contextMenuBuilder.item(_("Set End-End"), null, + new ChangeTypeAction( + DependencyType.END_END)); + + contextMenu = contextMenuBuilder.create(); + + } + return contextMenu; + } + + private TimeTrackerComponent getTimeTrackerComponent() { + return getGanttPanel().getTimeTrackerComponent(); + } + + public void redrawDependenciesConnectedTo(TaskComponent taskComponent) { + redrawDependencyComponents(getDependencyComponentsConnectedTo(taskComponent)); + } + + private List getDependencyComponentsConnectedTo( + TaskComponent taskComponent) { + ArrayList result = new ArrayList(); + List dependencies = getLimitingDependencyComponents(); + for (LimitingDependencyComponent dependencyComponent : dependencies) { + if (dependencyComponent.getSource().equals(taskComponent) + || dependencyComponent.getDestination().equals( + taskComponent)) { + result.add(dependencyComponent); + } + } + return result; + } + + public void redrawDependencies() { + redrawDependencyComponents(getLimitingDependencyComponents()); + } + + public void redrawDependencyComponents( + List dependencyComponents) { + for (LimitingDependencyComponent dependencyComponent : dependencyComponents) { + dependencyComponent.redrawDependency(); + } + } + + public void taskRemoved(Task task) { + for (LimitingDependencyComponent dependencyComponent : LimitingDependencyList.this + .getLimitingDependencyComponents()) { + if (dependencyComponent.contains(task)) { + this.removeChild(dependencyComponent); + } + } + } + + public void remove(Dependency dependency) { + for (LimitingDependencyComponent dependencyComponent : LimitingDependencyList.this + .getLimitingDependencyComponents()) { + if (dependencyComponent.hasSameSourceAndDestination(dependency)) { + this.removeChild(dependencyComponent); + } + } + } +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java new file mode 100644 index 000000000..423c1ccff --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourceQueueModel.java @@ -0,0 +1,304 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.joda.time.LocalDate; +import org.navalplanner.business.common.BaseEntity; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.orders.daos.IOrderDAO; +import org.navalplanner.business.orders.daos.IOrderElementDAO; +import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.orders.entities.OrderElement; +import org.navalplanner.business.planner.daos.ILimitingResourceQueueElementDAO; +import org.navalplanner.business.planner.daos.IResourceAllocationDAO; +import org.navalplanner.business.planner.entities.GenericResourceAllocation; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.business.resources.daos.IResourceDAO; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.business.users.daos.IOrderAuthorizationDAO; +import org.navalplanner.business.users.daos.IUserDAO; +import org.navalplanner.business.users.entities.OrderAuthorization; +import org.navalplanner.business.users.entities.OrderAuthorizationType; +import org.navalplanner.business.users.entities.User; +import org.navalplanner.business.users.entities.UserRole; +import org.navalplanner.web.security.SecurityUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.zkoss.ganttz.data.resourceload.TimeLineRole; +import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.ganttz.util.Interval; + +@Component +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class LimitingResourceQueueModel implements ILimitingResourceQueueModel { + + @Autowired + private IResourceDAO resourcesDAO; + + @Autowired + private IOrderElementDAO orderElementDAO; + + @Autowired + private IOrderDAO orderDAO; + + @Autowired + private IResourceAllocationDAO resourceAllocationDAO; + + @Autowired + private IUserDAO userDAO; + + @Autowired + private IOrderAuthorizationDAO orderAuthorizationDAO; + + @Autowired + private ILimitingResourceQueueElementDAO limitingResourceQueueElementDAO; + + private List limitingResourceQueues = new ArrayList(); + + private Interval viewInterval; + + private Order filterBy; + + private boolean filterByResources = true; + + @Override + @Transactional(readOnly = true) + public void initGlobalView(boolean filterByResources) { + filterBy = null; + this.filterByResources = filterByResources; + doGlobalView(); + } + + @Override + @Transactional(readOnly = true) + public void initGlobalView(Order filterBy, boolean filterByResources) { + this.filterBy = orderDAO.findExistingEntity(filterBy.getId()); + this.filterByResources = filterByResources; + doGlobalView(); + } + + @Override + @Transactional(readOnly = true) + public Order getOrderByTask(TaskElement task) { + return orderElementDAO + .loadOrderAvoidingProxyFor(task.getOrderElement()); + } + + @Override + @Transactional(readOnly = true) + public boolean userCanRead(Order order, String loginName) { + if (SecurityUtils.isUserInRole(UserRole.ROLE_READ_ALL_ORDERS) + || SecurityUtils.isUserInRole(UserRole.ROLE_EDIT_ALL_ORDERS)) { + return true; + } + try { + User user = userDAO.findByLoginName(loginName); + for (OrderAuthorization authorization : orderAuthorizationDAO + .listByOrderUserAndItsProfiles(order, user)) { + if (authorization.getAuthorizationType() == OrderAuthorizationType.READ_AUTHORIZATION + || authorization.getAuthorizationType() == OrderAuthorizationType.WRITE_AUTHORIZATION) { + return true; + } + } + } catch (InstanceNotFoundException e) { + // this case shouldn't happen, because it would mean that there + // isn't a logged user + // anyway, if it happenned we don't allow the user to pass + } + return false; + } + + private void doGlobalView() { + limitingResourceQueues = calculateLimitingResourceQueues(); + if (!limitingResourceQueues.isEmpty()) { + // Build interval + // viewInterval = + // LimitingResourceQueue.getIntervalFrom(limitingResourceQueues); + viewInterval = new Interval(new Date(), plusFiveYears(new Date())); + } else { + viewInterval = new Interval(new Date(), plusFiveYears(new Date())); + } + } + + private Date plusFiveYears(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.YEAR, 5); + return calendar.getTime(); + } + + private List calculateLimitingResourceQueues() { + List result = new ArrayList(); + result.addAll(groupsFor(resourcesToShow())); + return result; + } + + private List resourcesToShow() { + if (filter()) { + return resourcesForActiveTasks(); + } else { + return allLimitingResources(); + } + } + + private boolean filter() { + return filterBy != null; + } + + private List resourcesForActiveTasks() { + return Resource.sortByName(resourcesDAO + .findResourcesRelatedTo(justTasks(filterBy + .getAllChildrenAssociatedTaskElements()))); + } + + private List justTasks(Collection tasks) { + List result = new ArrayList(); + for (TaskElement taskElement : tasks) { + if (taskElement instanceof Task) { + result.add((Task) taskElement); + } + } + return result; + } + + private List allLimitingResources() { + List result = Resource.sortByName(resourcesDAO + .getAllLimitingResources()); + for (Resource each : result) { + each.getLimitingResourceQueue().getLimitingResourceQueueElements() + .size(); + limitingResourceQueues.add(each.getLimitingResourceQueue()); + } + return result; + } + + private TimeLineRole getCurrentTimeLineRole(BaseEntity entity) { + return new TimeLineRole(entity); + } + + private List groupsFor(List allResources) { + List result = new ArrayList(); + for (Resource resource : allResources) { + LimitingResourceQueue group = resource.getLimitingResourceQueue(); + result.add(group); + } + return result; + } + + private void initializeIfNeeded( + Map>> result, Order order) { + if (!result.containsKey(order)) { + result.put(order, new ArrayList>()); + } + } + + @Transactional(readOnly = true) + public Map>> byOrder( + Collection> allocations) { + Map>> result = new HashMap>>(); + for (ResourceAllocation resourceAllocation : allocations) { + if ((resourceAllocation.isSatisfied()) + && (resourceAllocation.getTask() != null)) { + OrderElement orderElement = resourceAllocation.getTask() + .getOrderElement(); + Order order = orderElementDAO + .loadOrderAvoidingProxyFor(orderElement); + initializeIfNeeded(result, order); + result.get(order).add(resourceAllocation); + } + } + return result; + } + + + private List onlyGeneric( + List> sortedByStartDate) { + return ResourceAllocation.getOfType(GenericResourceAllocation.class, + sortedByStartDate); + } + + public static String getName(Collection criterions, + Task task) { + String prefix = task.getName(); + return (prefix + " :: " + getName(criterions)); + } + + public static String getName(Collection criterions) { + if (criterions.isEmpty()) { + return _("[generic all workers]"); + } + String[] names = new String[criterions.size()]; + int i = 0; + for (Criterion criterion : criterions) { + names[i++] = criterion.getName(); + } + return (Arrays.toString(names)); + } + + + @Override + public List getLimitingResourceQueues() { + return limitingResourceQueues; + } + + @Override + public Interval getViewInterval() { + return viewInterval; + } + + public ZoomLevel calculateInitialZoomLevel() { + Interval interval = getViewInterval(); + return ZoomLevel.getDefaultZoomByDates(new LocalDate(interval + .getStart()), new LocalDate(interval.getFinish())); + } + + @Override + @Transactional(readOnly=true) + public List getUnassignedLimitingResourceQueueElements() { + List result = limitingResourceQueueElementDAO + .getUnassigned(); + for (LimitingResourceQueueElement each : result) { + limitingResourceQueueElementDAO.reattach(each); + each.getResourceAllocation().getTask().getName(); + } + return result; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java new file mode 100644 index 000000000..b762ab899 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesComponent.java @@ -0,0 +1,184 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galic + * ia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.zkoss.ganttz.IDatesMapper; +import org.zkoss.ganttz.timetracker.TimeTracker; +import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; +import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.ganttz.util.MenuBuilder; +import org.zkoss.ganttz.util.MenuBuilder.ItemAction; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.ext.AfterCompose; +import org.zkoss.zul.Div; +import org.zkoss.zul.impl.XulElement; + +/** + * This class wraps ResourceLoad data inside an specific HTML Div component. + * @author Lorenzo Tilve Álvaro + */ +public class LimitingResourcesComponent extends XulElement implements + AfterCompose { + + public static LimitingResourcesComponent create(TimeTracker timeTracker, + LimitingResourceQueue limitingResourceQueue) { + return new LimitingResourcesComponent(timeTracker, + limitingResourceQueue); + } + + private final LimitingResourceQueue limitingResourceQueue; + private final TimeTracker timeTracker; + private transient IZoomLevelChangedListener zoomChangedListener; + private List
queueElementDivs = new ArrayList
(); + + private LimitingResourcesComponent(final TimeTracker timeTracker, + final LimitingResourceQueue limitingResourceQueue) { + this.limitingResourceQueue = limitingResourceQueue; + this.timeTracker = timeTracker; + createChildren(limitingResourceQueue, timeTracker.getMapper()); + zoomChangedListener = new IZoomLevelChangedListener() { + + @Override + public void zoomLevelChanged(ZoomLevel detailLevel) { + getChildren().clear(); + createChildren(limitingResourceQueue, timeTracker.getMapper()); + invalidate(); + } + }; + this.timeTracker.addZoomListener(zoomChangedListener); + } + + private void createChildren(LimitingResourceQueue limitingResourceQueue, + IDatesMapper mapper) { + List
divs = createDivsForQueueElements(mapper, + limitingResourceQueue.getLimitingResourceQueueElements()); + if (divs != null) { + for (Div div : divs) { + appendChild(div); + } + queueElementDivs.addAll(divs); + } + } + + public String getResourceName() { + return limitingResourceQueue.getResource().getName(); + } + + private static List
createDivsForQueueElements( + IDatesMapper datesMapper, + Set list) { + List
result = new ArrayList
(); + + for (LimitingResourceQueueElement queueElement : list) { + validateQueueElement(queueElement); + result.add(createDivForQueueElement(datesMapper, queueElement)); + } + + return result; + } + + private static void validateQueueElement( + LimitingResourceQueueElement queueElement) { + if ((queueElement.getStartDate() == null) + || (queueElement.getStartDate() == null)) { + throw new ValidationException(_("Invalid queue element")); + } + } + + private void appendMenu(Div divElement) { + if (divElement.getPage() != null) { + MenuBuilder
menuBuilder = MenuBuilder.on(divElement.getPage(), + queueElementDivs); + menuBuilder.item(_("Unassign"), "/common/img/ico_borrar.png", + new ItemAction
() { + @Override + public void onEvent(Div choosen, Event event) { + unnasign(choosen); + } + }); + divElement.setContext(menuBuilder.createWithoutSettingContext()); + } + } + + // FIXME: Implement real unnasign operation + private void unnasign(Div choosen) { + choosen.detach(); + } + + private static Div createDivForQueueElement(IDatesMapper datesMapper, + LimitingResourceQueueElement queueElement) { + Div result = new Div(); + result.setClass("queue-element"); + + result.setTooltiptext(queueElement.getLimitingResourceQueue() + .getResource().getName()); + + result.setLeft(forCSS(getStartPixels(datesMapper, queueElement))); + result.setWidth(forCSS(getWidthPixels(datesMapper, queueElement))); + + return result; + } + + private static int getWidthPixels(IDatesMapper datesMapper, + LimitingResourceQueueElement loadPeriod) { + LocalDate start = loadPeriod.getStartDate(); + LocalDate end = loadPeriod.getEndDate(); + return datesMapper + .toPixels(toMilliseconds(end) - toMilliseconds(start)); + } + + private static long toMilliseconds(LocalDate localDate) { + return localDate.toDateMidnight().getMillis(); + } + + private static String forCSS(int pixels) { + return String.format("%dpx", pixels); + } + + private static int getStartPixels(IDatesMapper datesMapper, + LimitingResourceQueueElement queueElement) { + return datesMapper.toPixels(queueElement.getStartDate().toDateMidnight() + .toDate()); + } + + private void appendContextMenus() { + for (Div each : queueElementDivs) { + appendMenu(each); + } + } + + @Override + public void afterCompose() { + appendContextMenus(); + } + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java new file mode 100644 index 000000000..806a6e5d2 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesController.java @@ -0,0 +1,227 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang.Validate; +import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.web.limitingresources.LimitingResourcesPanel.IToolbarCommand; +import org.navalplanner.web.planner.order.BankHolidaysMarker; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.zkoss.ganttz.resourceload.IFilterChangedListener; +import org.zkoss.ganttz.timetracker.TimeTracker; +import org.zkoss.ganttz.timetracker.zoom.SeveralModificators; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.util.Composer; +import org.zkoss.zul.Button; +import org.zkoss.zul.Checkbox; +import org.zkoss.zul.Label; +import org.zkoss.zul.Messagebox; +import org.zkoss.zul.Row; +import org.zkoss.zul.RowRenderer; + +/** + * Controller for limiting resources view + * @author Lorenzo Tilve Álvaro + */ +@Component +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class LimitingResourcesController implements Composer { + + @Autowired + private ILimitingResourceQueueModel limitingResourceQueueModel; + + private List commands = new ArrayList(); + + private Order filterBy; + + private org.zkoss.zk.ui.Component parent; + + private LimitingResourcesPanel limitingResourcesPanel; + + private TimeTracker timeTracker; + + private final LimitingResourceQueueElementsRenderer limitingResourceQueueElementsRenderer = + new LimitingResourceQueueElementsRenderer(); + + private transient IFilterChangedListener filterChangedListener; + + public LimitingResourcesController() { + } + + public void add(IToolbarCommand... commands) { + Validate.noNullElements(commands); + this.commands.addAll(Arrays.asList(commands)); + } + + @Override + public void doAfterCompose(org.zkoss.zk.ui.Component comp) throws Exception { + this.parent = comp; + reload(); + } + + public void reload() { + // by default show the task by resources + boolean filterByResources = true; + reload(filterByResources); + } + + private void reload(boolean filterByResources) { + try { + if (filterBy == null) { + limitingResourceQueueModel.initGlobalView(filterByResources); + } else { + limitingResourceQueueModel.initGlobalView(filterBy, + filterByResources); + } + timeTracker = buildTimeTracker(); + limitingResourcesPanel = buildLimitingResourcesPanel(); + addListeners(); + + this.parent.getChildren().clear(); + this.parent.appendChild(limitingResourcesPanel); + limitingResourcesPanel.afterCompose(); + addCommands(limitingResourcesPanel); + } catch (IllegalArgumentException e) { + try { + e.printStackTrace(); + Messagebox.show(_("Limiting resources error") + e, _("Error"), + Messagebox.OK, Messagebox.ERROR); + } catch (InterruptedException o) { + throw new RuntimeException(e); + } + } + } + + private void addListeners() { + filterChangedListener = new IFilterChangedListener() { + + @Override + public void filterChanged(boolean filter) { + onApplyFilter(filter); + } + }; + // this.limitingResourcesPanel.addFilterListener(filterChangedListener); + } + + public void onApplyFilter(boolean filterByResources) { + limitingResourcesPanel.clearComponents(); + reload(filterByResources); + } + + private void addCommands(LimitingResourcesPanel limitingResourcesPanel) { + limitingResourcesPanel.add(commands.toArray(new IToolbarCommand[0])); + } + + private TimeTracker buildTimeTracker() { + return timeTracker = new TimeTracker(limitingResourceQueueModel + .getViewInterval(), limitingResourceQueueModel + .calculateInitialZoomLevel(), SeveralModificators.create(), + SeveralModificators.create(new BankHolidaysMarker()), parent); + } + + private LimitingResourcesPanel buildLimitingResourcesPanel() { + LimitingResourcesPanel result = new LimitingResourcesPanel( + limitingResourceQueueModel.getLimitingResourceQueues(), + timeTracker); + result.setVariable("limitingResourcesController", this, true); + return result; + } + + public List getUnassignedLimitingResourceQueueElements() { + return limitingResourceQueueModel.getUnassignedLimitingResourceQueueElements(); + } + + public void filterBy(Order order) { + this.filterBy = order; + } + + public LimitingResourceQueueElementsRenderer getLimitingResourceQueueElementsRenderer() { + return limitingResourceQueueElementsRenderer; + } + + private class LimitingResourceQueueElementsRenderer implements RowRenderer { + + @Override + public void render(Row row, Object data) throws Exception { + LimitingResourceQueueElement element = (LimitingResourceQueueElement) data; + + row.appendChild(label(getTaskName(element))); + row.appendChild(label(getOrderName(element))); + row.appendChild(assignButton(element)); + row.appendChild(automaticQueueing(element)); + } + + private Button assignButton(final LimitingResourceQueueElement element) { + Button result = new Button(); + result.setLabel("Assign"); + result.setTooltiptext(_("Assign to queue")); + result.addEventListener(Events.ON_CLICK, new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + // FIXME: assign element to queue + } + }); + return result; + } + + private Checkbox automaticQueueing(final LimitingResourceQueueElement element) { + Checkbox result = new Checkbox(); + result.setTooltiptext(_("Select for automatic queuing")); + return result; + } + + private Label label(String value) { + return new Label(value); + } + + private TaskElement getTask(LimitingResourceQueueElement element) { + return element.getResourceAllocation().getTask(); + } + + private String getTaskName(LimitingResourceQueueElement element) { + return getTask(element).getName(); + } + + private Order getOrder(LimitingResourceQueueElement element) { + return limitingResourceQueueModel.getOrderByTask(getTask(element)); + } + + private String getOrderName(LimitingResourceQueueElement element) { + return getOrder(element).getName(); + } + + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesLeftPane.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesLeftPane.java new file mode 100644 index 000000000..ebbe37c99 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesLeftPane.java @@ -0,0 +1,168 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.zkoss.ganttz.util.MutableTreeModel; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.HtmlMacroComponent; +import org.zkoss.zul.Div; +import org.zkoss.zul.Label; +import org.zkoss.zul.Popup; +import org.zkoss.zul.Treecell; +import org.zkoss.zul.Treechildren; +import org.zkoss.zul.Treeitem; +import org.zkoss.zul.TreeitemRenderer; +import org.zkoss.zul.Treerow; +import org.zkoss.zul.api.Tree; + +public class LimitingResourcesLeftPane extends HtmlMacroComponent { + + private MutableTreeModel modelForTree; + private final LimitingResourcesList limitingResourcesList; + + public LimitingResourcesLeftPane( + MutableTreeModel treeModel, + LimitingResourcesList resourceLoadList) { + this.limitingResourcesList = resourceLoadList; + this.modelForTree = treeModel; + } + + + @Override + public void afterCompose() { + super.afterCompose(); + getContainerTree().setModel(modelForTree); + getContainerTree().setTreeitemRenderer(getRendererForTree()); + } + + private TreeitemRenderer getRendererForTree() { + return new TreeitemRenderer() { + @Override + public void render(Treeitem item, Object data) + throws Exception { + LimitingResourceQueue line = (LimitingResourceQueue) data; + item.setOpen(false); + item.setValue(line); + + Treerow row = new Treerow(); + Treecell cell = new Treecell(); + Component component = createComponent(line); + item.appendChild(row); + row.appendChild(cell); + cell.appendChild(component); + } + + private Component createComponent(LimitingResourceQueue line) { + return isTopLevel(line) ? createFirstLevel(line) + : createSecondLevel(line); + } + + private boolean isTopLevel(LimitingResourceQueue line) { + int[] path = modelForTree.getPath(modelForTree.getRoot(), line); + return path.length == 0; + } + }; + } + + private void collapse(LimitingResourceQueue line) { + // unnecesary + limitingResourcesList.collapse(line); + } + + private void expand(LimitingResourceQueue line, + List closed) { + // unnecesary + // limitingResourcesList.expand(line, closed); + } + + private List calculatedClosedItems(Treeitem item) { + List result = new ArrayList(); + Treechildren treeChildren = item.getTreechildren(); + if (treeChildren != null) { + List myTreeItems = (List) treeChildren + .getChildren(); + Iterator iterator = myTreeItems.iterator(); + while (iterator.hasNext()) { + Treeitem child = (Treeitem) iterator.next(); + if (!child.isOpen()) { + result.addAll(getLineChildrenBy(child)); + } else { + result.addAll(calculatedClosedItems(child)); + } + } + } + return result; + } + + private List getLineChildrenBy(Treeitem item) { + List result = new ArrayList(); + LimitingResourceQueue line = getLineByTreeitem(item); + return result; + } + + private LimitingResourceQueue getLineByTreeitem(Treeitem child) { + LimitingResourceQueue line = null; + try { + line = (LimitingResourceQueue) child.getValue(); + } catch (Exception e) { + return null; + } + return line; + } + + private Tree getContainerTree() { + return (Tree) getFellow("loadsTree"); + } + + private Component createFirstLevel(LimitingResourceQueue principal) { + Div result = createLabelWithName(principal); + result.setSclass("firstlevel"); + return result; + } + + private Component createSecondLevel(LimitingResourceQueue loadTimeLine) { + Div result = createLabelWithName(loadTimeLine); + result.setSclass("secondlevel"); + return result; + } + + private Div createLabelWithName(LimitingResourceQueue principal) { + Div result = new Div(); + Label label = new Label(); + final String conceptName = principal.getResource().getName(); + label.setValue(conceptName); + result.appendChild(label); + return result; + } + + + private static Popup createPopup(Div parent, String originalValue) { + Popup result = new Popup(); + result.appendChild(new Label(originalValue)); + parent.appendChild(result); + return result; + } +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java new file mode 100644 index 000000000..80d34ac0c --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesList.java @@ -0,0 +1,117 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.zkoss.ganttz.timetracker.TimeTracker; +import org.zkoss.ganttz.timetracker.zoom.IZoomLevelChangedListener; +import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.ganttz.util.MutableTreeModel; +import org.zkoss.zk.au.out.AuInvoke; +import org.zkoss.zk.ui.HtmlMacroComponent; +import org.zkoss.zk.ui.ext.AfterCompose; + +/** + * Component to include a list of ResourceLoads inside the ResourcesLoadPanel. + * @author Lorenzo Tilve Álvaro + */ +public class LimitingResourcesList extends HtmlMacroComponent implements + AfterCompose { + + private final IZoomLevelChangedListener zoomListener; + + private Map fromTimeLineToComponent = new HashMap(); + + private final MutableTreeModel timelinesTree; + + private List limitingResourcesComponents = new ArrayList(); + + public LimitingResourcesList(TimeTracker timeTracker, + MutableTreeModel timelinesTree) { + this.timelinesTree = timelinesTree; + zoomListener = adjustTimeTrackerSizeListener(); + timeTracker.addZoomListener(zoomListener); + LimitingResourceQueue current = timelinesTree.getRoot(); + List toInsert = new ArrayList(); + fill(timelinesTree, current, toInsert); + insertAsComponents(timeTracker, toInsert); + } + + private void fill(MutableTreeModel timelinesTree, + LimitingResourceQueue current, List result) { + final int length = timelinesTree.getChildCount(current); + for (int i = 0; i < length; i++) { + LimitingResourceQueue child = timelinesTree.getChild(current, i); + result.add(child); + fill(timelinesTree, child, result); + } + } + + private IZoomLevelChangedListener adjustTimeTrackerSizeListener() { + return new IZoomLevelChangedListener() { + + @Override + public void zoomLevelChanged(ZoomLevel detailLevel) { + response(null, new AuInvoke(LimitingResourcesList.this, + "adjustTimeTrackerSize")); + response(null, new AuInvoke(LimitingResourcesList.this, + "adjustResourceLoadRows")); + } + }; + } + + private void insertAsComponents(TimeTracker timetracker, + List children) { + for (LimitingResourceQueue LimitingResourceQueue : children) { + LimitingResourcesComponent component = LimitingResourcesComponent + .create(timetracker, LimitingResourceQueue); + limitingResourcesComponents.add(component); + appendChild(component); + fromTimeLineToComponent.put(LimitingResourceQueue, component); + } + } + + public void collapse(LimitingResourceQueue line) { + } + + private LimitingResourcesComponent getComponentFor(LimitingResourceQueue l) { + LimitingResourcesComponent resourceLoadComponent = fromTimeLineToComponent + .get(l); + return resourceLoadComponent; + } + + public void expand(LimitingResourceQueue line, + List closed) { + } + + @Override + public void afterCompose() { + super.afterCompose(); + for (LimitingResourcesComponent each : limitingResourcesComponents) { + each.afterCompose(); + } + } +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java new file mode 100644 index 000000000..4fc6e9a5b --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/LimitingResourcesPanel.java @@ -0,0 +1,262 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import static org.zkoss.ganttz.i18n.I18nHelper._; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.navalplanner.business.resources.daos.IResourceDAO; +import org.navalplanner.business.resources.entities.LimitingResourceQueue; +import org.springframework.beans.factory.annotation.Autowired; +import org.zkoss.ganttz.timetracker.TimeTracker; +import org.zkoss.ganttz.timetracker.TimeTrackerComponent; +import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.ganttz.util.ComponentsFinder; +import org.zkoss.ganttz.util.MutableTreeModel; +import org.zkoss.ganttz.util.OnZKDesktopRegistry; +import org.zkoss.ganttz.util.script.IScriptsRegister; +import org.zkoss.zk.au.out.AuInvoke; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.HtmlMacroComponent; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; +import org.zkoss.zul.Button; +import org.zkoss.zul.Div; +import org.zkoss.zul.ListModel; +import org.zkoss.zul.Listbox; +import org.zkoss.zul.Separator; +import org.zkoss.zul.SimpleListModel; + +public class LimitingResourcesPanel extends HtmlMacroComponent { + + public interface IToolbarCommand { + public void doAction(); + + public String getLabel(); + + public String getImage(); + } + + private TimeTrackerComponent timeTrackerComponent; + + private LimitingResourcesLeftPane leftPane; + + private LimitingResourcesList limitingResourcesList; + + private List limitingResourceQueues = new ArrayList(); + + private MutableTreeModel treeModel; + + private TimeTracker timeTracker; + + // private LimitingDependencyList dependencyList; + + // private WeakReferencedListeners zoomListeners = + // WeakReferencedListeners.create(); + + private Listbox listZoomLevels; + + @Autowired + IResourceDAO resourcesDAO; + + private static final String filterResources = _("Filter by resources"); + private static final String filterCriterions = _("Filter by criterions"); + private boolean filterbyResources = true; + + public LimitingResourcesPanel(List groups, + TimeTracker timeTracker) { + init(groups, timeTracker); + + } + + public void init(List groups, TimeTracker timeTracker) { + this.limitingResourceQueues = groups; + this.timeTracker = timeTracker; + treeModel = createModelForTree(); + timeTrackerComponent = timeTrackerForResourcesLoadPanel(timeTracker); + limitingResourcesList = new LimitingResourcesList(timeTracker, + treeModel); + leftPane = new LimitingResourcesLeftPane(treeModel, + limitingResourcesList); + registerNeededScripts(); + } + + public ListModel getFilters() { + String[] filters = new String[] { filterResources, filterCriterions }; + return new SimpleListModel(filters); + } + + public void setFilter(String filterby) { + if (filterby.equals(filterResources)) { + this.filterbyResources = true; + } else { + this.filterbyResources = false; + } + } + + public boolean getFilter() { + return filterbyResources; + } + + public ListModel getZoomLevels() { + return new SimpleListModel(ZoomLevel.values()); + } + + public void setZoomLevel(final ZoomLevel zoomLevel) { + timeTracker.setZoomLevel(zoomLevel); + } + + public void zoomIncrease() { + timeTracker.zoomIncrease(); + } + + public void zoomDecrease() { + timeTracker.zoomDecrease(); + } + + public void add(final IToolbarCommand... commands) { + Component toolbar = getToolbar(); + Separator separator = getSeparator(); + for (IToolbarCommand c : commands) { + toolbar.insertBefore(asButton(c), separator); + } + } + + private Button asButton(final IToolbarCommand c) { + Button result = new Button(); + result.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + c.doAction(); + } + }); + if (!StringUtils.isEmpty(c.getImage())) { + result.setImage(c.getImage()); + result.setTooltiptext(c.getLabel()); + } else { + result.setLabel(c.getLabel()); + } + return result; + } + + @SuppressWarnings("unchecked") + private Separator getSeparator() { + List children = getToolbar().getChildren(); + Separator separator = ComponentsFinder.findComponentsOfType( + Separator.class, children).get(0); + return separator; + } + + private Component getToolbar() { + Component toolbar = getFellow("toolbar"); + return toolbar; + } + + private void registerNeededScripts() { + // getScriptsRegister().register(ScriptsRequiredByResourceLoadPanel.class); + } + + private IScriptsRegister getScriptsRegister() { + return OnZKDesktopRegistry.getLocatorFor(IScriptsRegister.class) + .retrieve(); + } + + private MutableTreeModel createModelForTree() { + MutableTreeModel result = MutableTreeModel + .create(LimitingResourceQueue.class); + for (LimitingResourceQueue LimitingResourceQueue : this.limitingResourceQueues) { + result.addToRoot(LimitingResourceQueue); + } + return result; + } + + + private TimeTrackerComponent timeTrackerForResourcesLoadPanel( + TimeTracker timeTracker) { + return new TimeTrackerComponent(timeTracker) { + @Override + protected void scrollHorizontalPercentage(int pixelsDisplacement) { + response("", new AuInvoke(limitingResourcesList, + "adjustScrollHorizontalPosition", pixelsDisplacement + + "")); + } + }; + } + + @Override + public void afterCompose() { + + super.afterCompose(); + + // Insert resourcesList left pane component + getFellow("insertionPointLeftPanel").appendChild(leftPane); + leftPane.afterCompose(); + + // Insert timetracker watermarks and limitingResourcesQueues + getFellow("insertionPointRightPanel").appendChild(timeTrackerComponent); + getFellow("insertionPointRightPanel") + .appendChild(limitingResourcesList); + limitingResourcesList.afterCompose(); + + Div source = new Div(); + Div destination = new Div(); + + LimitingDependencyComponent limitingDependencyComponent = new LimitingDependencyComponent( + source, destination); + + LimitingDependencyList dependencyList = new LimitingDependencyList(null); + dependencyList.addDependencyComponent(limitingDependencyComponent); + + getFellow("insertionPointRightPanel").appendChild(dependencyList); + + dependencyList.afterCompose(); + + // Insert timetracker headers + TimeTrackerComponent timeTrackerHeader = createTimeTrackerHeader(); + getFellow("insertionPointTimetracker").appendChild(timeTrackerHeader); + + timeTrackerHeader.afterCompose(); + timeTrackerComponent.afterCompose(); + listZoomLevels = (Listbox) getFellow("listZoomLevels"); + listZoomLevels.setSelectedIndex(timeTracker.getDetailLevel().ordinal()); + } + + public void clearComponents() { + getFellow("insertionPointLeftPanel").getChildren().clear(); + getFellow("insertionPointRightPanel").getChildren().clear(); + getFellow("insertionPointTimetracker").getChildren().clear(); + } + + private TimeTrackerComponent createTimeTrackerHeader() { + return new TimeTrackerComponent( + timeTracker) { + + @Override + protected void scrollHorizontalPercentage(int pixelsDisplacement) { + } + }; + } + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTaskGenerator.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTaskGenerator.java new file mode 100644 index 000000000..7247617c1 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/limitingresources/QueueTaskGenerator.java @@ -0,0 +1,365 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.limitingresources; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.Validate; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.joda.time.LocalDate; +import org.navalplanner.business.planner.entities.DayAssignment; +import org.navalplanner.business.planner.entities.GenericResourceAllocation; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.SpecificDayAssignment; +import org.navalplanner.business.resources.daos.IResourceDAO; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.CriterionCompounder; +import org.navalplanner.business.resources.entities.ICriterion; +import org.navalplanner.business.resources.entities.Resource; +import org.zkoss.ganttz.data.limitingresource.QueueTask; +import org.zkoss.ganttz.data.resourceload.LoadLevel; + +interface QueueTaskGeneratorFactory { + QueueTaskGenerator create(ResourceAllocation allocation); +} + + +abstract class QueueTaskGenerator { + + private static final Log LOG = LogFactory.getLog(QueueTaskGenerator.class); + + public static QueueTaskGeneratorFactory onResource(Resource resource) { + return new OnResourceFactory(resource); + } + + public static QueueTaskGeneratorFactory onResourceSatisfying( + Resource resource, Collection criterions) { + return new OnResourceFactory(resource, criterions); + } + + private static class OnResourceFactory implements + QueueTaskGeneratorFactory { + + private final Resource resource; + + private final ICriterion criterion; + + public OnResourceFactory(Resource resource) { + this(resource, Collections. emptyList()); + } + + public OnResourceFactory(Resource resource, + Collection criterionsToSatisfy) { + Validate.notNull(resource); + this.resource = resource; + this.criterion = CriterionCompounder.buildAnd(criterionsToSatisfy) + .getResult(); + } + + @Override + public QueueTaskGenerator create(ResourceAllocation allocation) { + return new QueueTaskGeneratorOnResource(resource, allocation, + criterion); + } + + } + + public static QueueTaskGeneratorFactory onCriterion( + final Criterion criterion, final IResourceDAO resourcesDAO) { + return new QueueTaskGeneratorFactory() { + + @Override + public QueueTaskGenerator create(ResourceAllocation allocation) { + return new QueueTaskGeneratorOnCriterion(criterion, + allocation, findResources(criterion, resourcesDAO)); + } + + private List findResources(final Criterion criterion, + final IResourceDAO resourcesDAO) { + return resourcesDAO + .findSatisfyingCriterionsAtSomePoint(Collections + .singletonList(criterion)); + } + }; + } + + protected final LocalDate start; + protected final LocalDate end; + + private List> allocationsOnInterval = new ArrayList>(); + + protected QueueTaskGenerator(LocalDate start, + LocalDate end, List> allocationsOnInterval) { + Validate.notNull(start); + Validate.notNull(end); + Validate.notNull(allocationsOnInterval); + this.start = start; + this.end = end; + this.allocationsOnInterval = ResourceAllocation + .getSatisfied(allocationsOnInterval); + } + + public List join(QueueTaskGenerator next) { + if (!overlaps(next)) { + return stripEmpty(this, next); + } + if (isIncluded(next)) { + return stripEmpty(this.until(next.start), intersect(next), this + .from(next.end)); + } + assert overlaps(next) && !isIncluded(next); + return stripEmpty(this.until(next.start), intersect(next), next + .from(end)); + } + + protected List> getAllocationsOnInterval() { + return allocationsOnInterval; + } + + private List stripEmpty( + QueueTaskGenerator... generators) { + List result = new ArrayList(); + for (QueueTaskGenerator loadPeriodGenerator : generators) { + if (!loadPeriodGenerator.isEmpty()) { + result.add(loadPeriodGenerator); + } + } + return result; + } + + private boolean isEmpty() { + return start.equals(end); + } + + protected abstract QueueTaskGenerator create(LocalDate start, + LocalDate end, List> allocationsOnInterval); + + private QueueTaskGenerator intersect(QueueTaskGenerator other) { + return create(max(this.start, other.start), + min(this.end, other.end), plusAllocations(other)); + } + + private static LocalDate max(LocalDate l1, LocalDate l2) { + return l1.compareTo(l2) < 0 ? l2 : l1; + } + + private static LocalDate min(LocalDate l1, LocalDate l2) { + return l1.compareTo(l2) < 0 ? l1 : l2; + } + + private List> plusAllocations( + QueueTaskGenerator other) { + List> result = new ArrayList>(); + result.addAll(allocationsOnInterval); + result.addAll(other.allocationsOnInterval); + return result; + } + + private QueueTaskGenerator from(LocalDate newStart) { + return create(newStart, end, + allocationsOnInterval); + } + + private QueueTaskGenerator until(LocalDate newEnd) { + return create(start, newEnd, + allocationsOnInterval); + } + + boolean overlaps(QueueTaskGenerator other) { + return (start.compareTo(other.end) < 0 && other.start + .compareTo(this.end) < 0); + } + + private boolean isIncluded(QueueTaskGenerator other) { + return other.start.compareTo(start) >= 0 + && other.end.compareTo(end) <= 0; + } + + public QueueTask build() { + return new QueueTask(start, end, getTotalWorkHours(), + getHoursAssigned(), new LoadLevel( + calculateLoadPercentage())); + } + + protected abstract int getTotalWorkHours(); + + private int calculateLoadPercentage() { + final int totalResourceWorkHours = getTotalWorkHours(); + int assigned = getHoursAssigned(); + if (totalResourceWorkHours == 0) { + return assigned == 0 ? 0 : Integer.MAX_VALUE; + } + double proportion = assigned / (double) totalResourceWorkHours; + return new BigDecimal(proportion).scaleByPowerOfTen(2).intValue(); + } + + protected abstract int getHoursAssigned(); + + protected final int sumAllocations() { + int sum = 0; + for (ResourceAllocation resourceAllocation : allocationsOnInterval) { + sum += getAssignedHoursFor(resourceAllocation); + } + return sum; + } + + protected abstract int getAssignedHoursFor( + ResourceAllocation resourceAllocation); + + public LocalDate getStart() { + return start; + } + + public LocalDate getEnd() { + return end; + } +} + +class QueueTaskGeneratorOnResource extends QueueTaskGenerator { + + private Resource resource; + + private final ICriterion criterion; + + QueueTaskGeneratorOnResource(Resource resource, LocalDate start, + LocalDate end, List> allocationsOnInterval, + ICriterion criterion) { + super(start, end, allocationsOnInterval); + this.resource = resource; + this.criterion = criterion; + } + + QueueTaskGeneratorOnResource(Resource resource, + ResourceAllocation initial, ICriterion criterion) { + super(initial.getStartDate(), initial.getEndDate(), Arrays.> asList(initial)); + this.resource = resource; + this.criterion = criterion; + } + + @Override + protected QueueTaskGenerator create(LocalDate start, LocalDate end, + List> allocationsOnInterval) { + return new QueueTaskGeneratorOnResource(resource, start, end, + allocationsOnInterval, criterion); + } + + @Override + protected int getTotalWorkHours() { + return resource.getTotalWorkHours(start, end, criterion); + } + + @Override + protected int getAssignedHoursFor(ResourceAllocation resourceAllocation) { + return resourceAllocation.getAssignedHours(resource, start, end); + } + + @Override + protected int getHoursAssigned() { + return sumAllocations(); + } + +} + +class QueueTaskGeneratorOnCriterion extends QueueTaskGenerator { + + private final Criterion criterion; + private final List resourcesSatisfyingCriterionAtSomePoint; + + public QueueTaskGeneratorOnCriterion(Criterion criterion, + ResourceAllocation allocation, + List resourcesSatisfyingCriterionAtSomePoint) { + this(criterion, allocation.getStartDate(), allocation.getEndDate(), + Arrays.> asList(allocation), + resourcesSatisfyingCriterionAtSomePoint); + } + + public QueueTaskGeneratorOnCriterion(Criterion criterion, + LocalDate startDate, LocalDate endDate, + List> allocations, + List resourcesSatisfyingCriterionAtSomePoint) { + super(startDate, endDate, allocations); + this.criterion = criterion; + this.resourcesSatisfyingCriterionAtSomePoint = resourcesSatisfyingCriterionAtSomePoint; + } + + @Override + protected QueueTaskGenerator create(LocalDate start, LocalDate end, + List> allocationsOnInterval) { + QueueTaskGeneratorOnCriterion result = new QueueTaskGeneratorOnCriterion( + criterion, start, end, allocationsOnInterval, + resourcesSatisfyingCriterionAtSomePoint); + result.specificByResourceCached = specificByResourceCached; + return result; + } + + private List genericAllocationsOnInterval() { + return ResourceAllocation.getOfType(GenericResourceAllocation.class, + getAllocationsOnInterval()); + } + + @Override + protected int getAssignedHoursFor(ResourceAllocation resourceAllocation) { + return resourceAllocation.getAssignedHours(start, end); + } + + @Override + protected int getTotalWorkHours() { + int sum = 0; + for (Resource resource : resourcesSatisfyingCriterionAtSomePoint) { + sum += resource.getTotalWorkHours(start, end, criterion); + } + return sum; + } + + @Override + protected int getHoursAssigned() { + return sumAllocations(); + } + + private Map> specificByResourceCached = new HashMap>(); + + private List getSpecificOrderedAssignmentsFor( + Resource resource) { + if (!specificByResourceCached.containsKey(resource)) { + specificByResourceCached.put(resource, DayAssignment + .specific(DayAssignment.orderedByDay(resource + .getAssignments()))); + } + return specificByResourceCached.get(resource); + } + + private int sum(List specific) { + int result = 0; + for (SpecificDayAssignment s : specific) { + result += s.getHours(); + } + return result; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IManageOrderElementAdvancesModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IManageOrderElementAdvancesModel.java index a92208fe5..7acbbbe40 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IManageOrderElementAdvancesModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/IManageOrderElementAdvancesModel.java @@ -62,7 +62,7 @@ public interface IManageOrderElementAdvancesModel { public boolean isReadOnlyAdvanceMeasurements(); - public void cleanAdvance(); + public void cleanAdvance(DirectAdvanceAssignment advance); public boolean isPrecisionValid(AdvanceMeasurement advanceMeasurement); @@ -99,4 +99,13 @@ public interface IManageOrderElementAdvancesModel { public boolean isQualityForm(AdvanceAssignment advance); public boolean lessThanPreviousMeasurements(); + + public boolean hasConsolidatedAdvances(AdvanceAssignment advance); + + public boolean hasConsolidatedAdvances(AdvanceMeasurement advanceMeasurement); + + public boolean findIndirectConsolidation( + AdvanceMeasurement advanceMeasurement); + + public void resetAdvanceAssignment(); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesController.java index d509dd2fb..1bacef8a5 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesController.java @@ -29,6 +29,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.joda.time.LocalDate; @@ -164,17 +165,33 @@ public class ManageOrderElementAdvancesController extends return orderElementModel.getOrderElement(); } + private void resetAdvancesGrid() { + manageOrderElementAdvancesModel.resetAdvanceAssignment(); + this.indexSelectedItem = -1; + reloadAdvances(); + } + + private void reloadAdvances() { + Util.reloadBindings(self); + if (indexSelectedItem > -1) { + editAdvances.setSelectedItem(editAdvances + .getItemAtIndex(indexSelectedItem)); + editAdvances.invalidate(); + } + } + private Listbox editAdvances; public void prepareEditAdvanceMeasurements(Listitem selectedItem) { AdvanceAssignment advanceAssignment = (AdvanceAssignment) selectedItem .getValue(); if (advanceAssignment.getAdvanceType() != null) { - validateListAdvanceMeasurement(); - manageOrderElementAdvancesModel + validateListAdvanceMeasurement(); + manageOrderElementAdvancesModel .prepareEditAdvanceMeasurements(advanceAssignment); - this.indexSelectedItem = editAdvances.getIndexOfItem(editAdvances.getSelectedItem()); - Util.reloadBindings(self); + this.indexSelectedItem = editAdvances.getIndexOfItem(editAdvances + .getSelectedItem()); + reloadAdvances(); } else { Component comboAdvanceType = selectedItem.getFirstChild() .getFirstChild(); @@ -187,14 +204,12 @@ public class ManageOrderElementAdvancesController extends public void goToCreateLineAdvanceAssignment() { manageOrderElementAdvancesModel.addNewLineAdvaceAssignment(); - manageOrderElementAdvancesModel.prepareEditAdvanceMeasurements(null); - this.indexSelectedItem = -1; - Util.reloadBindings(self); + resetAdvancesGrid(); } public void goToCreateLineAdvanceMeasurement() { manageOrderElementAdvancesModel.addNewLineAdvaceMeasurement(); - Util.reloadBindings(self); + reloadAdvances(); } public void goToRemoveLineAdvanceAssignment(){ @@ -204,7 +219,7 @@ public class ManageOrderElementAdvancesController extends .getValue(); manageOrderElementAdvancesModel .removeLineAdvanceAssignment(advanceAssignment); - Util.reloadBindings(self); + reloadAdvances(); } } @@ -218,7 +233,7 @@ public class ManageOrderElementAdvancesController extends if (advanceMeasurement != null) { manageOrderElementAdvancesModel .removeLineAdvanceMeasurement(advanceMeasurement); - Util.reloadBindings(self); + reloadAdvances(); } } } @@ -306,8 +321,9 @@ public class ManageOrderElementAdvancesController extends @Override public void onEvent(Event event) throws Exception { setMaxValue(listItem,comboAdvanceTypes); - cleanFields(); + cleanFields(advance); setPercentage(); + resetAdvancesGrid(); } }); @@ -351,7 +367,7 @@ public class ManageOrderElementAdvancesController extends private void appendDecimalBoxMaxValue(final Listitem listItem, boolean isQualityForm) { - AdvanceAssignment advanceAssignment = (AdvanceAssignment) listItem + final AdvanceAssignment advanceAssignment = (AdvanceAssignment) listItem .getValue(); Decimalbox maxValue = new Decimalbox(); maxValue.setScale(2); @@ -379,15 +395,23 @@ public class ManageOrderElementAdvancesController extends @Override public void set(BigDecimal value) { - directAdvanceAssignment.setMaxValue(value); + if (!manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advanceAssignment)) { + directAdvanceAssignment.setMaxValue(value); + } } }); maxValue.addEventListener(Events.ON_CHANGE, new EventListener() { @Override public void onEvent(Event event) throws Exception { - setPercentage(); - Util.reloadBindings(self); + if (manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advanceAssignment)) { + showMessagesConsolidation(1); + } else { + setPercentage(); + reloadAdvances(); + } } }); @@ -531,7 +555,7 @@ public class ManageOrderElementAdvancesController extends } else { selectedAdvances.remove(advance); } - Util.reloadBindings(self); + reloadAdvances(); } }); @@ -548,9 +572,18 @@ public class ManageOrderElementAdvancesController extends removeButton.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - manageOrderElementAdvancesModel + if (manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advance)) { + showMessagesConsolidation(1); + } else { + manageOrderElementAdvancesModel .removeLineAdvanceAssignment(advance); - Util.reloadBindings(self); + if (indexSelectedItem == editAdvances + .getIndexOfItem(listItem)) { + indexSelectedItem = -1; + } + reloadAdvances(); + } } }); @@ -589,12 +622,14 @@ public class ManageOrderElementAdvancesController extends Listitem item = (Listitem) comp.getParent().getParent(); DirectAdvanceAssignment advance = (DirectAdvanceAssignment) item .getValue(); - if (value == null) { - ((Decimalbox) comp).setValue(advance.getMaxValue()); - ((Decimalbox) comp).invalidate(); - throw new WrongValueException( - comp, - _("The max value must be not empty")); + if (!manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advance)) { + if (value == null) { + ((Decimalbox) comp).setValue(advance.getMaxValue()); + ((Decimalbox) comp).invalidate(); + throw new WrongValueException(comp, + _("The max value must be not empty")); + } } } }; @@ -651,7 +686,6 @@ public class ManageOrderElementAdvancesController extends ((Decimalbox) value.getFirstChild()).invalidate(); } } - } private Chart chart; @@ -695,29 +729,47 @@ public class ManageOrderElementAdvancesController extends } } - private void cleanFields(){ - this.manageOrderElementAdvancesModel.cleanAdvance(); - Util.reloadBindings(self); + private void cleanFields(DirectAdvanceAssignment advance) { + this.manageOrderElementAdvancesModel + .cleanAdvance((DirectAdvanceAssignment) advance); } private void setReportGlobalAdvance(final Listitem item){ - for(int i=0; i< editAdvances.getChildren().size(); i++){ - if(editAdvances.getChildren().get(i) instanceof Listitem){ - Listitem listItem = (Listitem) editAdvances.getChildren().get(i); - Listcell celdaSpread = (Listcell) listItem.getChildren().get(5); - Radio radioSpread = ((Radio)celdaSpread.getFirstChild()); - if(!radioSpread.isDisabled()){ - radioSpread.setChecked(false); - ((AdvanceAssignment) listItem.getValue()) - .setReportGlobalAdvance(false); + boolean spread = true; + if (!radioSpreadIsConsolidated()) { + for (int i = 0; i < editAdvances.getChildren().size(); i++) { + if (editAdvances.getChildren().get(i) instanceof Listitem) { + Listitem listItem = (Listitem) editAdvances.getChildren() + .get(i); + Listcell celdaSpread = (Listcell) listItem.getChildren() + .get(5); + Radio radioSpread = ((Radio) celdaSpread.getFirstChild()); + if (!radioSpread.isDisabled()) { + radioSpread.setChecked(false); + ((AdvanceAssignment) listItem.getValue()) + .setReportGlobalAdvance(false); + } } } + } else { + spread = false; } Listcell celdaSpread = (Listcell) item.getChildren().get(5); - ((Radio)celdaSpread.getFirstChild()).setChecked(true); - ((AdvanceAssignment) item.getValue()).setReportGlobalAdvance(true); + ((Radio) celdaSpread.getFirstChild()).setChecked(spread); + ((AdvanceAssignment) item.getValue()).setReportGlobalAdvance(spread); } + private boolean radioSpreadIsConsolidated() { + for (AdvanceAssignment advance : getAdvanceAssignments()) { + if ((advance.getReportGlobalAdvance()) + && (manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advance))) { + showMessagesConsolidation(1); + return true; + } + } + return false; + } private boolean validateDataForm(){ return ((validateListAdvanceAssignment()) @@ -845,7 +897,12 @@ public class ManageOrderElementAdvancesController extends @Override public void onEvent(Event event) throws Exception { - updatesValue(value); + if (manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advanceMeasurement)) { + showMessagesConsolidation(2); + } else { + updatesValue(value); + } } }); @@ -859,9 +916,11 @@ public class ManageOrderElementAdvancesController extends @Override public void set(BigDecimal value) { - advanceMeasurement.setValue(value); - Util.reloadBindings(chart); - Util.reloadBindings(editAdvances); + if (!manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advanceMeasurement)) { + advanceMeasurement.setValue(value); + reloadAdvances(); + } } }); value.setConstraint(checkValidValue()); @@ -883,7 +942,7 @@ public class ManageOrderElementAdvancesController extends private void appendDateboxDate(final Listitem listitem) { final AdvanceMeasurement advanceMeasurement = (AdvanceMeasurement) listitem .getValue(); - Datebox date = new Datebox(); + final Datebox date = new Datebox(); Listcell listcell = new Listcell(); listcell.appendChild(date); @@ -894,7 +953,12 @@ public class ManageOrderElementAdvancesController extends @Override public void onEvent(Event event) throws Exception { - setCurrentDate(listitem); + if (manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advanceMeasurement)) { + showMessagesConsolidation(2); + } else { + setCurrentDate(listitem); + } } }); @@ -912,10 +976,13 @@ public class ManageOrderElementAdvancesController extends @Override public void set(Date value) { - advanceMeasurement.setDate(new LocalDate(value)); - manageOrderElementAdvancesModel + if (!manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advanceMeasurement)) { + advanceMeasurement.setDate(new LocalDate(value)); + manageOrderElementAdvancesModel .sortListAdvanceMeasurement(); - Util.reloadBindings(self); + reloadAdvances(); + } } }); date.setConstraint(checkValidDate()); @@ -927,7 +994,9 @@ public class ManageOrderElementAdvancesController extends public void validate(Component comp, Object value) throws WrongValueException { AdvanceMeasurement advanceMeasurement = getAdvanceMeasurementByComponent(comp); - if (advanceMeasurement != null) { + if ((advanceMeasurement != null) + && (!manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advanceMeasurement))) { advanceMeasurement.setValue((BigDecimal) value); if (((BigDecimal) value) == null) { throw new WrongValueException( @@ -977,29 +1046,32 @@ public class ManageOrderElementAdvancesController extends public void validate(Component comp, Object value) throws WrongValueException { AdvanceMeasurement advanceMeasurement = getAdvanceMeasurementByComponent(comp); - if (((Date) value) == null) { - advanceMeasurement.setDate(null); - throw new WrongValueException( - comp, - _("The date is not valid, the date must be not empty")); - } else { - if (!manageOrderElementAdvancesModel - .isDistinctValidDate((Date) value, - advanceMeasurement)) { + if ((!manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advanceMeasurement))) { + if (((Date) value) == null) { + advanceMeasurement.setDate(null); throw new WrongValueException( comp, - _("The date is not valid, the date must be unique for this advanced assignment")); - } - if (advanceMeasurement != null) { - advanceMeasurement.setDate(new LocalDate( - (Date) value)); - manageOrderElementAdvancesModel - .sortListAdvanceMeasurement(); - if (manageOrderElementAdvancesModel - .lessThanPreviousMeasurements()) { + _("The date is not valid, the date must be not empty")); + } else { + if (!manageOrderElementAdvancesModel + .isDistinctValidDate((Date) value, + advanceMeasurement)) { throw new WrongValueException( comp, - _("Value is not valid, the value must be greater than the value of the previous advances.")); + _("The date is not valid, the date must be unique for this advanced assignment")); + } + if (advanceMeasurement != null) { + advanceMeasurement.setDate(new LocalDate( + (Date) value)); + manageOrderElementAdvancesModel + .sortListAdvanceMeasurement(); + if (manageOrderElementAdvancesModel + .lessThanPreviousMeasurements()) { + throw new WrongValueException( + comp, + _("Value is not valid, the value must be greater than the value of the previous advances.")); + } } } } @@ -1016,9 +1088,14 @@ public class ManageOrderElementAdvancesController extends removeButton.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - manageOrderElementAdvancesModel + if (manageOrderElementAdvancesModel + .hasConsolidatedAdvances(advance)) { + showMessagesConsolidation(2); + } else { + manageOrderElementAdvancesModel .removeLineAdvanceMeasurement(advance); - Util.reloadBindings(self); + reloadAdvances(); + } } }); @@ -1047,4 +1124,19 @@ public class ManageOrderElementAdvancesController extends manageOrderElementAdvancesModel.refreshChangesFromOrderElement(); } + private void showMessagesConsolidation(int opcion) { + String message = ""; + switch (opcion) { + case 1: + message = _("This advance can not be changed or removed, because it has got consolidated advances. It is needed removing the consolidation on all its advances."); + break; + case 2: + message = _("This advance measurement can not be changed or removed, because it is consolidated. It is needed removing its consolidation."); + break; + } + if (!StringUtils.isBlank(message)) { + messagesForUser.showMessage(Level.ERROR, message); + } + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesModel.java index d8ac6707a..2552c3a2e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/ManageOrderElementAdvancesModel.java @@ -28,6 +28,8 @@ import java.math.RoundingMode; import java.util.ArrayList; import java.util.Collections; import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.SortedSet; @@ -51,6 +53,8 @@ import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.orders.daos.IOrderElementDAO; import org.navalplanner.business.orders.entities.OrderElement; import org.navalplanner.business.orders.entities.OrderLineGroup; +import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidatedValue; +import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -176,10 +180,22 @@ public class ManageOrderElementAdvancesModel implements private void forceLoadAdvanceAssignmentsAndMeasurements() { for (DirectAdvanceAssignment each : orderElement .getDirectAdvanceAssignments()) { - each.getAdvanceMeasurements().size(); + forceLoadAdvanceConsolidatedValues(each); + each.getNonCalculatedConsolidation().size(); } if (orderElement instanceof OrderLineGroup) { ((OrderLineGroup) orderElement).getIndirectAdvanceAssignments().size(); + for (IndirectAdvanceAssignment each : ((OrderLineGroup) orderElement) + .getIndirectAdvanceAssignments()) { + each.getCalculatedConsolidation().size(); + } + } + } + + private void forceLoadAdvanceConsolidatedValues( + DirectAdvanceAssignment advance) { + for (AdvanceMeasurement measurement : advance.getAdvanceMeasurements()) { + measurement.getNonCalculatedConsolidatedValues().size(); } } @@ -302,18 +318,18 @@ public class ManageOrderElementAdvancesModel implements } @Override - public void cleanAdvance(){ - if (this.advanceAssignment != null) { - this.advanceAssignment.setReportGlobalAdvance(false); - List listAdvanceMeasurements = new ArrayList( - this.advanceAssignment.getAdvanceMeasurements()); - for (AdvanceMeasurement advanceMeasurement : listAdvanceMeasurements) { - advanceMeasurement.setValue(BigDecimal.ZERO); - advanceMeasurement.setDate(null); - } + public void cleanAdvance(DirectAdvanceAssignment advanceAssignment) { + if (advanceAssignment != null) { + advanceAssignment.setReportGlobalAdvance(false); + advanceAssignment.getAdvanceMeasurements().clear(); } } + @Override + public void resetAdvanceAssignment() { + this.advanceAssignment = null; + } + @Override @Transactional(readOnly = true) public void confirmSave() throws InstanceNotFoundException, @@ -561,6 +577,50 @@ public class ManageOrderElementAdvancesModel implements return xymodel; } + @Override + @Transactional(readOnly = true) + public boolean hasConsolidatedAdvances(AdvanceAssignment advance) { + if (advance instanceof DirectAdvanceAssignment) { + if ((advance.getReportGlobalAdvance()) + && (!((DirectAdvanceAssignment) advance) + .getNonCalculatedConsolidation().isEmpty())) { + return true; + } + return (!((DirectAdvanceAssignment) advance).isFake()) + && (containsAnyConsolidatedAdvanceMeasurement((DirectAdvanceAssignment) advance)); + + } else { + return ((advance.getReportGlobalAdvance()) && (!((IndirectAdvanceAssignment) advance) + .getCalculatedConsolidation().isEmpty())); + } + } + + private boolean containsAnyConsolidatedAdvanceMeasurement( + DirectAdvanceAssignment advance) { + Iterator iterator = advance + .getAdvanceMeasurements().iterator(); + while (iterator.hasNext()) { + if (hasConsolidatedAdvances(iterator.next())) { + return true; + } + } + return false; + } + + @Transactional(readOnly = true) + public boolean hasConsolidatedAdvances( + AdvanceMeasurement advanceMeasurement) { + + if (advanceMeasurement.isNewObject() || isIndirectAdvanceAssignment) { + return false; + } + + if (!advanceMeasurement.getNonCalculatedConsolidatedValues().isEmpty()) { + return true; + } + return findIndirectConsolidation(advanceMeasurement); + } + @Override @Transactional(readOnly = true) public boolean isQualityForm(AdvanceAssignment advance) { @@ -569,4 +629,69 @@ public class ManageOrderElementAdvancesModel implements return advanceType.isQualityForm(); } + @Override + @Transactional(readOnly = true) + public boolean findIndirectConsolidation( + AdvanceMeasurement advanceMeasurement) { + + AdvanceAssignment advance = advanceMeasurement.getAdvanceAssignment(); + if ((orderElement != null) && (orderElement.getParent() != null) && (advance instanceof DirectAdvanceAssignment)) { + + List types = new ArrayList(); + + types.add(advance.getAdvanceType().getUnitName()); + if (advance.getReportGlobalAdvance()) { + types.add(PredefinedAdvancedTypes.CHILDREN.getTypeName()); + } + + Set indirects = getSpreadIndirectAdvanceAssignmentWithSameType( + orderElement, types); + + for (IndirectAdvanceAssignment indirect : indirects) { + if (findConsolidatedAdvance(indirect + .getCalculatedConsolidation(), advanceMeasurement)) { + return true; + } + } + } + return false; + } + + private Set getSpreadIndirectAdvanceAssignmentWithSameType( + OrderElement orderElement, List types) { + + orderElementDAO.reattach(orderElement); + Set result = new HashSet(); + + for (IndirectAdvanceAssignment indirect : orderElement + .getIndirectAdvanceAssignments()) { + if ((indirect.getReportGlobalAdvance()) + && (types.contains(indirect.getAdvanceType().getUnitName()))) { + result.add(indirect); + } + } + + OrderElement parent = orderElement.getParent(); + if (parent != null) { + result.addAll(getSpreadIndirectAdvanceAssignmentWithSameType( + parent, types)); + } + + return result; + } + + private boolean findConsolidatedAdvance( + Set consolidations, + AdvanceMeasurement advance) { + for (CalculatedConsolidation consolidation : consolidations) { + for (CalculatedConsolidatedValue value : consolidation + .getCalculatedConsolidatedValues()) { + if (value.getDate().compareTo(advance.getDate()) == 0) { + return true; + } + } + } + return false; + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementTreeController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementTreeController.java index 0b4dc801f..41d94c102 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementTreeController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/orders/OrderElementTreeController.java @@ -410,7 +410,7 @@ public class OrderElementTreeController extends TreeController { InputElement codeBox = (InputElement) ((Treecell)row.getChildren().get(1)).getChildren().get(0); InputElement nameBox = (InputElement) - ((Treecell)row.getChildren().get(2)).getChildren().get(1); + ((Treecell)row.getChildren().get(2)).getChildren().get(0); InputElement hoursBox = (InputElement) ((Treecell)row.getChildren().get(3)).getChildren().get(0); InputElement initDateBox = (InputElement) @@ -423,30 +423,13 @@ public class OrderElementTreeController extends TreeController { @Override protected void addDescriptionCell(OrderElement element) { - addTaskNumberCell(element); + addTaskNameCell(element); } - private void addTaskNumberCell(final OrderElement orderElementForThisRow) { + private void addTaskNameCell(final OrderElement orderElementForThisRow) { int[] path = getModel().getPath(orderElementForThisRow); String cssClass = "depth_" + path.length; - Label taskNumber = new Label(pathAsString(path)); - taskNumber.setSclass("tasknumber"); - taskNumber.addEventListener(Events.ON_DOUBLE_CLICK, - new EventListener() { - - @Override - public void onEvent(Event event) throws Exception { - IOrderElementModel model = orderModel - .getOrderElementModel(orderElementForThisRow); - orderElementController.openWindow(model); - // Util.reloadBindings(tree); - } - - }); - - // TODO It would be needed to expand the width for the numbers - // to make it ready for 2 and 3 digit numbers Textbox textBox = Util.bind(new Textbox(), new Util.Getter() { @@ -464,7 +447,7 @@ public class OrderElementTreeController extends TreeController { if (readOnly) { textBox.setDisabled(true); } - addCell(cssClass, taskNumber, textBox); + addCell(cssClass, textBox); registerKeyboardListener(textBox); } @@ -584,6 +567,7 @@ public class OrderElementTreeController extends TreeController { notifyDateboxCantBeCreated(dateboxName, currentOrderElement .getCode()); } + registerFocusEvent(dinamicDatebox.getDateTextBox()); addCell(cell); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRow.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRow.java index efa5aff8d..88cff794e 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRow.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/AllocationRow.java @@ -266,7 +266,12 @@ public abstract class AllocationRow { public void setResourcesPerDay(ResourcesPerDay resourcesPerDay) { this.resourcesPerDay = resourcesPerDay; - resourcesPerDayInput.setValue(this.resourcesPerDay.getAmount()); + resourcesPerDayInput.setValue(getAmount(resourcesPerDay)); + } + + private BigDecimal getAmount(ResourcesPerDay resourcesPerDay) { + return (resourcesPerDay != null) ? resourcesPerDay.getAmount() + : new BigDecimal(0); } public void setTemporal(ResourceAllocation last) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ILimitingResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ILimitingResourceAllocationModel.java index 852cd5876..336c51db4 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ILimitingResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ILimitingResourceAllocationModel.java @@ -24,7 +24,6 @@ import java.util.List; import org.navalplanner.business.orders.entities.AggregatedHoursGroup; import org.navalplanner.business.planner.entities.Task; -import org.navalplanner.web.planner.allocation.LimitingResourceAllocationModel.LimitingResourceAllocationRow; /** * Contract for {@link Task}. @@ -33,14 +32,14 @@ import org.navalplanner.web.planner.allocation.LimitingResourceAllocationModel.L */ public interface ILimitingResourceAllocationModel extends INewAllocationsAdder { + void confirmSave(); + List getHoursAggregatedByCriteria(); Integer getOrderHours(); - List getResourceAllocations(); + List getResourceAllocationRows(); void init(Task task); - void removeAllResourceAllocations(); - -} +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingAllocationRow.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingAllocationRow.java new file mode 100644 index 000000000..61b90fe88 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingAllocationRow.java @@ -0,0 +1,232 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.navalplanner.web.planner.allocation; + +import static org.navalplanner.business.i18n.I18nHelper._; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.math.NumberUtils; +import org.navalplanner.business.planner.entities.GenericResourceAllocation; +import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.SpecificResourceAllocation; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.web.common.components.NewAllocationSelector.AllocationType; + +/** + * + * @author Diego Pino Garcia + * + */ +public class LimitingAllocationRow { + + public static final int DEFAULT_PRIORITY = 5; + + private ResourceAllocation resourceAllocation; + + private int hours = 0; + + private int priority = DEFAULT_PRIORITY; + + private Task task; + + private Collection resources; + + public LimitingAllocationRow() { + + } + + public static List toRows(Task task) { + List result = new ArrayList(); + for (ResourceAllocation each : task.getLimitingResourceAllocations()) { + result.add(new LimitingAllocationRow(each, task)); + } + return result; + } + + public static LimitingAllocationRow create(Resource resource, Task task) { + return new LimitingAllocationRow(SpecificResourceAllocation.create(resource, task), task); + } + + private LimitingAllocationRow(ResourceAllocation resourceAllocation, + Task task) { + this.resourceAllocation = resourceAllocation; + this.task = task; + this.hours = calculateTotalHours(); + this.priority = task.getPriority(); + } + + /** + * Returns initial total hours + * + * @return + */ + private Integer calculateTotalHours() { + return (resourceAllocation != null && resourceAllocation.getIntendedTotalHours() != null) ? + resourceAllocation.getIntendedTotalHours() : task.getTotalHours(); + } + + public static LimitingAllocationRow create(Set criteria, + Collection resources, Task task, int priority) { + LimitingAllocationRow result = new LimitingAllocationRow( + GenericResourceAllocation.create(task, criteria), task, + priority); + result.setResources(resources); + return result; + } + + private void setResources(Collection resources) { + this.resources = resources; + } + + public static LimitingAllocationRow create(Resource resource, Task task, + int priority) { + return new LimitingAllocationRow(SpecificResourceAllocation.create( + resource, task), task, priority); + } + + private LimitingAllocationRow(ResourceAllocation resourceAllocation, + Task task, int priority) { + + this.resourceAllocation = resourceAllocation; + this.task = task; + this.hours = calculateTotalHours(); + + task.setPriority(priority); + this.priority = task.getPriority(); + } + + public AllocationType getAllocationType() { + return (resourceAllocation instanceof SpecificResourceAllocation) ? AllocationType.SPECIFIC + : AllocationType.GENERIC; + } + + public String getAllocationTypeStr() { + return getAllocationType().toString(); + } + + public String getAllocation() { + final AllocationType type = getAllocationType(); + if (AllocationType.GENERIC.equals(type)) { + final GenericResourceAllocation generic = (GenericResourceAllocation) resourceAllocation; + return formatCriteria(generic.getCriterions()); + } + if (AllocationType.SPECIFIC.equals(type)) { + return formatResources(resourceAllocation.getAssociatedResources()); + } + return ""; + } + + private String formatCriteria(Set criteria) { + List criteriaNames = new ArrayList(); + for (Criterion each: criteria) { + criteriaNames.add(each.getName()); + } + return (criteriaNames.isEmpty()) ? _("[generic all workers]") : StringUtils.join(criteriaNames, ","); + } + + private String formatResources(List resources) { + List resourcesNames = new ArrayList(); + for (Resource each: resources) { + resourcesNames.add(each.getName()); + } + return StringUtils.join(resourcesNames, ","); + } + + public int getHours() { + return hours; + } + + public void setHours(int hours) { + this.hours = hours; + updateHours(resourceAllocation, hours); + } + + private void updateHours(ResourceAllocation resourceAllocation, final int hours) { + if (resourceAllocation != null) { + resourceAllocation.setIntendedTotalHours(hours); + } + } + + public int getPriority() { + return priority; + } + + public String getPriorityStr() { + return (new Integer(getPriority()).toString()); + } + + public void setPriorityStr(String priority) { + this.priority = toNumber(priority); + task.setPriority(this.priority); + } + + private int toNumber(String str) { + if (NumberUtils.isNumber(str)) { + int result = NumberUtils.toInt(str); + return (result >= 1 && result <= 10) ? result : 1; + } + return 1; + } + + public ResourceAllocation getResourceAllocation() { + return resourceAllocation; + } + + public Set getResourcesIds() { + Set result = new HashSet(); + if (resources != null) { + for (Resource each: resources) { + result.add(each.getId()); + } + } + return result; + } + + public Set getCriteriaIds() { + Set result = new HashSet(); + + if (resourceAllocation instanceof GenericResourceAllocation) { + GenericResourceAllocation generic = (GenericResourceAllocation) resourceAllocation; + for (Criterion each: generic.getCriterions()) { + result.add(each.getId()); + } + } + return result; + } + + public boolean isSpecific() { + return resourceAllocation != null + && resourceAllocation instanceof SpecificResourceAllocation; + } + + public boolean isGeneric() { + return resourceAllocation != null + && resourceAllocation instanceof GenericResourceAllocation; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java index 825d8ef99..c2199086b 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationController.java @@ -27,11 +27,9 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.navalplanner.business.orders.entities.AggregatedHoursGroup; import org.navalplanner.business.planner.entities.ResourceAllocation; -import org.navalplanner.web.common.ConstraintChecker; import org.navalplanner.web.common.IMessagesForUser; import org.navalplanner.web.common.Util; import org.navalplanner.web.common.components.NewAllocationSelector; -import org.navalplanner.web.planner.allocation.LimitingResourceAllocationModel.LimitingResourceAllocationRow; import org.navalplanner.web.planner.allocation.ResourceAllocationController.HoursRendererColumn; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; @@ -100,7 +98,7 @@ public class LimitingResourceAllocationController extends GenericForwardComposer resourceAllocationModel.getHoursAggregatedByCriteria())); gridLimitingOrderElementHours.setRowRenderer(createOrderElementHoursRenderer()); } catch (Exception e) { - + LOG.error(e.getStackTrace()); } } @@ -123,12 +121,8 @@ public class LimitingResourceAllocationController extends GenericForwardComposer return resourceAllocationModel.getOrderHours(); } - public List getResourceAllocations() { - return resourceAllocationModel.getResourceAllocations(); - } - - public void removeAllResourceAllocations() { - resourceAllocationModel.removeAllResourceAllocations(); + public List getResourceAllocationRows() { + return resourceAllocationModel.getResourceAllocationRows(); } public void onSelectWorkers(Event event) { @@ -142,7 +136,6 @@ public class LimitingResourceAllocationController extends GenericForwardComposer } private void addSelectedResources() { - resourceAllocationModel.removeAllResourceAllocations(); limitingNewAllocationSelector.addChoosen(); } @@ -151,7 +144,6 @@ public class LimitingResourceAllocationController extends GenericForwardComposer } public void clear() { - resourceAllocationModel.removeAllResourceAllocations(); limitingNewAllocationSelector.clearAll(); } @@ -159,13 +151,17 @@ public class LimitingResourceAllocationController extends GenericForwardComposer return gridLimitingAllocationRenderer; } + public void accept() { + resourceAllocationModel.confirmSave(); + } + public class GridLimitingAllocationRenderer implements RowRenderer { @Override public void render(Row row, Object data) throws Exception { - LimitingResourceAllocationRow resourceAllocation = (LimitingResourceAllocationRow) data; + LimitingAllocationRow resourceAllocation = (LimitingAllocationRow) data; - row.appendChild(label(resourceAllocation.getAllocationType())); + row.appendChild(label(resourceAllocation.getAllocationTypeStr())); row.appendChild(label(resourceAllocation.getAllocation())); row.appendChild(intboxHours(resourceAllocation)); row.appendChild(listboxPriority(resourceAllocation)); @@ -175,11 +171,11 @@ public class LimitingResourceAllocationController extends GenericForwardComposer return new Label(value); } - private Intbox intboxHours(final LimitingResourceAllocationRow resourceAllocation) { + private Intbox intboxHours(final LimitingAllocationRow resourceAllocation) { return bindToHours(new Intbox(), resourceAllocation); } - private Intbox bindToHours(Intbox intbox, final LimitingResourceAllocationRow resourceAllocation) { + private Intbox bindToHours(Intbox intbox, final LimitingAllocationRow resourceAllocation) { Util.bind(intbox, new Util.Getter() { @Override @@ -197,7 +193,7 @@ public class LimitingResourceAllocationController extends GenericForwardComposer return intbox; } - private Listbox listboxPriority(final LimitingResourceAllocationRow resourceAllocation) { + private Listbox listboxPriority(final LimitingAllocationRow resourceAllocation) { return bindToPriority(buildPriorityList(resourceAllocation.getPriority()), resourceAllocation); } @@ -221,17 +217,24 @@ public class LimitingResourceAllocationController extends GenericForwardComposer return result; } - private Listbox bindToPriority(Listbox listbox, final LimitingResourceAllocationRow resourceAllocation) { + private Listbox bindToPriority(Listbox listbox, final LimitingAllocationRow resourceAllocation) { listbox.addEventListener("onSelect", new EventListener() { @Override public void onEvent(Event event) throws Exception { - resourceAllocation.setPriorityStr((String) event.getData()); + String priority = getSelectedValue((Listbox) event.getTarget()); + resourceAllocation.setPriorityStr(priority); } }); return listbox; } + private String getSelectedValue(Listbox listbox) { + final Listitem item = listbox.getSelectedItem(); + final Listcell cell = (Listcell) item.getChildren().get(0); + return cell.getLabel(); + } + } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java index fefa3d88c..815ff1b77 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/LimitingResourceAllocationModel.java @@ -20,26 +20,26 @@ package org.navalplanner.web.planner.allocation; -import static org.navalplanner.business.i18n.I18nHelper._; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; -import org.apache.commons.lang.math.NumberUtils; import org.navalplanner.business.orders.daos.IHoursGroupDAO; import org.navalplanner.business.orders.entities.AggregatedHoursGroup; import org.navalplanner.business.orders.entities.HoursGroup; import org.navalplanner.business.orders.entities.TaskSource; +import org.navalplanner.business.planner.daos.ITaskElementDAO; import org.navalplanner.business.planner.daos.ITaskSourceDAO; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; +import org.navalplanner.business.planner.entities.ResourceAllocation; import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.resources.daos.ICriterionDAO; +import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.business.resources.entities.Criterion; import org.navalplanner.business.resources.entities.CriterionType; import org.navalplanner.business.resources.entities.Resource; -import org.navalplanner.web.common.components.NewAllocationSelector.AllocationType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; @@ -64,120 +64,87 @@ public class LimitingResourceAllocationModel implements ILimitingResourceAllocat @Autowired private ICriterionDAO criterionDAO; + @Autowired + private ITaskElementDAO taskDAO; + + @Autowired + private IResourceDAO resourceDAO; + private Task task; - private List resourceAllocations = new ArrayList(); + private List limitingAllocationRows = new ArrayList(); @Override public void init(Task task) { this.task = task; - this.resourceAllocations = new ArrayList(); + limitingAllocationRows = LimitingAllocationRow.toRows(task); } @Override + @Transactional(readOnly=true) public Integer getOrderHours() { if (task == null) { return 0; } - return AggregatedHoursGroup.sum(task.getAggregatedByCriterions()); - } - - public class LimitingResourceAllocationRow { - - private static final int DEFAULT_PRIORITY = 5; - - private AllocationType type = AllocationType.GENERIC; - - private int hours = 0; - - private int priority = DEFAULT_PRIORITY; - - public LimitingResourceAllocationRow() { - - } - - public LimitingResourceAllocationRow(AllocationType type, int hours) { - this(type, hours, DEFAULT_PRIORITY); - } - - public LimitingResourceAllocationRow(AllocationType type, int hours, int priority) { - this.type = type; - this.hours = hours; - this.priority = priority; - } - - public String getAllocationType() { - return type.toString(); - } - - public String getAllocation() { - if (AllocationType.GENERIC.equals(type)) { - return _("Criteria"); - } - if (AllocationType.SPECIFIC.equals(type)) { - return _("Resource"); - } - return ""; - } - - public int getHours() { - return hours; - } - - public void setHours(int hours) { - this.hours = hours; - } - - public int getPriority() { - return priority; - } - - public String getPriorityStr() { - return (new Integer(priority)).toString(); - } - - public void setPriorityStr(String priority) { - this.priority = toNumber(priority); - } - - private int toNumber(String str) { - if (NumberUtils.isNumber(str)) { - int result = NumberUtils.toInt(str); - return (result >= 1 && result <= 10) ? result : 1; - } - return 1; - } - - }; - - private void addSpecificResourceAllocation(Resource resource) { - LimitingResourceAllocationRow resourceAllocation = new LimitingResourceAllocationRow( - AllocationType.SPECIFIC, getSumHoursGroups()); - resourceAllocations.add(resourceAllocation); - } - - private int getSumHoursGroups() { - return task.getTaskSource().getTotalHours(); - } - - private void addGenericResourceAllocation(Resource resource) { - LimitingResourceAllocationRow resourceAllocation = new LimitingResourceAllocationRow( - AllocationType.GENERIC, getSumHoursGroups()); - resourceAllocations.add(resourceAllocation); + return AggregatedHoursGroup.sum(getHoursAggregatedByCriteria()); } @Override - public void addGeneric(Set criterions, + @Transactional(readOnly = true) + public void addGeneric(Set criteria, Collection resources) { if (resources.size() >= 1) { - addGenericResourceAllocation(getFirstChild(resources)); + reattachResources(resources); + addGenericResourceAllocation(criteria, resources); } } + private void reattachResources(Collection resources) { + for (Resource each: resources) { + resourceDAO.reattach(each); + } + } + + private void addGenericResourceAllocation(Set criteria, + Collection resources) { + if (isNew(criteria, resources)) { + limitingAllocationRows.clear(); + LimitingAllocationRow allocationRow = LimitingAllocationRow.create( + criteria, resources, task, LimitingAllocationRow.DEFAULT_PRIORITY); + limitingAllocationRows.add(allocationRow); + } + } + + private boolean isNew(Set criteria, Collection resources) { + LimitingAllocationRow allocationRow = getLimitingAllocationRow(); + + if (allocationRow == null || allocationRow.isSpecific()) { + return true; + } + + Set allocatedResourcesIds = allocationRow.getResourcesIds(); + for (Resource each: resources) { + if (!allocatedResourcesIds.contains(each.getId())) { + return true; + } + } + + Set allocatedCriteriaIds = allocationRow.getCriteriaIds(); + for (Criterion each: criteria) { + if (!allocatedCriteriaIds.contains(each.getId())) { + return true; + } + } + return false; + } + @Override + @Transactional(readOnly = true) public void addSpecific(Collection resources) { if (resources.size() >= 1) { - addSpecificResourceAllocation(getFirstChild(resources)); + Resource resource = getFirstChild(resources); + resourceDAO.reattach(resource); + addSpecificResourceAllocation(resource); } } @@ -185,9 +152,58 @@ public class LimitingResourceAllocationModel implements ILimitingResourceAllocat return collection.iterator().next(); } + private void addSpecificResourceAllocation(Resource resource) { + if (isNew(resource)) { + limitingAllocationRows.clear(); + LimitingAllocationRow allocationRow = LimitingAllocationRow.create( + resource, task, LimitingAllocationRow.DEFAULT_PRIORITY); + limitingAllocationRows.add(allocationRow); + } + } + + private boolean isNew(Resource resource) { + LimitingAllocationRow allocationRow = getLimitingAllocationRow(); + + if (allocationRow == null || allocationRow.isGeneric()) { + return true; + } + + final Resource taskResource = getAssociatedResource(); + if (taskResource != null) { + return (!resource.getId().equals(taskResource.getId())); + } + return true; + } + + private Resource getAssociatedResource() { + ResourceAllocation resourceAllocation = getAssociatedResourceAllocation(); + if (resourceAllocation != null) { + List resources = resourceAllocation.getAssociatedResources(); + if (resources != null && resources.size() >= 1) { + return (Resource) resources.iterator().next(); + } + } + return null; + } + + private ResourceAllocation getAssociatedResourceAllocation() { + LimitingAllocationRow allocationRow = getLimitingAllocationRow(); + if (allocationRow != null) { + return allocationRow.getResourceAllocation(); + } + return null; + } + + private LimitingAllocationRow getLimitingAllocationRow() { + if (limitingAllocationRows.size() >= 1) { + return limitingAllocationRows.get(0); + } + return null; + } + @Override - public List getResourceAllocations() { - return Collections.unmodifiableList(resourceAllocations); + public List getResourceAllocationRows() { + return Collections.unmodifiableList(limitingAllocationRows); } @Override @@ -243,8 +259,22 @@ public class LimitingResourceAllocationModel implements ILimitingResourceAllocat } @Override - public void removeAllResourceAllocations() { - resourceAllocations.clear(); + @Transactional(readOnly=true) + public void confirmSave() { + taskDAO.reattach(task); + ResourceAllocation resourceAllocation = getAssociatedResourceAllocation(); + if (resourceAllocation != null && resourceAllocation.isNewObject()) { + task.removeAllResourceAllocations(); + addResourceAllocation(task, resourceAllocation); + } + taskDAO.save(task); } -} + private void addResourceAllocation(Task task, ResourceAllocation resourceAllocation) { + LimitingResourceQueueElement element = LimitingResourceQueueElement.create(); + element.setEarlierStartDateBecauseOfGantt(task.getStartDate()); + resourceAllocation.setLimitingResourceQueueElement(element); + task.addResourceAllocation(resourceAllocation, false); + } + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationCommand.java index 7518c2bf4..4bb733f38 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationCommand.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationCommand.java @@ -51,16 +51,22 @@ public class ResourceAllocationCommand implements IResourceAllocationCommand { public ResourceAllocationCommand() { } - @Override - public void doAction(IContextWithPlannerTask context, - TaskElement task) { - editTaskUtilities.reattach(task); + @Override + public void doAction(IContextWithPlannerTask context, + TaskElement taskElement) { + editTaskUtilities.reattach(taskElement); - if (isApplicableTo(task)) { - this.editTaskController.showEditFormResourceAllocation(context, - (Task) task, planningState); - } - } + if (isApplicableTo(taskElement)) { + Task task = (Task) taskElement; + if (task.isSubcontracted()) { + editTaskController.showEditFormSubcontract(context, task, + planningState); + } else { + editTaskController.showEditFormResourceAllocation(context, + task, planningState); + } + } + } @Override public String getName() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java index 668e48aa9..422814096 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationController.java @@ -71,7 +71,6 @@ import org.zkoss.zul.Row; import org.zkoss.zul.RowRenderer; import org.zkoss.zul.SimpleListModel; import org.zkoss.zul.Tab; -import org.zkoss.zul.api.Tabpanel; /** * Controller for {@link ResourceAllocation} view. @@ -121,8 +120,6 @@ public class ResourceAllocationController extends GenericForwardComposer { private Tab workerSearchTab; - private Tabpanel tabpanel; - public static void registerNeededScripts() { getScriptsRegister() .register(ScriptsRequiredByAdvancedAllocation.class); @@ -136,7 +133,6 @@ public class ResourceAllocationController extends GenericForwardComposer { @Override public void doAfterCompose(Component comp) throws Exception { super.doAfterCompose(comp); - tabpanel = (Tabpanel) comp; allResourcesPerDay = new Decimalbox(); newAllocationSelector.setLimitingResourceFilter(false); makeReadyInputsForCalculationTypes(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java index 04504b4d9..987f6eadc 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/allocation/ResourceAllocationModel.java @@ -246,8 +246,8 @@ public class ResourceAllocationModel implements IResourceAllocationModel { reattachCriterions(this.task.getHoursGroup().getValidCriterions()); loadResources(this.task.getSatisfiedResourceAllocations()); loadDerivedAllocations(this.task.getSatisfiedResourceAllocations()); - List initialRows = AllocationRow.toRows(this.task - .getAllResourceAllocations(), resourceDAO); + List initialRows = AllocationRow.toRows(task + .getNonLimitingResourceAllocations(), resourceDAO); allocationRowsHandler = AllocationRowsHandler.create(task, initialRows, createWorkerFinder()); return allocationRowsHandler; diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java index 1f043f56d..4a359be3b 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/company/CompanyPlanningModel.java @@ -42,6 +42,7 @@ import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.calendars.entities.SameWorkHoursEveryDay; import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; +import org.navalplanner.business.common.daos.IConfigurationDAO; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.orders.daos.IOrderDAO; import org.navalplanner.business.orders.entities.Order; @@ -82,6 +83,7 @@ import org.zkforge.timeplot.Plotinfo; import org.zkforge.timeplot.Timeplot; import org.zkforge.timeplot.geometry.TimeGeometry; import org.zkforge.timeplot.geometry.ValueGeometry; +import org.zkoss.ganttz.IChartVisibilityChangedListener; import org.zkoss.ganttz.IPredicate; import org.zkoss.ganttz.Planner; import org.zkoss.ganttz.adapters.IStructureNavigator; @@ -154,6 +156,11 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { private MultipleTabsPlannerController tabs; + private List keepAliveChartVisibilityListeners = new ArrayList(); + + @Autowired + private IConfigurationDAO configurationDAO; + @Autowired private IScenarioManager scenarioManager; @@ -205,6 +212,8 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { IPredicate predicate) { PlannerConfiguration configuration = createConfiguration(predicate); + configuration.setExpandPlanningViewCharts(configurationDAO + .getConfiguration().isExpandCompanyPlanningViewCharts()); Tabbox chartComponent = new Tabbox(); chartComponent.setOrient("vertical"); @@ -247,22 +256,26 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { configuration.setSecondLevelModificators(new BankHolidaysMarker()); planner.setConfiguration(configuration); - Timeplot chartLoadTimeplot = new Timeplot(); - Timeplot chartEarnedValueTimeplot = new Timeplot(); + Timeplot chartLoadTimeplot = createEmptyTimeplot(); + Timeplot chartEarnedValueTimeplot = createEmptyTimeplot(); CompanyEarnedValueChartFiller earnedValueChartFiller = new CompanyEarnedValueChartFiller(); earnedValueChartFiller.calculateValues(planner.getTimeTracker() .getRealInterval()); appendTabpanels(chartComponent, chartLoadTimeplot, chartEarnedValueTimeplot, earnedValueChartFiller); - setupChart(chartLoadTimeplot, new CompanyLoadChartFiller(), planner - .getTimeTracker(), planner.getZoomLevel()); + setupChart(chartLoadTimeplot, new CompanyLoadChartFiller(), planner); Chart earnedValueChart = setupChart(chartEarnedValueTimeplot, - earnedValueChartFiller, planner.getTimeTracker(), - planner.getZoomLevel()); + earnedValueChartFiller, planner); setEventListenerConfigurationCheckboxes(earnedValueChart); } + private Timeplot createEmptyTimeplot() { + Timeplot timeplot = new Timeplot(); + timeplot.appendChild(new Plotinfo()); + return timeplot; + } + private void appendTabs(Tabbox chartComponent) { Tabs chartTabs = new Tabs(); chartTabs.appendChild(new Tab(_("Load"))); @@ -486,6 +499,7 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { configuration.setCriticalPathEnabled(false); configuration.setExpandAllEnabled(false); configuration.setFlattenTreeEnabled(false); + configuration.setRenamingTasksEnabled(false); } private void addAdditionalCommands( @@ -518,17 +532,43 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { } private Chart setupChart(Timeplot chartComponent, - IChartFiller loadChartFiller, TimeTracker timeTracker, - ZoomLevel defaultZoomLevel) { + IChartFiller loadChartFiller, Planner planner) { + TimeTracker timeTracker = planner.getTimeTracker(); Chart loadChart = new Chart(chartComponent, loadChartFiller, timeTracker); - loadChart.setZoomLevel(defaultZoomLevel); - loadChart.fillChart(); - timeTracker.addZoomListener(fillOnZoomChange(loadChart)); + loadChart.setZoomLevel(planner.getZoomLevel()); + if (planner.isVisibleChart()) { + loadChart.fillChart(); + } + timeTracker.addZoomListener(fillOnZoomChange(planner, loadChart)); + planner + .addChartVisibilityListener(fillOnChartVisibilityChange(loadChart)); return loadChart; } - private IZoomLevelChangedListener fillOnZoomChange( + private IChartVisibilityChangedListener fillOnChartVisibilityChange( + final Chart loadChart) { + IChartVisibilityChangedListener chartVisibilityChangedListener = new IChartVisibilityChangedListener() { + + @Override + public void chartVisibilityChanged(final boolean visible) { + transactionService + .runOnReadOnlyTransaction(new IOnTransaction() { + @Override + public Void execute() { + if (visible) { + loadChart.fillChart(); + } + return null; + } + }); + } + }; + keepAliveChartVisibilityListeners.add(chartVisibilityChangedListener); + return chartVisibilityChangedListener; + } + + private IZoomLevelChangedListener fillOnZoomChange(final Planner planner, final Chart loadChart) { IZoomLevelChangedListener zoomListener = new IZoomLevelChangedListener() { @@ -541,7 +581,9 @@ public abstract class CompanyPlanningModel implements ICompanyPlanningModel { .runOnReadOnlyTransaction(new IOnTransaction() { @Override public Void execute() { - loadChart.fillChart(); + if (planner.isVisibleChart()) { + loadChart.fillChart(); + } return null; } }); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationCommand.java new file mode 100644 index 000000000..095b7154f --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationCommand.java @@ -0,0 +1,87 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.planner.consolidations; + +import static org.navalplanner.web.I18nHelper._; + +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.web.planner.order.IEditTaskUtilities; +import org.navalplanner.web.planner.order.PlanningState; +import org.navalplanner.web.planner.taskedition.EditTaskController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; +import org.zkoss.ganttz.extensions.IContextWithPlannerTask; + +/** + * A command that opens a window to make the advances consolidations of a task. + * @author Susana Montes Pedreira + */ +@Component +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class AdvanceConsolidationCommand implements + IAdvanceConsolidationCommand { + + private EditTaskController editTaskController; + private PlanningState planningState; + + @Autowired + private IEditTaskUtilities editTaskUtilities; + + public AdvanceConsolidationCommand() { + } + + @Override + public void doAction(IContextWithPlannerTask context, + TaskElement task) { + editTaskUtilities.reattach(task); + + if (isApplicableTo(task)) { + this.editTaskController.showEditFormAdvanceConsolidation(context, + (Task) task, planningState); + } + } + + @Override + public String getName() { + return _("Advance consolidation"); + } + + @Override + public void initialize(EditTaskController editTaskController, + PlanningState planningState) { + this.editTaskController = editTaskController; + this.planningState = planningState; + } + + @Override + public String getIcon() { + return "/common/img/ico_ok.png"; + } + + @Override + public boolean isApplicableTo(TaskElement task) { + return task instanceof Task; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationController.java new file mode 100644 index 000000000..ef7fc798d --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationController.java @@ -0,0 +1,118 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.planner.consolidations; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.List; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.web.common.IMessagesForUser; +import org.navalplanner.web.common.Util; +import org.navalplanner.web.planner.order.PlanningState; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.zkoss.ganttz.extensions.IContextWithPlannerTask; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.util.GenericForwardComposer; +import org.zkoss.zul.Grid; + +/** + * Controller for {@link Advance} consolidation view. + * @author Susana Montes Pedreira + */ +@org.springframework.stereotype.Component("advanceConsolidationController") +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class AdvanceConsolidationController extends GenericForwardComposer { + + private static final Log LOG = LogFactory + .getLog(AdvanceConsolidationController.class); + + private IAdvanceConsolidationModel advanceConsolidationModel; + + private Grid advancesGrid; + + @Override + public void doAfterCompose(Component comp) throws Exception { + super.doAfterCompose(comp); + } + + public void init(IContextWithPlannerTask context, + org.navalplanner.business.planner.entities.Task task, + PlanningState planningState, IMessagesForUser messagesForUser) { + advanceConsolidationModel.initAdvancesFor(task, context, planningState); + reloadAdvanceGrid(); + } + + // Triggered when closable button is clicked + public void onClose(Event event) { + cancel(); + event.stopPropagation(); + } + + public void cancel() { + clear(); + advanceConsolidationModel.cancel(); + } + + private void clear() { + } + + public void accept() { + advanceConsolidationModel.accept(); + clear(); + } + + public String getInfoAdvance() { + String infoAdvanceAssignment = advanceConsolidationModel + .getInfoAdvanceAssignment(); + if (infoAdvanceAssignment.isEmpty()) { + return _("Advance measurements"); + } + + return _("Advance measurements: ") + infoAdvanceAssignment; + } + + public List getAdvances() { + return advanceConsolidationModel.getConsolidationDTOs(); + } + + public void reloadAdvanceGrid() { + advanceConsolidationModel.initLastConsolidatedDate(); + Util.reloadBindings(advancesGrid); + } + + public boolean isVisibleAdvances() { + return advanceConsolidationModel.isVisibleAdvances(); + } + + public boolean isVisibleMessages() { + return advanceConsolidationModel.isVisibleMessages(); + } + + public String infoMessages() { + return advanceConsolidationModel.infoMessages(); + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationDTO.java new file mode 100644 index 000000000..0cc814cb6 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationDTO.java @@ -0,0 +1,175 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.planner.consolidations; + +import java.math.BigDecimal; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +import org.navalplanner.business.advance.entities.AdvanceMeasurement; +import org.navalplanner.business.planner.entities.consolidations.ConsolidatedValue; + +/** + * Controller for {@link Advance} consolidation view. + * @author Susana Montes Pedreira + */ + +public class AdvanceConsolidationDTO { + + public static Date lastConsolidatedDate; + + public static Date lastConsolidatedAndSavedDate; + + private AdvanceMeasurement advanceMeasurement; + + private ConsolidatedValue consolidatedValue; + + private Date date; + + private BigDecimal value; + + private boolean consolidated; + + public static List sortByDate( + List consolidationDTOs) { + Collections.sort(consolidationDTOs, + new Comparator() { + + @Override + public int compare(AdvanceConsolidationDTO o1, + AdvanceConsolidationDTO o2) { + if (o1.getDate() == null) { + return 1; + } + if (o2.getDate() == null) { + return -1; + } + return o1.getDate().compareTo(o2.getDate()); + } + }); + return consolidationDTOs; + } + + public AdvanceConsolidationDTO(AdvanceMeasurement advanceMeasurement) { + this(advanceMeasurement, null); + } + + public AdvanceConsolidationDTO(AdvanceMeasurement advanceMeasurement, + ConsolidatedValue consolidatedValue) { + this.setAdvanceMeasurement(advanceMeasurement); + this.setConsolidatedValue(consolidatedValue); + initConsolidated(); + initDate(); + initValue(); + } + + private void initConsolidated() { + this.setConsolidated((getConsolidatedValue() != null)); + } + + private void initDate() { + if (consolidatedValue != null) { + this.date = consolidatedValue.getDate().toDateTimeAtStartOfDay() + .toDate(); + } else if (advanceMeasurement != null) { + this.date = advanceMeasurement.getDate().toDateTimeAtStartOfDay() + .toDate(); + } + } + + private void initValue() { + if (consolidatedValue != null) { + this.value = this.consolidatedValue.getValue(); + } else if (advanceMeasurement != null) { + this.value = this.advanceMeasurement.getValue(); + } + } + + public void setAdvanceMeasurement(AdvanceMeasurement advanceMeasurement) { + this.advanceMeasurement = advanceMeasurement; + } + + public AdvanceMeasurement getAdvanceMeasurement() { + return advanceMeasurement; + } + + public void setConsolidatedValue(ConsolidatedValue consolidatedValue) { + this.consolidatedValue = consolidatedValue; + } + + public ConsolidatedValue getConsolidatedValue() { + return consolidatedValue; + } + + public void setConsolidated(boolean consolidated) { + this.consolidated = consolidated; + } + + public boolean isConsolidated() { + return consolidated; + } + + public static void setLastConsolidatedAdvance(Date lastConsolidatedAdvance) { + AdvanceConsolidationDTO.lastConsolidatedDate = lastConsolidatedAdvance; + } + + public static Date getLastConsolidatedAdvance() { + return lastConsolidatedDate; + } + + public Boolean canNotBeConsolidated() { + if ((isConsolidated()) && (consolidatedValue != null) + && (!consolidatedValue.isNewObject())) { + return true; + } + if (lastConsolidatedDate != null) { + if (date != null) { + return ((lastConsolidatedDate.compareTo(date)) > 0); + } + } + return false; + } + + public static Boolean canBeConsolidateAndShow(Date date) { + if (lastConsolidatedAndSavedDate != null) { + if (date != null) { + return ((lastConsolidatedAndSavedDate.compareTo(date)) < 0); + } + } + return true; + } + + public boolean isSavedConsolidatedValue() { + return ((consolidatedValue != null) && (!consolidatedValue + .isNewObject())); + } + + public Date getDate() { + return date; + } + + public BigDecimal getValue() { + return value; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationModel.java new file mode 100644 index 000000000..d4600d260 --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/AdvanceConsolidationModel.java @@ -0,0 +1,356 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.planner.consolidations; + +import static org.navalplanner.web.I18nHelper._; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import org.joda.time.LocalDate; +import org.navalplanner.business.advance.entities.AdvanceMeasurement; +import org.navalplanner.business.advance.entities.DirectAdvanceAssignment; +import org.navalplanner.business.advance.entities.IndirectAdvanceAssignment; +import org.navalplanner.business.orders.entities.OrderElement; +import org.navalplanner.business.planner.daos.ITaskElementDAO; +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidatedValue; +import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidation; +import org.navalplanner.business.planner.entities.consolidations.ConsolidatedValue; +import org.navalplanner.business.planner.entities.consolidations.Consolidation; +import org.navalplanner.business.planner.entities.consolidations.NonCalculatedConsolidatedValue; +import org.navalplanner.business.planner.entities.consolidations.NonCalculatedConsolidation; +import org.navalplanner.business.planner.entities.consolidations.PendingConsolidatedHoursPerResourceAllocation; +import org.navalplanner.web.planner.order.PlanningState; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.zkoss.ganttz.extensions.IContextWithPlannerTask; + +/** + * Model for UI operations related to {@link Task} + * @author Susana Montes Pedreira + */ +@Service +@Scope(BeanDefinition.SCOPE_PROTOTYPE) +public class AdvanceConsolidationModel implements IAdvanceConsolidationModel { + + @Autowired + private ITaskElementDAO taskElementDAO; + + private Task task; + + private PlanningState planningState; + + private IContextWithPlannerTask context; + + private Consolidation consolidation; + + private DirectAdvanceAssignment spreadAdvance; + + private OrderElement orderElement; + + private Date firstConsolidatedDate; + + private String info_message; + + private List consolidationDTOs = new ArrayList(); + + private void initConsolidatedDates() { + consolidationDTOs = AdvanceConsolidationDTO + .sortByDate(getConsolidationDTOs()); + initLastConsolidatedDate(); + initLastConsolidatedAndSavedDate(); + } + + private boolean containsAdvance(AdvanceMeasurement advanceMeasurement) { + for (AdvanceConsolidationDTO dto : consolidationDTOs) { + if (dto.getDate().compareTo( + advanceMeasurement.getDate().toDateTimeAtStartOfDay() + .toDate()) == 0) { + return true; + } + } + return false; + } + + @Override + public void initLastConsolidatedDate() { + // init the lastConsolidatedDate + AdvanceConsolidationDTO.lastConsolidatedDate = null; + int i = 0; + while((i < consolidationDTOs.size()) && (!consolidationDTOs.get(i).isConsolidated())){ + i++; + } + if(i < consolidationDTOs.size()){ + AdvanceConsolidationDTO.lastConsolidatedDate = consolidationDTOs.get(i).getDate(); + } + } + + private void initLastConsolidatedAndSavedDate() { + // init the lastConsolidatedAndSaveDate + int i = 0; + AdvanceConsolidationDTO.lastConsolidatedAndSavedDate = null; + while ((i < consolidationDTOs.size()) + && (!consolidationDTOs.get(i).isSavedConsolidatedValue())) { + i++; + } + if(i < consolidationDTOs.size()){ + AdvanceConsolidationDTO.lastConsolidatedAndSavedDate = consolidationDTOs.get(i).getDate(); + } + } + + @Override + public void cancel() { + + } + + @Override + @Transactional + public void accept() { + if (context != null && orderElement != null && isVisibleAdvances()) { + + createConsolidationIfNeeded(); + + for (AdvanceConsolidationDTO dto : consolidationDTOs) { + if (dto.isConsolidated()) { + addConsolidationIfIsNeeded(dto); + } else { + deleteConsolidationIfIsNeeded(dto); + } + } + + } + } + + private void createConsolidationIfNeeded() { + if (consolidation == null && task != null) { + if (advanceIsCalculated()) { + IndirectAdvanceAssignment indirectAdvanceAssignment = getIndirecAdvanceAssignment(); + consolidation = CalculatedConsolidation.create(task, + indirectAdvanceAssignment); + } else { + consolidation = NonCalculatedConsolidation.create(task, + spreadAdvance); + } + task.setConsolidation(consolidation); + } + } + + private IndirectAdvanceAssignment getIndirecAdvanceAssignment() { + if (orderElement != null) { + Set indirects = orderElement + .getIndirectAdvanceAssignments(); + for (IndirectAdvanceAssignment indirectAdvanceAssignment : indirects) { + if (indirectAdvanceAssignment.getReportGlobalAdvance()) { + return indirectAdvanceAssignment; + } + } + } + return null; + } + + private void addConsolidationIfIsNeeded(AdvanceConsolidationDTO dto) { + if (dto.getConsolidatedValue() == null) { + ConsolidatedValue consolidatedValue = createNewConsolidatedValue(dto); + dto.setConsolidatedValue(consolidatedValue); + } + addConsolidatedValue(dto.getConsolidatedValue()); + } + + private void addConsolidatedValue(ConsolidatedValue value) { + if (consolidation != null && task != null) { + if (!consolidation.isCalculated()) { + ((NonCalculatedConsolidation) consolidation) + .addConsolidatedValue((NonCalculatedConsolidatedValue) value); + } else { + ((CalculatedConsolidation) consolidation) + .addConsolidatedValue((CalculatedConsolidatedValue) value); + } + } + } + + private ConsolidatedValue createNewConsolidatedValue( + AdvanceConsolidationDTO dto) { + if (consolidation != null && task != null) { + + Set pendingConsolidatedHours = ConsolidatedValue + .createPendingConsolidatedHours(LocalDate + .fromDateFields(dto.getDate()), task + .getAllResourceAllocations()); + + if (consolidation.isCalculated()) { + return CalculatedConsolidatedValue.create(LocalDate + .fromDateFields(dto.getDate()), dto.getValue(), + pendingConsolidatedHours); + } else { + return NonCalculatedConsolidatedValue.create(LocalDate + .fromDateFields(dto.getDate()), dto.getValue(), dto + .getAdvanceMeasurement(), pendingConsolidatedHours); + } + } + return null; + } + + private void deleteConsolidationIfIsNeeded(AdvanceConsolidationDTO dto) { + if ((dto.getConsolidatedValue() != null) + && (dto.getConsolidatedValue().isNewObject())) { + if (consolidation != null && task != null) { + if (!consolidation.isCalculated()) { + ((NonCalculatedConsolidation) consolidation) + .getNonCalculatedConsolidatedValues().remove( + dto.getConsolidatedValue()); + } else { + ((CalculatedConsolidation) consolidation) + .getCalculatedConsolidatedValues().remove( + dto.getConsolidatedValue()); + } + } + } + } + + @Override + @Transactional(readOnly = true) + public void initAdvancesFor(Task task, + IContextWithPlannerTask context, + PlanningState planningState) { + this.context = context; + initTask(task); + this.planningState = planningState; + initAdvanceConsolidationsDTOs(task); + } + + private void initTask(Task task) { + this.task = task; + taskElementDAO.reattach(this.task); + } + + private void initAdvanceConsolidationsDTOs(Task task) { + orderElement = task.getOrderElement(); + spreadAdvance = orderElement.getReportGlobalAdvanceAssignment(); + consolidation = task.getConsolidation(); + + if (spreadAdvance != null) { + createAdvanceConsolidationDTOs(); + initConsolidatedDates(); + addNonConsolidatedAdvances(); + } + } + + private void createAdvanceConsolidationDTOs() { + consolidationDTOs = new ArrayList(); + if (consolidation != null) { + if (!consolidation.isCalculated()) { + for (NonCalculatedConsolidatedValue consolidatedValue : ((NonCalculatedConsolidation) consolidation) + .getNonCalculatedConsolidatedValues()) { + consolidationDTOs.add(new AdvanceConsolidationDTO( + consolidatedValue.getAdvanceMeasurement(), + consolidatedValue)); + } + }else{ + for (CalculatedConsolidatedValue consolidatedValue : ((CalculatedConsolidation) consolidation) + .getCalculatedConsolidatedValues()) { + consolidationDTOs.add(new AdvanceConsolidationDTO(null, + consolidatedValue)); + } + } + } + } + + private void addNonConsolidatedAdvances() { + int i = 0; + for (AdvanceMeasurement advance : getAdvances()) { + if (canBeConsolidateAndShow(advance)) { + consolidationDTOs.add(new AdvanceConsolidationDTO(advance)); + } + } + consolidationDTOs = AdvanceConsolidationDTO + .sortByDate(consolidationDTOs); + } + + private boolean canBeConsolidateAndShow( + AdvanceMeasurement advanceMeasurement) { + Date date = advanceMeasurement.getDate().toDateTimeAtStartOfDay().toDate(); + return ((AdvanceConsolidationDTO.canBeConsolidateAndShow(date)) && (!containsAdvance(advanceMeasurement))); + } + + @Override + public String getInfoAdvanceAssignment() { + if (this.spreadAdvance == null || this.orderElement == null) { + return ""; + } + return getInfoAdvanceAssignment(this.spreadAdvance); + } + + private String getInfoAdvanceAssignment(DirectAdvanceAssignment assignment) { + if (assignment == null) { + return ""; + } + if (assignment.getMaxValue() == null) { + return ""; + } + return _("( max: {0} )", assignment.getMaxValue()); + } + + private List getAdvances() { + if (spreadAdvance != null) { + return new ArrayList(spreadAdvance + .getAdvanceMeasurements()); + } + return new ArrayList(); + } + + @Override + public boolean isVisibleAdvances() { + return (!isVisibleMessages()); + } + + @Override + public boolean isVisibleMessages() { + return (getAdvances().size() == 0); + } + + private boolean advanceIsCalculated(){ + return ((spreadAdvance != null) && (spreadAdvance.isFake())); + } + + public String infoMessages() { + return _("There are not any assigned advance to current task"); + } + + public void setConsolidationDTOs( + List consolidationDTOs) { + this.consolidationDTOs = consolidationDTOs; + } + + public List getConsolidationDTOs() { + if (spreadAdvance != null && orderElement != null) { + return consolidationDTOs; + } + return new ArrayList(); + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/IAdvanceConsolidationCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/IAdvanceConsolidationCommand.java new file mode 100644 index 000000000..83e1e9c8f --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/IAdvanceConsolidationCommand.java @@ -0,0 +1,37 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.planner.consolidations; + +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.web.planner.order.PlanningState; +import org.navalplanner.web.planner.taskedition.EditTaskController; +import org.zkoss.ganttz.extensions.ICommandOnTask; + +/** + * Contract for {@link AdvanceConsolidationCommand}. + * @author Susana Montes Pedreira + */ +public interface IAdvanceConsolidationCommand extends + ICommandOnTask { + + void initialize(EditTaskController editTaskController, PlanningState state); + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/IAdvanceConsolidationModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/IAdvanceConsolidationModel.java new file mode 100644 index 000000000..da7c6b65f --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/consolidations/IAdvanceConsolidationModel.java @@ -0,0 +1,72 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.navalplanner.web.planner.consolidations; + +import java.util.List; + +import org.navalplanner.business.planner.entities.Task; +import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.web.planner.order.PlanningState; +import org.zkoss.ganttz.extensions.IContextWithPlannerTask; + +/** + * Contract for {@link Task}. + * @author Susana Montes Pedreira + */ +public interface IAdvanceConsolidationModel { + + public interface IAdvanceConsolidationContext { + public T doInsideTransaction(); + } + + String getInfoAdvanceAssignment(); + + void initLastConsolidatedDate(); + + boolean isVisibleAdvances(); + + boolean isVisibleMessages(); + + List getConsolidationDTOs(); + + String infoMessages(); + + /** + * Cancel operation + */ + void cancel(); + + /** + * Save task + */ + void accept(); + + /** + * Starts the use case + * @param task + * @param ganttTask + * @param planningState + */ + void initAdvancesFor(Task task, + IContextWithPlannerTask context, + PlanningState planningState); + +} \ No newline at end of file diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/OrderPlanningModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/OrderPlanningModel.java index 49698f8e1..4e41dd570 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/OrderPlanningModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/OrderPlanningModel.java @@ -46,6 +46,7 @@ import org.navalplanner.business.calendars.entities.BaseCalendar; import org.navalplanner.business.calendars.entities.SameWorkHoursEveryDay; import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; +import org.navalplanner.business.common.daos.IConfigurationDAO; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.orders.daos.IOrderDAO; import org.navalplanner.business.orders.entities.HoursGroup; @@ -77,7 +78,6 @@ import org.navalplanner.business.users.entities.OrderAuthorization; import org.navalplanner.business.users.entities.OrderAuthorizationType; import org.navalplanner.business.users.entities.User; import org.navalplanner.business.users.entities.UserRole; -import org.navalplanner.business.workreports.daos.IWorkReportLineDAO; import org.navalplanner.web.calendars.BaseCalendarModel; import org.navalplanner.web.common.ViewSwitcher; import org.navalplanner.web.planner.ITaskElementAdapter; @@ -90,6 +90,7 @@ import org.navalplanner.web.planner.chart.ChartFiller; import org.navalplanner.web.planner.chart.EarnedValueChartFiller; import org.navalplanner.web.planner.chart.IChartFiller; import org.navalplanner.web.planner.chart.EarnedValueChartFiller.EarnedValueType; +import org.navalplanner.web.planner.consolidations.IAdvanceConsolidationCommand; import org.navalplanner.web.planner.milestone.IAddMilestoneCommand; import org.navalplanner.web.planner.milestone.IDeleteMilestoneCommand; import org.navalplanner.web.planner.order.ISaveCommand.IAfterSaveListener; @@ -108,6 +109,7 @@ import org.zkforge.timeplot.Plotinfo; import org.zkforge.timeplot.Timeplot; import org.zkforge.timeplot.geometry.TimeGeometry; import org.zkforge.timeplot.geometry.ValueGeometry; +import org.zkoss.ganttz.IChartVisibilityChangedListener; import org.zkoss.ganttz.Planner; import org.zkoss.ganttz.adapters.IStructureNavigator; import org.zkoss.ganttz.adapters.PlannerConfiguration; @@ -217,9 +219,6 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { @Autowired private ICriterionDAO criterionDAO; - @Autowired - private IWorkReportLineDAO workReportLineDAO; - @Autowired private ITaskElementDAO taskDAO; @@ -246,6 +245,11 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { private Order orderReloaded; + private List keepAliveChartVisibilityListeners = new ArrayList(); + + @Autowired + private IConfigurationDAO configurationDAO; + private IAssignmentsOnResourceCalculator assigmentsOnResourceCalculator = new Resource.AllResourceAssignments(); private Scenario currentScenario; @@ -326,6 +330,9 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { currentScenario = scenarioManager.getCurrent(); orderReloaded = reload(order); PlannerConfiguration configuration = createConfiguration(orderReloaded); + configuration.setExpandPlanningViewCharts(configurationDAO + .getConfiguration().isExpandOrderPlanningViewCharts()); + addAdditional(additional, configuration); ZoomLevel defaultZoomLevel = OrderPlanningModel @@ -347,8 +354,11 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { .addCommandOnTask(buildCalendarAllocationCommand(calendarAllocationController)); configuration .addCommandOnTask(buildTaskPropertiesCommand(editTaskController)); + configuration + .addCommandOnTask(buildAdvanceConsolidationCommand(editTaskController)); configuration .addCommandOnTask(buildSubcontractCommand(editTaskController)); + configuration.setDoubleClickCommand(resourceAllocationCommand); addPrintSupport(configuration, order); Tabbox chartComponent = new Tabbox(); @@ -360,8 +370,8 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { configureModificators(orderReloaded, configuration); planner.setConfiguration(configuration); - Timeplot chartLoadTimeplot = new Timeplot(); - Timeplot chartEarnedValueTimeplot = new Timeplot(); + Timeplot chartLoadTimeplot = createEmptyTimeplot(); + Timeplot chartEarnedValueTimeplot = createEmptyTimeplot(); OrderEarnedValueChartFiller earnedValueChartFiller = new OrderEarnedValueChartFiller( orderReloaded); earnedValueChartFiller.calculateValues(planner.getTimeTracker() @@ -371,19 +381,23 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { Chart loadChart = setupChart(orderReloaded, new OrderLoadChartFiller(orderReloaded), chartLoadTimeplot, - planner.getTimeTracker(), - planner.getZoomLevel()); + planner); refillLoadChartWhenNeeded(configuration, planner, saveCommand, loadChart); Chart earnedValueChart = setupChart(orderReloaded, earnedValueChartFiller, - chartEarnedValueTimeplot, planner - .getTimeTracker(), planner.getZoomLevel()); + chartEarnedValueTimeplot, planner); refillLoadChartWhenNeeded(configuration, planner, saveCommand, earnedValueChart); setEventListenerConfigurationCheckboxes(earnedValueChart); } + private Timeplot createEmptyTimeplot() { + Timeplot timeplot = new Timeplot(); + timeplot.appendChild(new Plotinfo()); + return timeplot; + } + private void addPrintSupport( PlannerConfiguration configuration, final Order order) { configuration.setPrintAction(new IPrintAction() { @@ -698,24 +712,31 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { } private void refillLoadChartWhenNeeded( - PlannerConfiguration configuration, Planner planner, + PlannerConfiguration configuration, final Planner planner, ISaveCommand saveCommand, final Chart loadChart) { - planner.getTimeTracker().addZoomListener(fillOnZoomChange(loadChart)); + planner.getTimeTracker().addZoomListener( + fillOnZoomChange(loadChart, planner)); + planner + .addChartVisibilityListener(fillOnChartVisibilityChange(loadChart)); if(saveCommand != null) { - saveCommand.addListener(fillChartOnSave(loadChart)); + saveCommand.addListener(fillChartOnSave(loadChart, planner)); } taskElementAdapter.addListener(readOnlyProxy(transactionService, IOnMoveListener.class, new IOnMoveListener() { @Override public void moved(TaskElement taskElement) { - loadChart.fillChart(); + if (planner.isVisibleChart()) { + loadChart.fillChart(); + } } })); configuration.addReloadChartListener(readOnlyProxy(transactionService, IReloadChartListener.class, new IReloadChartListener() { @Override public void reloadChart() { - loadChart.fillChart(); + if (planner.isVisibleChart()) { + loadChart.fillChart(); + } } })); } @@ -792,6 +813,14 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { return taskPropertiesCommand; } + private IAdvanceConsolidationCommand buildAdvanceConsolidationCommand( + EditTaskController editTaskController) { + IAdvanceConsolidationCommand advanceConsolidationCommand = getAdvanceConsolidationCommand(); + advanceConsolidationCommand.initialize(editTaskController, + planningState); + return advanceConsolidationCommand; + } + private IAddMilestoneCommand buildMilestoneCommand() { IAddMilestoneCommand addMilestoneCommand = getAddMilestoneCommand(); addMilestoneCommand.setState(planningState); @@ -820,16 +849,41 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { private Chart setupChart(Order orderReloaded, IChartFiller loadChartFiller, Timeplot chartComponent, - TimeTracker timeTracker, - ZoomLevel defaultZoomLevel) { + Planner planner) { + TimeTracker timeTracker = planner.getTimeTracker(); Chart result = new Chart(chartComponent, loadChartFiller, timeTracker); - result.setZoomLevel(defaultZoomLevel); - result.fillChart(); + result.setZoomLevel(planner.getZoomLevel()); + if (planner.isVisibleChart()) { + result.fillChart(); + } return result; } - private IZoomLevelChangedListener fillOnZoomChange(final Chart loadChart) { + private IChartVisibilityChangedListener fillOnChartVisibilityChange( + final Chart loadChart) { + IChartVisibilityChangedListener chartVisibilityChangedListener = new IChartVisibilityChangedListener() { + + @Override + public void chartVisibilityChanged(final boolean visible) { + transactionService + .runOnReadOnlyTransaction(new IOnTransaction() { + @Override + public Void execute() { + if (visible) { + loadChart.fillChart(); + } + return null; + } + }); + } + }; + keepAliveChartVisibilityListeners.add(chartVisibilityChangedListener); + return chartVisibilityChangedListener; + } + + private IZoomLevelChangedListener fillOnZoomChange(final Chart loadChart, + final Planner planner) { IZoomLevelChangedListener zoomListener = new IZoomLevelChangedListener() { @Override @@ -840,7 +894,9 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { .runOnReadOnlyTransaction(new IOnTransaction() { @Override public Void execute() { - loadChart.fillChart(); + if (planner.isVisibleChart()) { + loadChart.fillChart(); + } return null; } }); @@ -852,7 +908,8 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { return zoomListener; } - private IAfterSaveListener fillChartOnSave(final Chart loadChart) { + private IAfterSaveListener fillChartOnSave(final Chart loadChart, + final Planner planner) { IAfterSaveListener result = new IAfterSaveListener() { @Override @@ -861,7 +918,9 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { .runOnReadOnlyTransaction(new IOnTransaction() { @Override public Void execute() { - loadChart.fillChart(); + if (planner.isVisibleChart()) { + loadChart.fillChart(); + } return null; } }); @@ -893,6 +952,7 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { criterionDAO.list(Criterion.class); TaskGroup taskElement = orderReloaded.getAssociatedTaskElement(); forceLoadOfChildren(Arrays.asList(taskElement)); + forceLoadDayAssignments(orderReloaded.getResources()); switchAllocationsToScenario(currentScenario, taskElement); final IScenarioInfo scenarioInfo = buildScenarioInfo(orderReloaded); PlanningState result = PlanningState.create(taskElement, orderReloaded @@ -904,6 +964,12 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { return result; } + private void forceLoadDayAssignments(Set resources) { + for (Resource resource : resources) { + resource.getAssignments().size(); + } + } + private IScenarioInfo buildScenarioInfo(Order orderReloaded) { if (orderReloaded.isUsingTheOwnerScenario()) { return PlanningState.ownerScenarioInfo(currentScenario); @@ -1042,6 +1108,8 @@ public abstract class OrderPlanningModel implements IOrderPlanningModel { protected abstract ITaskPropertiesCommand getTaskPropertiesCommand(); + protected abstract IAdvanceConsolidationCommand getAdvanceConsolidationCommand(); + protected abstract ICalendarAllocationCommand getCalendarAllocationCommand(); private Order reload(Order order) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommand.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommand.java index 7118cc515..3678eedce 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommand.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SaveCommand.java @@ -28,6 +28,7 @@ import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; +import java.util.SortedSet; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -35,6 +36,7 @@ import org.navalplanner.business.common.IAdHocTransactionService; import org.navalplanner.business.common.IOnTransaction; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; import org.navalplanner.business.orders.daos.IOrderDAO; +import org.navalplanner.business.planner.daos.IConsolidationDAO; import org.navalplanner.business.planner.daos.ISubcontractedTaskDataDAO; import org.navalplanner.business.planner.daos.ITaskElementDAO; import org.navalplanner.business.planner.daos.ITaskSourceDAO; @@ -42,9 +44,17 @@ import org.navalplanner.business.planner.entities.DayAssignment; import org.navalplanner.business.planner.entities.DerivedAllocation; import org.navalplanner.business.planner.entities.DerivedDayAssignment; import org.navalplanner.business.planner.entities.DerivedDayAssignmentsContainer; +import org.navalplanner.business.planner.entities.LimitingResourceQueueElement; import org.navalplanner.business.planner.entities.ResourceAllocation; +import org.navalplanner.business.planner.entities.Task; import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.planner.entities.TaskGroup; +import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidatedValue; +import org.navalplanner.business.planner.entities.consolidations.CalculatedConsolidation; +import org.navalplanner.business.planner.entities.consolidations.ConsolidatedValue; +import org.navalplanner.business.planner.entities.consolidations.Consolidation; +import org.navalplanner.business.planner.entities.consolidations.NonCalculatedConsolidatedValue; +import org.navalplanner.business.planner.entities.consolidations.NonCalculatedConsolidation; import org.navalplanner.business.scenarios.daos.IScenarioDAO; import org.navalplanner.web.common.concurrentdetection.OnConcurrentModification; import org.springframework.beans.factory.annotation.Autowired; @@ -66,6 +76,9 @@ public class SaveCommand implements ISaveCommand { private static final Log LOG = LogFactory.getLog(SaveCommand.class); + @Autowired + private IConsolidationDAO consolidationDAO; + @Autowired private ITaskElementDAO taskElementDAO; @@ -150,6 +163,7 @@ public class SaveCommand implements ISaveCommand { private void saveTasksToSave() { for (TaskElement taskElement : state.getTasksToSave()) { + removeEmptyConsolidation(taskElement); taskElementDAO.save(taskElement); if (taskElement.getTaskSource() != null && taskElement.getTaskSource().isNewObject()) { @@ -173,6 +187,47 @@ public class SaveCommand implements ISaveCommand { } } + private void removeEmptyConsolidation(TaskElement taskElement) { + if ((taskElement.isLeaf()) && (!taskElement.isMilestone())) { + Consolidation consolidation = ((Task) taskElement) + .getConsolidation(); + if ((consolidation != null) + && (isEmptyConsolidation(consolidation))) { + if (!consolidation.isNewObject()) { + try { + consolidationDAO.remove(consolidation.getId()); + } catch (InstanceNotFoundException e) { + throw new RuntimeException(e); + } + } + ((Task) taskElement).setConsolidation(null); + } + } + } + + private boolean isEmptyConsolidation(final Consolidation consolidation) { + return transactionService + .runOnTransaction(new IOnTransaction() { + @Override + public Boolean execute() { + + consolidationDAO.reattach(consolidation); + if (consolidation instanceof CalculatedConsolidation) { + SortedSet consolidatedValues = ((CalculatedConsolidation) consolidation) + .getCalculatedConsolidatedValues(); + return consolidatedValues.isEmpty(); + } + if (consolidation instanceof NonCalculatedConsolidation) { + SortedSet consolidatedValues = ((NonCalculatedConsolidation) consolidation) + .getNonCalculatedConsolidatedValues(); + return consolidatedValues.isEmpty(); + } + return false; + + } + }); + } + // newly added TaskElement such as milestones must be called // dontPoseAsTransientObjectAnymore private void dontPoseAsTransient(TaskElement taskElement) { @@ -186,6 +241,29 @@ public class SaveCommand implements ISaveCommand { dontPoseAsTransient(each); } } + if (taskElement instanceof Task) { + dontPoseAsTransient(((Task) taskElement).getConsolidation()); + } + } + + private void dontPoseAsTransient(Consolidation consolidation) { + if (consolidation != null) { + consolidation.dontPoseAsTransientObjectAnymore(); + if (consolidation.isCalculated()) { + dontPoseAsTransient(((CalculatedConsolidation) consolidation) + .getCalculatedConsolidatedValues()); + } else { + dontPoseAsTransient(((NonCalculatedConsolidation) consolidation) + .getNonCalculatedConsolidatedValues()); + } + } + } + + private void dontPoseAsTransient( + SortedSet values) { + for (ConsolidatedValue value : values) { + value.dontPoseAsTransientObjectAnymore(); + } } private void dontPoseAsTransient( @@ -208,6 +286,13 @@ public class SaveCommand implements ISaveCommand { eachAssignment.dontPoseAsTransientObjectAnymore(); } } + dontPoseAsTransient(each.getLimitingResourceQueueElement()); + } + } + + private void dontPoseAsTransient(LimitingResourceQueueElement element) { + if (element != null) { + element.dontPoseAsTransientObjectAnymore(); } } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SubcontractModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SubcontractModel.java index 2bc93714c..f4c810d12 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SubcontractModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/order/SubcontractModel.java @@ -106,7 +106,7 @@ public class SubcontractModel implements ISubcontractModel { .applyChanges(subcontractedTaskData); } - task.removeAllResourceAllocations(); + task.removeAllSatisfiedResourceAllocations(); convertOnStartOnFixedDate(task); } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/IGlobalViewEntryPoints.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/IGlobalViewEntryPoints.java index de7d5db3b..307c2dc13 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/IGlobalViewEntryPoints.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/IGlobalViewEntryPoints.java @@ -37,6 +37,9 @@ public interface IGlobalViewEntryPoints { @EntryPoint("company_load") public void goToCompanyLoad(); + @EntryPoint("limiting_resources") + public void goToLimitingResources(); + @EntryPoint("orders_list") public void goToOrdersList(); @@ -46,4 +49,6 @@ public interface IGlobalViewEntryPoints { @EntryPoint( { "orderElement", "order" }) public void goToOrderElementDetails(OrderElement orderElement, Order order); + void goToCompanyLimitingResources(); + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/LimitingResourcesTabCreator.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/LimitingResourcesTabCreator.java new file mode 100644 index 000000000..170ba6bfe --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/LimitingResourcesTabCreator.java @@ -0,0 +1,158 @@ +/* + * This file is part of NavalPlan + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.navalplanner.web.planner.tabs; + +import static org.navalplanner.web.I18nHelper._; +import static org.navalplanner.web.planner.tabs.MultipleTabsPlannerController.BREADCRUMBS_SEPARATOR; +import static org.navalplanner.web.planner.tabs.MultipleTabsPlannerController.PLANNIFICATION; + +import java.util.HashMap; +import java.util.Map; + +import org.navalplanner.business.orders.entities.Order; +import org.navalplanner.web.limitingresources.LimitingResourcesController; +import org.navalplanner.web.planner.tabs.CreatedOnDemandTab.IComponentCreator; +import org.zkoss.ganttz.extensions.ITab; +import org.zkoss.ganttz.resourceload.ResourcesLoadPanel.IToolbarCommand; +import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.Executions; +import org.zkoss.zul.Image; +import org.zkoss.zul.Label; + +/** + * @author Lorenzo Tilve Álvaro + */ +public class LimitingResourcesTabCreator { + + private static final String LIMITING_RESOURCES_VIEW = _("Limiting resources"); + + /* Unnecesary */ + private static final String ORDER_LIMITING_RESOURCES_VIEW = _("Limiting resources (order)"); + + public static ITab create(Mode mode, + LimitingResourcesController LimitingResourcesController, + IToolbarCommand upCommand, + LimitingResourcesController LimitingResourcesControllerGlobal, + Component breadcrumbs) { + return new LimitingResourcesTabCreator(mode, + LimitingResourcesController, LimitingResourcesControllerGlobal, + breadcrumbs) + .build(); + } + + private final Mode mode; + private final LimitingResourcesController LimitingResourcesController; + + private final LimitingResourcesController LimitingResourcesControllerGlobal; + + private final Component breadcrumbs; + + private LimitingResourcesTabCreator(Mode mode, + LimitingResourcesController LimitingResourcesController, + LimitingResourcesController LimitingResourcesControllerGlobal, + Component breadcrumbs) { + this.mode = mode; + this.LimitingResourcesController = LimitingResourcesController; + this.LimitingResourcesControllerGlobal = LimitingResourcesControllerGlobal; + this.breadcrumbs = breadcrumbs; + } + + private ITab build() { + return TabOnModeType.forMode(mode) +.forType(ModeType.GLOBAL, + createGlobalLimitingResourcesTab()).forType(ModeType.ORDER, + createOrderLimitingResourcesTab()) + .create(); + } + + private ITab createOrderLimitingResourcesTab() { + IComponentCreator componentCreator = new IComponentCreator() { + + @Override + /* Should never be called */ + public org.zkoss.zk.ui.Component create( + org.zkoss.zk.ui.Component parent) { + Map arguments = new HashMap(); + // LimitingResourcesController.add(upCommand); + arguments.put("LimitingResourcesController", + LimitingResourcesController); + return Executions.createComponents( + "/limitingresources/_limitingresources.zul", parent, + arguments); + } + + }; + return new CreatedOnDemandTab(ORDER_LIMITING_RESOURCES_VIEW, + "order-limiting-resources", + componentCreator) { + + @Override + protected void afterShowAction() { + breadcrumbs.getChildren().clear(); + breadcrumbs.appendChild(new Image(BREADCRUMBS_SEPARATOR)); + breadcrumbs.appendChild(new Label(PLANNIFICATION)); + breadcrumbs.appendChild(new Image(BREADCRUMBS_SEPARATOR)); + breadcrumbs + .appendChild(new Label(ORDER_LIMITING_RESOURCES_VIEW)); + breadcrumbs.appendChild(new Image(BREADCRUMBS_SEPARATOR)); + Order currentOrder = mode.getOrder(); + LimitingResourcesController.filterBy(currentOrder); + LimitingResourcesController.reload(); + breadcrumbs.appendChild(new Label(currentOrder.getName())); + } + }; + } + + private ITab createGlobalLimitingResourcesTab() { + + final IComponentCreator componentCreator = new IComponentCreator() { + + @Override + public org.zkoss.zk.ui.Component create( + org.zkoss.zk.ui.Component parent) { + Map arguments = new HashMap(); + arguments.put("LimitingResourcesController", + LimitingResourcesControllerGlobal); + return Executions.createComponents( + "/limitingresources/_limitingresources.zul", parent, + // "/resourceload/_resourceload.zul", parent, + arguments); + } + + }; + return new CreatedOnDemandTab(LIMITING_RESOURCES_VIEW, + "limiting-resources", + componentCreator) { + @Override + protected void afterShowAction() { + LimitingResourcesControllerGlobal.filterBy(null); + LimitingResourcesControllerGlobal.reload(); + if (breadcrumbs.getChildren() != null) { + breadcrumbs.getChildren().clear(); + } + breadcrumbs.appendChild(new Image(BREADCRUMBS_SEPARATOR)); + breadcrumbs.appendChild(new Label(PLANNIFICATION)); + breadcrumbs.appendChild(new Image(BREADCRUMBS_SEPARATOR)); + breadcrumbs.appendChild(new Label(LIMITING_RESOURCES_VIEW)); + } + }; + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/MultipleTabsPlannerController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/MultipleTabsPlannerController.java index 8dfdcb3a2..f479e2293 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/MultipleTabsPlannerController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/tabs/MultipleTabsPlannerController.java @@ -33,6 +33,7 @@ import org.navalplanner.business.planner.entities.TaskElement; import org.navalplanner.business.resources.daos.IResourceDAO; import org.navalplanner.web.common.entrypoints.URLHandler; import org.navalplanner.web.common.entrypoints.URLHandlerRegistry; +import org.navalplanner.web.limitingresources.LimitingResourcesController; import org.navalplanner.web.orders.OrderCRUDController; import org.navalplanner.web.planner.allocation.AdvancedAllocationController.IBack; import org.navalplanner.web.planner.company.CompanyPlanningController; @@ -131,6 +132,8 @@ public class MultipleTabsPlannerController implements Composer, private ITab resourceLoadTab; + private ITab limitingResourcesTab; + private ITab ordersTab; private TabSwitcher tabsSwitcher; @@ -144,6 +147,12 @@ public class MultipleTabsPlannerController implements Composer, @Autowired private ResourceLoadController resourceLoadControllerGlobal; + @Autowired + private LimitingResourcesController limitingResourcesController; + + @Autowired + private LimitingResourcesController limitingResourcesControllerGlobal; + private org.zkoss.zk.ui.Component breadcrumbs; @Autowired @@ -193,6 +202,12 @@ public class MultipleTabsPlannerController implements Composer, } }, breadcrumbs); + + /* FIX */ + limitingResourcesTab = LimitingResourcesTabCreator.create(mode, + limitingResourcesController, upCommand(), + limitingResourcesControllerGlobal, breadcrumbs); + ordersTab = OrdersTabCreator.create(mode, orderCRUDController, breadcrumbs, new IOrderPlanningGate() { @@ -222,6 +237,8 @@ public class MultipleTabsPlannerController implements Composer, return TabsConfiguration.create() .add(tabWithNameReloading(planningTab, typeChanged)) .add(tabWithNameReloading(resourceLoadTab, typeChanged)) +.add( + tabWithNameReloading(limitingResourcesTab, typeChanged)) .add(tabWithNameReloading(ordersTab, typeChanged)) .add(visibleOnlyAtOrderMode(advancedAllocation)); } @@ -347,6 +364,11 @@ public class MultipleTabsPlannerController implements Composer, getTabsRegistry().show(resourceLoadTab); } + @Override + public void goToCompanyLimitingResources() { + getTabsRegistry().show(limitingResourcesTab); + } + @Override public void goToOrdersList() { // ordersTab.show(); @@ -371,4 +393,9 @@ public class MultipleTabsPlannerController implements Composer, orderCRUDController.highLight(orderElement); } + @Override + public void goToLimitingResources() { + getTabsRegistry().show(limitingResourcesTab); + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java index c66835aa6..0541db042 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/planner/taskedition/EditTaskController.java @@ -40,6 +40,7 @@ import org.navalplanner.web.planner.allocation.ResourceAllocationController; import org.navalplanner.web.planner.allocation.AdvancedAllocationController.IAdvanceAllocationResultReceiver; import org.navalplanner.web.planner.allocation.AdvancedAllocationController.Restriction; import org.navalplanner.web.planner.allocation.AdvancedAllocationController.Restriction.IRestrictionSource; +import org.navalplanner.web.planner.consolidations.AdvanceConsolidationController; import org.navalplanner.web.planner.order.PlanningState; import org.navalplanner.web.planner.order.SubcontractController; import org.navalplanner.web.planner.taskedition.TaskPropertiesController.ResourceAllocationTypeEnum; @@ -72,6 +73,9 @@ public class EditTaskController extends GenericForwardComposer { @Autowired private LimitingResourceAllocationController limitingResourceAllocationController; + @Autowired + private AdvanceConsolidationController advanceConsolidationController; + @Autowired private SubcontractController subcontractController; @@ -82,10 +86,12 @@ public class EditTaskController extends GenericForwardComposer { private Tab resourceAllocationTab; private Tab limitingResourceAllocationTab; private Tab subcontractTab; + private Tab advanceConsolidationTab; private Tabpanel taskPropertiesTabpanel; private Tabpanel resourceAllocationTabpanel; private Tabpanel limitingResourceAllocationTabpanel; + private Tabpanel advanceConsolidationTabpanel; private Tabpanel subcontractTabpanel; private Component messagesContainer; @@ -104,6 +110,8 @@ public class EditTaskController extends GenericForwardComposer { window = (Window) comp; taskPropertiesController.doAfterCompose(taskPropertiesTabpanel); resourceAllocationController.doAfterCompose(resourceAllocationTabpanel); + advanceConsolidationController + .doAfterCompose(advanceConsolidationTabpanel); subcontractController.doAfterCompose(subcontractTabpanel); limitingResourceAllocationController.doAfterCompose(limitingResourceAllocationTabpanel); messagesForUser = new MessagesForUser(messagesContainer); @@ -121,6 +129,10 @@ public class EditTaskController extends GenericForwardComposer { return limitingResourceAllocationController; } + public AdvanceConsolidationController getAdvanceConsolidationController() { + return advanceConsolidationController; + } + public SubcontractController getSubcontractController() { return subcontractController; } @@ -132,13 +144,10 @@ public class EditTaskController extends GenericForwardComposer { this.planningState = planningState; taskPropertiesController.init(this, context, taskElement); - if (taskElement instanceof Task) { - resourceAllocationController.init(context, (Task) taskElement, + + if (isTask(taskElement)) { + advanceConsolidationController.init(context, (Task) taskElement, planningState, messagesForUser); - limitingResourceAllocationController.init((Task) taskElement, messagesForUser); - if (taskElement.isSubcontracted()) { - subcontractController.init((Task) taskElement, context); - } } try { @@ -202,8 +211,14 @@ public class EditTaskController extends GenericForwardComposer { public void showEditFormResourceAllocation( IContextWithPlannerTask context, TaskElement taskElement, PlanningState planningState) { - if (isNotSubcontractedAndIsTask(taskElement)) { - editTaskTabbox.setSelectedPanelApi(resourceAllocationTabpanel); + + if (isTask(taskElement)) { + Task task = asTask(taskElement); + if (task.isLimiting()) { + editTaskTabbox.setSelectedPanelApi(limitingResourceAllocationTabpanel); + } else { + editTaskTabbox.setSelectedPanelApi(resourceAllocationTabpanel); + } } else { editTaskTabbox.setSelectedPanelApi(taskPropertiesTabpanel); } @@ -221,6 +236,17 @@ public class EditTaskController extends GenericForwardComposer { showEditForm(context, taskElement, planningState); } + public void showEditFormAdvanceConsolidation( + IContextWithPlannerTask context, + TaskElement taskElement, PlanningState planningState) { + if (isNotSubcontractedAndIsTask(taskElement)) { + editTaskTabbox.setSelectedPanelApi(advanceConsolidationTabpanel); + } else { + editTaskTabbox.setSelectedPanelApi(taskPropertiesTabpanel); + } + showEditForm(context, taskElement, planningState); + } + public void accept() { try { if (taskPropertiesController.stateHasChanged()) { @@ -231,6 +257,12 @@ public class EditTaskController extends GenericForwardComposer { editTaskTabbox.setSelectedPanelApi(taskPropertiesTabpanel); taskPropertiesController.accept(); + if (isTask(taskElement)) { + editTaskTabbox + .setSelectedPanelApi(advanceConsolidationTabpanel); + advanceConsolidationController.accept(); + } + ResourceAllocationTypeEnum currentState = taskPropertiesController.getCurrentState(); if (ResourceAllocationTypeEnum.NON_LIMITING_RESOURCES.equals(currentState)) { editTaskTabbox.setSelectedPanelApi(resourceAllocationTabpanel); @@ -238,6 +270,9 @@ public class EditTaskController extends GenericForwardComposer { } else if (ResourceAllocationTypeEnum.SUBCONTRACT.equals(currentState)) { editTaskTabbox.setSelectedPanelApi(subcontractTabpanel); subcontractController.accept(); + } else if (ResourceAllocationTypeEnum.LIMITING_RESOURCES.equals(currentState)) { + editTaskTabbox.setSelectedPanelApi(limitingResourceAllocationTabpanel); + limitingResourceAllocationController.accept(); } askForReloads(); @@ -276,6 +311,7 @@ public class EditTaskController extends GenericForwardComposer { taskPropertiesController.cancel(); subcontractController.cancel(); resourceAllocationController.cancel(); + advanceConsolidationController.cancel(); taskElement = null; context = null; @@ -392,4 +428,4 @@ public class EditTaskController extends GenericForwardComposer { } } -} \ No newline at end of file +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java index a38138593..1c29e86b7 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/IResourceLoadModel.java @@ -24,6 +24,8 @@ import java.util.List; import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.Resource; import org.zkoss.ganttz.data.resourceload.LoadTimeLine; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; import org.zkoss.ganttz.util.Interval; @@ -43,4 +45,13 @@ public interface IResourceLoadModel { Order getOrderByTask(TaskElement task); boolean userCanRead(Order order, String loginName); + + void setResourcesToShow(List resourcesList); + + void clearResourcesToShow(); + + void setCriteriaToShow(List criteriaList); + + void clearCriteriaToShow(); + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java index fe92d4b9d..221a00d73 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadController.java @@ -29,6 +29,10 @@ import java.util.List; import org.apache.commons.lang.Validate; import org.navalplanner.business.orders.entities.Order; import org.navalplanner.business.planner.entities.TaskElement; +import org.navalplanner.business.resources.entities.Criterion; +import org.navalplanner.business.resources.entities.Resource; +import org.navalplanner.web.common.components.bandboxsearch.BandboxMultipleSearch; +import org.navalplanner.web.common.components.finders.FilterPair; import org.navalplanner.web.planner.order.BankHolidaysMarker; import org.navalplanner.web.planner.order.IOrderPlanningGate; import org.navalplanner.web.security.SecurityUtils; @@ -44,7 +48,12 @@ import org.zkoss.ganttz.resourceload.ResourcesLoadPanel.IToolbarCommand; import org.zkoss.ganttz.timetracker.TimeTracker; import org.zkoss.ganttz.timetracker.zoom.SeveralModificators; import org.zkoss.ganttz.timetracker.zoom.ZoomLevel; +import org.zkoss.zk.ui.event.Event; +import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.util.Composer; +import org.zkoss.zul.Button; +import org.zkoss.zul.Hbox; import org.zkoss.zul.Messagebox; /** @@ -74,6 +83,11 @@ public class ResourceLoadController implements Composer { private IOrderPlanningGate planningControllerEntryPoints; + private BandboxMultipleSearch bandBox; + + private boolean currentFilterByResources = true; + private boolean filterHasChanged = false; + public ResourceLoadController() { } @@ -98,6 +112,9 @@ public class ResourceLoadController implements Composer { private void reload(boolean filterByResources) { try { + this.filterHasChanged = (filterByResources != currentFilterByResources); + this.currentFilterByResources = filterByResources; + if (filterBy == null) { resourceLoadModel.initGlobalView(filterByResources); } else { @@ -110,7 +127,6 @@ public class ResourceLoadController implements Composer { this.parent.appendChild(resourcesLoadPanel); resourcesLoadPanel.afterCompose(); - addListeners(); addCommands(resourcesLoadPanel); } catch (IllegalArgumentException e) { try { @@ -164,15 +180,91 @@ public class ResourceLoadController implements Composer { private void buildResourcesLoadPanel() { if (resourcesLoadPanel != null) { + if(bandBox != null) { + //if the filter has changed, we have to clear the model and + //the bandbox, and change its finder + if(filterHasChanged) { + if(currentFilterByResources) { + bandBox.setFinder("workerMultipleFiltersFinder"); + resourceLoadModel.clearCriteriaToShow(); + } + else { + bandBox.setFinder("criterionMultipleFiltersFinder"); + resourceLoadModel.clearResourcesToShow(); + } + bandBox.clear(); + bandBox.afterCompose(); + } + + //if the bandbox filter is active, we disable the name filter + resourcesLoadPanel.setNameFilterDisabled( + !bandBox.getSelectedElements().isEmpty()); + } resourcesLoadPanel.init(resourceLoadModel.getLoadTimeLines(), timeTracker); } else { resourcesLoadPanel = new ResourcesLoadPanel(resourceLoadModel .getLoadTimeLines(), timeTracker, parent); + if(filterBy == null) { + addWorkersBandbox(); + } addListeners(); } } + private void addWorkersBandbox() { + bandBox = new BandboxMultipleSearch(); + bandBox.setId("workerBandboxMultipleSearch"); + bandBox.setWidthBandbox("285px"); + bandBox.setWidthListbox("300px"); + bandBox.setFinder("workerMultipleFiltersFinder"); + bandBox.afterCompose(); + + Button button = new Button(); + button.setImage("/common/img/ico_filter.png"); + button.setTooltip(_("Filter by worker")); + button.addEventListener(Events.ON_CLICK, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + if(currentFilterByResources) { + filterResourcesFromBandbox(); + } + else { + filterCriteriaFromBandbox(); + } + } + }); + + Hbox hbox = new Hbox(); + hbox.appendChild(bandBox); + hbox.appendChild(button); + hbox.setAlign("center"); + + resourcesLoadPanel.setVariable("additionalFilter", hbox, true); + } + + @SuppressWarnings("unchecked") + private void filterResourcesFromBandbox() { + List filterPairList = bandBox.getSelectedElements(); + List workersList = new ArrayList(); + for(FilterPair filterPair : filterPairList) { + workersList.add((Resource)filterPair.getValue()); + } + resourceLoadModel.setResourcesToShow(workersList); + reload(true); + } + + @SuppressWarnings("unchecked") + private void filterCriteriaFromBandbox() { + List filterPairList = bandBox.getSelectedElements(); + List criteriaList = new ArrayList(); + for(FilterPair filterPair : filterPairList) { + criteriaList.add((Criterion)filterPair.getValue()); + } + resourceLoadModel.setCriteriaToShow(criteriaList); + reload(false); + } + public void filterBy(Order order) { this.filterBy = order; } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java index 65704f9bb..ec3fbca4a 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/resourceload/ResourceLoadModel.java @@ -107,6 +107,10 @@ public class ResourceLoadModel implements IResourceLoadModel { private boolean filterByResources = true; + private List resourcesToShowList = new ArrayList(); + + private List criteriaToShowList = new ArrayList(); + @Override @Transactional(readOnly = true) public void initGlobalView(boolean filterByResources) { @@ -184,6 +188,10 @@ public class ResourceLoadModel implements IResourceLoadModel { } private Map> genericAllocationsByCriterion() { + if(!criteriaToShowList.isEmpty()) { + return resourceAllocationDAO + .findGenericAllocationsBySomeCriterion(criteriaToShowList); + } if (filter()) { List criterions = new ArrayList(); List generics = new ArrayList(); @@ -205,6 +213,10 @@ public class ResourceLoadModel implements IResourceLoadModel { } private List resourcesToShow() { + if(!resourcesToShowList.isEmpty()) { + return getResourcesToShowReattached(); + } + // if we haven't manually specified some resources to show, we load them if (filter()) { return resourcesForActiveTasks(); } else { @@ -248,7 +260,7 @@ public class ResourceLoadModel implements IResourceLoadModel { Map> genericAllocationsByCriterion) { List result = new ArrayList(); List criterions = Criterion - .sortByName(genericAllocationsByCriterion.keySet()); + .sortByTypeAndName(genericAllocationsByCriterion.keySet()); for (Criterion criterion : criterions) { List allocations = ResourceAllocation .sortedByStartDate(genericAllocationsByCriterion @@ -397,8 +409,8 @@ public class ResourceLoadModel implements IResourceLoadModel { private LoadTimeLine createPrincipal(Criterion criterion, List orderedAllocations, TimeLineRole role) { - return new LoadTimeLine(criterion.getName(), createPeriods(criterion, - orderedAllocations), "global-generic", role); + return new LoadTimeLine(criterion.getType().getName() + ": " + criterion.getName(), + createPeriods(criterion, orderedAllocations), "global-generic", role); } private List createPeriods(Criterion criterion, @@ -424,7 +436,7 @@ public class ResourceLoadModel implements IResourceLoadModel { .findAllocationsRelatedTo(resource)); TimeLineRole role = getCurrentTimeLineRole(resource); LoadTimeLine result = new LoadTimeLine(buildTimeLine(resource, resource - .getShortDescription(), sortedByStartDate, "resource", role), + .getName(), sortedByStartDate, "resource", role), buildSecondLevel(resource, sortedByStartDate)); return result; @@ -656,6 +668,43 @@ public class ResourceLoadModel implements IResourceLoadModel { .getStart()), new LocalDate(interval.getFinish())); } + @Override + public void setResourcesToShow(List resourcesList) { + this.resourcesToShowList.clear(); + this.resourcesToShowList.addAll(resourcesList); + } + + @Override + public void clearResourcesToShow() { + resourcesToShowList.clear(); + } + + private List getResourcesToShowReattached() { + List list = new ArrayList(); + for(Resource worker : resourcesToShowList) { + try { + //for some reason, resourcesDAO.reattach(worker) doesn't work + //and we have to retrieve them again with find + list.add(resourcesDAO.find(worker.getId())); + } + catch(InstanceNotFoundException e) { + //maybe it was removed by another transaction + //we just ignore the exception to not show the Resource + } + } + return list; + } + + @Override + public void clearCriteriaToShow() { + criteriaToShowList.clear(); + } + + @Override + public void setCriteriaToShow(List criteriaList) { + criteriaToShowList.clear(); + criteriaToShowList.addAll(criteriaList); + } } class PeriodsBuilder { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/tree/TreeController.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/tree/TreeController.java index 63f8c99a2..5f0ffca5f 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/web/tree/TreeController.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/tree/TreeController.java @@ -58,6 +58,7 @@ import org.zkoss.zul.Treecell; import org.zkoss.zul.Treeitem; import org.zkoss.zul.TreeitemRenderer; import org.zkoss.zul.Treerow; +import org.zkoss.zul.impl.api.InputElement; public abstract class TreeController> extends GenericForwardComposer { @@ -318,6 +319,20 @@ public abstract class TreeController> extends public Renderer() { } + protected void registerFocusEvent(final InputElement inputElement) { + inputElement.addEventListener(Events.ON_FOCUS, + new EventListener() { + + private Treeitem item = (Treeitem) getCurrentTreeRow().getParent(); + + @Override + public void onEvent(Event event) throws Exception { + item.setSelected(true); + Util.reloadBindings(item.getParent()); + } + }); + } + protected Treecell addCell(Component... components) { return addCell(null, components); } @@ -329,6 +344,9 @@ public abstract class TreeController> extends } for (Component component : components) { cell.appendChild(component); + if(component instanceof InputElement) { + registerFocusEvent((InputElement) component); + } } currentTreeRow.appendChild(cell); return cell; @@ -426,6 +444,16 @@ public abstract class TreeController> extends onDoubleClickForSchedulingStateCell(currentElement); } }); + cell.addEventListener(Events.ON_CLICK, new EventListener() { + + private Treeitem item = (Treeitem) getCurrentTreeRow().getParent(); + + @Override + public void onEvent(Event event) throws Exception { + item.setSelected(true); + Util.reloadBindings(item.getParent()); + } + }); schedulingState.addTypeChangeListener( new ITypeChangedListener() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/calendars/api/CalendarDataDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/calendars/api/CalendarDataDTO.java index e9105eb5c..ac21f8ac3 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/calendars/api/CalendarDataDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/calendars/api/CalendarDataDTO.java @@ -40,7 +40,7 @@ public class CalendarDataDTO extends IntegrationEntityDTO { public final static String ENTITY_TYPE = "calendar-data"; - @XmlElementWrapper(name = "hors-per-day-list") + @XmlElementWrapper(name = "hours-per-day-list") @XmlElement(name = "hours-per-day") public List hoursPerDays = new ArrayList(); diff --git a/navalplanner-webapp/src/main/resources/metainfo/zk/lang-addon.xml b/navalplanner-webapp/src/main/resources/metainfo/zk/lang-addon.xml index bd9d774e6..64462c444 100755 --- a/navalplanner-webapp/src/main/resources/metainfo/zk/lang-addon.xml +++ b/navalplanner-webapp/src/main/resources/metainfo/zk/lang-addon.xml @@ -82,4 +82,45 @@ /common/components/templateFinder.zul + + LimitingResourcesPanel + org.navalplanner.web.limitingresources.LimitingResourcesPanel + /limitingresources/limitingResourcesLayout.zul + + + + LimitingResourcesList + org.navalplanner.web.limitingresources.LimitingResourcesList + + default + /limitingresources/limitingresourceslist.dsp + + + + + LimitingResourcesLeftPane + org.navalplanner.web.limitingresources.LimitingResourcesLeftPane + /limitingresources/leftPane.zul + + + + limitingresources + org.navalplanner.web.limitingresources.LimitingResourcesComponent + + default + /limitingresources/limitingresources.dsp + + + + + + limitingdependencylist + org.navalplanner.web.limitingresources.LimitingDependencyList + + default + /limitingresources/limitingdependencylist.dsp + + + + diff --git a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml index 857711250..e346d8e1e 100644 --- a/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml +++ b/navalplanner-webapp/src/main/resources/navalplanner-webapp-spring-config.xml @@ -37,6 +37,7 @@ + @@ -52,6 +53,11 @@ + + + diff --git a/navalplanner-webapp/src/main/webapp/calendars/_edition.zul b/navalplanner-webapp/src/main/webapp/calendars/_edition.zul index 14c789052..a295a7e09 100644 --- a/navalplanner-webapp/src/main/webapp/calendars/_edition.zul +++ b/navalplanner-webapp/src/main/webapp/calendars/_edition.zul @@ -91,8 +91,7 @@ @@ -132,11 +128,9 @@