# Contract Allocation

## Story

In this model we have a set of contracts, where every contract represents an amount of commodity that has to be supplied. The objective is to determine which of the producers will take care of which contract such that the total costs are minimal, under the following conditions:

• The demand for every contract is met.

• The amount supplied by each producer does not exceed the total amount available for supply.

• If a producer supplies a part of a contract then this contribution has a given minimal size.

• There is a minimal number of suppliers for every contract.

• The total cost associated with all the deliveries is minimal.

## Mathematical Model

This AIMMS project illustrates the use of a semi-continuous variable. A semi-continuous variable is either zero or within a certain range. This type of variables can be used in conditions like, whenever there is a transport this transport has a minimum size.

Contract Allocation Problem

Sets and indices:

$$P$$, $$p \in P$$

Producers

$$C$$, $$c \in C$$

Contracts

Parameters:

$$M_{p} \in \mathbb{R_{+}}$$

minimal delivery

$$A_{p} \in \mathbb{R_{+}}$$

available capacity

$$S_{c} \in \mathbb{R_{+}}$$

contract size

$$N_{c} \in \mathbb{R_{+}}$$

minimal number of contributors

$$T_{p,c} \in \mathbb{R_{+}}$$

delivery cost by p for c

Variables:

$$X_{p,c} \in \{0\} \cup \{M_{p}..10000\}$$

amount of commodity delivered by p to c

$$Y_{p,c} \in \{0..1\}$$

p produce to c

Constraints:

1

$$\forall p: \sum_c X_{p,c} \leq A_{p}$$

production capacity for p

2

$$\forall c: \sum_p X_{p,c} \geq S_{c}$$

demand fulfillment for c

3

$$\forall c: \sum_p X_{p,c} \geq N_{c}$$

minimal number of contributors to c

4

$$\forall p, c: X_{p,c} \geq M_{p} * Y_{p,c}$$

if p delivers to c

Minimize:

$$\sum_{p,c} T_{p,c} * X_{p,c}$$

The number of matches

## Language

In this example, there are two main ways to import data: by a custom Excel file, and by a pre-defined Excel which is currently on the project’s main directory. You can choose which one to use on the inputs page through the import dialog page.

For the default data import, you will be importing 10 northwestern states for the contracts and 5 cities from that region for the producers. You can add more data freely without changing the sheets structure.

Procedure pr_importExcelData

This procedure will add and read the xml mapping available. Take a look at Mappings/inputs.xml.

 1dex::AddMapping(
2   mappingName :  "inputs",
3   mappingFile :  "Mappings/inputs.xml");
4
6   dataFile         :  "DefaultData.xlsx",
7   mappingName      :  "inputs",
8   emptyIdentifiers :  1,
9   emptySets        :  1,
10   resetCounters    :  1);
11
12ep_actualContract := first(i_contract);
13ep_actualProducer := first(i_producer);


For the custom Excel file import, you can either copy the structure from the default data Excel or download the template file. Add your data, and then, use the upload button to select the Excel to import.

This procedure will make you select a Excel file on your computer to import.

 1block ! import a custom Excel file
2   ! we store the location of the file in string parameter UploadLocation
4
6
9      mappingName      :  "inputs",
10      emptyIdentifiers :  1,
11      emptySets        :  1,
12      resetCounters    :  1)
13   then
14      ! if successful, statusCode is set to 'OK' which will trigger the WebUI to show the message below in a grey box
15      StatusCode := webui::ReturnStatusCode('OK');
16
17      ! displaying the status message, and logging it in the WebUI messages
19
21   endif;
22
23onerror ep_err do
24   ! setting the statusCode to 'ERROR'
25   statusCode := webui::ReturnStatusCode('ERROR');
26
27   !displaying a custom error message
28   statusDescription := "Error when reading file " + errh::Message( ep_err );
29   errh::MarkAsHandled(ep_err) ;
30
32endblock;


We also use create a page action and a dialog for users to export the results to several different DEX supported formats (Excel, JSON, CSV, etc.). You can add more identifiers to be exported by using the DEX annotations.

Procedure pr_openExportPage

This procedure will generate all the possible mappings in DEX based on current identifier DEX annotations. Details on how to setup annotations can be found here. It will also initialize identifiers used in our dialog and open that dialog page.

 1!Generating all possible mappings
2dex::GenerateDatasetMappings();
3
4!Selecting the Excel mapping as initial value
5if not ep_selectedMapping then
6   ep_selectedMapping
7   :=  First(i_generatedMappings | FindString(
8            SearchString  :  i_generatedMappings,
9            Key           :  "excel",
10            CaseSensitive :  0,
11            WordOnly      :  0,
12            IgnoreWhite   :  0));
13endif;
14
15!Defining Dialog and actions - Only done required
16s_actions:= data { Done };
17ep_pageId := 'export_page';
18
19!Opening dialog page - no action on done - webui::NoOp1 does nothing
20webui::OpenDialogPage(
21   pageId  :  ep_pageId,
22   title   :  "Export Data",
23   actions :  s_actions,
24   onDone  :  'webui::NoOp1');

Procedure pr_exportExcelData

 1! we want to download a file
2sp_out_fileLocation := sp_FileName;
3
4! we store the location of the file in string parameter FinalLocation
5sp_loc_FinalLocation := webui::GetIOFilePath(sp_out_fileLocation);
6
7! writing the output file locally
8dex::WriteToFile(
9   dataFile    :  sp_loc_FinalLocation,
10   mappingName :  ep_selectedMapping,
11   pretty      :  1);
12
13! checking if the previous write statement was successful or not
14if FileExists(sp_loc_FinalLocation) then
15
16   ! if successful, statusCode is set to 'CREATED' which will trigger the download widget to show the Get button
17   p_out_statusCode := webui::ReturnStatusCode('CREATED');
20
21else    !if previous write statement was not successful
22
23   ! setting the statusCode to 'ERROR' and the download widget will not show the Get button anymore
24   p_out_statusCode := webui::ReturnStatusCode('ERROR');
25   !displaying a custom error message
26   sp_out_statusDescription := "Something went wrong when creating the file.";
27
28endif;


To understand in depth check out DEX documentation.

## WebUI Features

On input page, if you click around the graphs, a highlighted cell will appear identifying the last clicked element. The results are displayed in a combination chart (stacked bar chart).

The following WebUI features are used:

## UI Styling

Below there are the css files you will find with comments on what they change.

 1.annotation-bkg-cell {
2   background: var(--primary90Transparent);
3}
4
5.annotation-bkg-cell-default {
6   background: var(--primary90Transparent);
7}
8
9.annotation-bkg-cell-default input{
10   color: transparent;
11}
12
13.annotation-reach-maximum {
14   background: rgba(255, 0, 0, 0.438);
15}
16
17.annotation-reach-minimum {
18   background: rgba(255, 255, 0, 0.438);
19}
20
21.annotation-between {
22   background: rgba(0, 128, 0, 0.438);
23}

 1:root {
2/*---------------------------------------------------------------------
3      COLORS
4----------------------------------------------------------------------*/
5--primary: #3DDAB4;
6--primaryDark: #00B569;
7--primary90Transparent: #3ddab33b;
8
9/*---------------------------------------------------------------------
10      LOGO
11----------------------------------------------------------------------*/
12--bg_app-logo: 15px 50% / 30px 30px no-repeat url(/app-resources/resources/images/budgeting.png);
13--spacing_app-logo_width: 45px;
14
15
16--color_bg_button_primary: var(--primaryDark);
17--color_bg_button_primary_hover: var(--primary);
19
20
21/*---------------------------------------------------------------------
22      WORKFLOW
23----------------------------------------------------------------------*/
26
27/* Step background and content (text, icon) colors for the 4 states*/
28/*current + current with error*/
29--color_bg_workflow_current: var(--primaryDark);
30--color_workflow_current: var(--color_text_inverted);
31--color_bg_workflow_error-current: #d1454b;
32
33/*active*/
34--color_bg_workflow_active: #e6edff;
35--color_workflow_active: var(--primaryDark);
36
37/*inactive*/
38--color_bg_workflow_inactive: #dde0e8;
39--color_workflow_inactive: #b0b5c2;
40
41/*error*/
42--color_bg_workflow_error: #f9e9e9;
43--color_workflow_error: #d1454b;
44
45/* Child indentation, border colors */
46--spacing_workflow-child-indent: 1rem;
47--color_workflow-item-divider: var(--primaryDark);
48
49/* Icon background, border, for non-error state */
50--color_bg_workflow-icon: #ffffff;
51--color_workflow-icon-border: var(--primaryDark);
52}

 1/*Change table text color*/
4   color: var(--primaryDark);
5}
6
7/*Change scalar text color*/
8.tag-scalar .kpi .value {
9   color: var(--primaryDark);
10}
11
13.ql-snow a {
14   color: var(--primaryDark);
15}
16
17/*Change table default text color*/
18.tag-table .grid-viewport .cell.flag-default,
19html:not(.using-touch) .tag-table .grid-viewport .cell.flag-default {
20   color: white;
21}

1/*Add image on the background*/
2.scroll-wrapper--pagev2 .page-container {
3   content: " ";
4   background: url(img/RightBackground.png) rgb(249, 249, 249) no-repeat left/contain;
5}

1.theme-aimms header.tag-application {
2   border-bottom: 2px solid var(--primary);
3}

1/*Change color of togglelegend of the combination chart*/
2.togglelegend-button svg{
3   fill: var(--primaryDark);
4}
5
6.togglelegend-button-active:hover svg g, .togglelegend-button-active svg g {
7   fill: var(--primary);
8}

 1/*Change color after tab click*/
2.sidepanel-container .sidepanel-tab.active {
3   background-color: var(--primary);
4}
5
6/*Change letter color on hover*/
7.sidepanel-container .sidepanel-tab.active:hover {
8   color: white;
9}
10
11/*Change icon color*/
12.sidepanel-container .sidepanel-tab .sidepanel-icon,
13.sidepanel-container .sidepanel-tab:hover {
14   color: var(--primary);
15}
16
17/*Change color after all tabs*/
18.sidepanel-container .sidepanel-tabs-container:after {
19   background: var(--primary);
20}
21
22/*Change the color below sidepanel tabs*/
23.sidepanel-container {
24   background-color:   rgb(249, 249, 249);
25}
26
27.sidepanel-container .sidepanel-tab {
28   height: 180px;
29}

1/*Change color of the busy button*/
2.veil-msg.state-busy .ui-button {
3   background-color: var(--primary);
4}

 1.page-action-v2 .page-action-menu,
3   background: var(--primaryDark);
4}
5
8   background: var(--primary);
9}
10
11.page-action-v2 .page-action-holder .page-action-item .page-action-icon,
12.page-action-v2 .page-action-holder .page-action-item .page-action-letter {
13   background-color: var(--primaryDark);
14}
15
16.page-action-v2 .page-action-holder .page-action-item .page-action-icon:hover,
17.page-action-v2 .page-action-holder .page-action-item .page-action-letter:hover {
18   background-color: var(--primary);
19}

1.tag-table.focused .focus-cell {
2   box-shadow: inset 0 0 0 2px var(--primaryDark);
3}


## Minimal Requirements

AIMMS Community license is sufficient for working with this example.

## Release Notes

v1.3 (09/08/2023)

v1.2 (15/06/2023)

Updated to 4.95 and added dependent styling using annotation on Results page.

v1.1 (15/05/2023)

Updated to 4.94 and improved Input page for better UX flow.

v1.0 (17/03/2023)

First logged version with the new workflow structure and colors.