Skip to content

OpenConstructionERP v17.8.0

Choose a tag to compare

@github-actions github-actions released this 21 Sep 22:23
· 2326 commits to main since this release

Approving a change order no longer counts the project budget twice. Since 17.7.0 each approved change order wrote its budget line with the whole project budget carried forward as its original figure. The Finance tab, EVM snapshots and the change order impact preview add up every budget line, so a project with a budget of 100,000 and two change orders of 10,000 and 5,000 showed 310,000 original and 325,000 revised instead of 100,000 and 115,000. A change order line now carries only its own change again. A project with no budget lines of its own takes its original budget from the project, so it still does not show a zero budget after its first change order. Lines written by 17.7.0 and 17.7.1 are repaired automatically when the application starts. Only lines that match that exact shape are rewritten, baseline lines and anything edited by hand are left as they are, and the number of repaired lines is written to the log and the data repair ledger.

A change order with no cost impact can be approved again. Since 17.7.0 approving one failed with a server error, and because execution starts from the approved status it could not be executed either, which stopped the ordinary case of an order that changes scope or time but not money. The budget writeback that raised the error now treats such an order as moving no money, which is what it did before 17.7.0.

An RFI can be printed. The RFI page has an Export PDF button that downloads the RFI as an A4 form: the project, the subject and question, the people by name, the dates, priority and discipline, the documents it references, the cost and schedule impact, and the official response, or an empty box to answer on paper when there is none yet, followed by signature lines for both sides. The form follows the interface language in every left-to-right language we ship, with labels as on the RFI screens and day counts in each language's plural forms. Arabic, Persian, Hebrew and Urdu get the English form for now, because the PDF fonts cannot yet lay out right-to-left text, and so does Bengali, which none of the bundled fonts can draw. A change order raised from the answer is named as a change order. The RFI log export was not fit to print either, listing people as raw ids and statuses as internal keys across several portrait pages. It now names people, spells out the status and prints landscape one page wide with the header row repeated on every page. The German status and discipline labels are spelled with their umlauts, and on the funding programmes tab the search icon and the field's padding no longer end up on opposite sides in right-to-left languages.

The RFI screens say what the English says in every language we ship. The form, the four step workflow card, the filters, the badges, the insights panel and the introduction had been translated word for word in most of them, so raising an RFI read as lifting or levying it, the ball in court became a sports ball or a tribunal, discipline became punishment, a change order became a purchase order and the need-by date read like the response deadline next to it. Some strings were half English or garbled. About a thousand strings across 43 languages were corrected, and the buttons, statuses and chips are now named the same way in the sentences that describe them. Spanish and Portuguese call the module RFI throughout instead of switching between RFI and SDI.

The desktop app can change its text size. Settings, Appearance has a Text size row with five steps from 90 to 150 percent, and Ctrl with plus, minus or zero does the same. The app zooms its own window the way a browser does, so menus and drawings stay aligned, and it remembers the size on that computer. In the browser the row points to the browser's own zoom.

Entering a price in a bill no longer freezes the editor. The grid read its selection settings as changed on every render of the page and closed the open cell editor each time, so a rate typed into a row that was not the active one vanished a moment later, a saved quantity closed the rate editor the user had just tabbed into, and the refresh after each save closed whatever was open when it landed. The editor now stays open and a save redraws only what changed. On Croatian, German and many other layouts the euro sign is AltGr and E, which Windows reports as Ctrl and Alt, so typing a price in euros also started an Excel export. The shortcuts ignore AltGr now, and export, import, lock and duplicate wait until the cell editor is closed. Resource rates and the manual resource dialog read 1.234,56 as the thousand it is, and the tier rate columns accept a comma decimal.

A price typed into a cell reached with the keyboard keeps its first digit. Tab, Enter and the arrow keys move to a cell without opening it, so the next key is the one that opens the editor, and the quantity, rate and unit editors dropped that key: 12,50 typed after Tab was saved as 2,50. They now start from the key that opened them, open empty on Backspace or Delete and open on the stored value for F2 or a click. A rate typed with its currency sign, such as 12,50 €, is read as the number instead of being refused. Escape cancels an edit in these three cells. It closed the editor, but closing it counted as leaving the cell, which saves, so the value typed before Escape was stored anyway.

A saved price is also much cheaper on the server. Every edit loaded the whole bill, each position and markup in it, several times over to check that it was not locked and to find its project, re-embedded the position for search although a price is not part of the searchable text, and the header rescanned every installed partner pack on each page. Those checks read the one column they need, the search index is only rewritten when the text changes, and the installed packs are read from a cache.

A large bill opens faster and no longer holds up everyone else while it does. Opening a bill read each of its positions three times, and on a bill of about 2,100 lines each read took long enough that twenty people opening it at once queued behind each other for most of a minute, with the health check waiting in the same queue. The positions are now read once per open.

Editing one price could change prices elsewhere in the project. When the edited position was the oldest one carrying a shared resource code, typing, pasting or filling down its unit rate rescaled its resource rates, and the server took that rescaling for the user redefining the resource and rewrote every other position in the project sharing the code. Paste, fill down, the batch factor and the other bulk edits reached it the same way. Only a resource change the user actually sends now spreads to the other positions, and when one does, only the positions that can carry the code are read and they are written in one pass, and finding them in a project of two thousand positions takes well under a second instead of several. Only 17.7.0 and 17.7.1 did this, because the rescaling that set it off arrived in 17.7.0, so prices edited on an earlier release are not affected. On those two releases, a position was re-priced this way when it shares a resource code with a position whose unit rate was typed, pasted, filled down or multiplied by a factor, and that edited position was the oldest one in the project carrying the code. Its resource prices then equal the edited position's and its unit rate follows from them. Anyone who edited rates on positions with shared resource codes on those releases should check the other positions carrying the same codes, most simply by comparing their totals with a snapshot or an export taken before the edit. Each such rewrite also left an entry in the activity panel of the bill where the price was typed, reading "Propagated resource definition" with the resource codes and the number of positions it reached.

The Windows server keeps accepting connections after a client gives up. Python's default event loop on Windows closes the listening socket for good when one accept fails, and a browser that abandons a connection before it is accepted makes it fail, so the app went on running but answered nothing until it was restarted. The server now stays on that loop with an accept that waits for the next client instead.

Other people working in the app now show up as they come and go. The presence connection read each arriving user from a field the server does not send, so every join and every page change by someone else raised an error in the browser and the list kept only the people who were there when the page opened.

Change orders and Variations are told apart in every language. They are two modules, one pricing a change to the contract sum and one following a change event from notice to agreement, but many languages translated both with one word, so the menu showed two entries with the same name, and buttons that create a change order said Variation. Each language now has its own word for each, used in the menu, on both module pages and in the buttons and messages that lead to them, and the buttons on an RFI and on a non-conformance report say Create Change Order in English as well. The RFI step by step guide is translated into every language, and the usage count on a cost item says position or positions by the number instead of the singular for any count.

The Zurich and Lausanne demo projects price in Swiss francs at a Swiss price level. The demo cost table had no franc row, so both seeded German prices under a CHF label; the new row keeps materials and labour apart like every other one. The Vancouver demo now carries GST and PST as one 12% tax line, the same money on the same base, and the message shown when a bill fails to load names the bill the way the reader's trade does in German, French, Italian, Chinese, Japanese and Korean instead of saying BOQ.

Sixty-six Uzbek strings write the o and g marks and the glottal stop with the letters Uzbek uses instead of a straight quote. Most of them are on the new funding screens, in the check of what a document delete would break and in the Modules page note on Simple mode.

Funding obligations are written in the reader's language. An obligation derived from a programme's terms used to carry its sentence as English prose the service had composed, so a German user reading a German programme met an English deadline on the page. Obligations now carry a translation key and its parameters beside the prose, all 43 locale files carry the new text, and a hand-written obligation still shows the words somebody typed into it. The key used to be picked by asking whether an obligation was manual, which handed a condition copied out of an award notice the generic sentence for its kind, so any client that trusted the key showed a stock sentence where a person had typed a real one. The next deadline on the application summary now carries a key of its own, and dates in the funding tables follow the reader's locale and date preference instead of one fixed pattern. On an installation upgraded from an earlier version the deadline list answered with an error while the summary above it went on counting the same obligations, so one screen showed two numbers that disagreed; those rows now read as obligations without parameters, which is what a hand-written one has always been, and the upgrade fills the missing parameters in.

Importing a cost base and repricing a region no longer hold the whole job in memory at once. A large import could run out of memory partway through: building the resource price sheet read every active work item in the region in one go, about 55,700 component breakdowns for a large region, on top of what the import itself was still holding. Both that read and the repricing pass now work in pages, so memory stays flat however large the region is. Pre-built embeddings stream into the vector store in batches instead of being decoded whole first, and an uploaded cost file is handed to the service in bounded slices.

Three answers used to come back short with nothing to say they were short: a repricing pass that reached its ceiling, a catalogue search that reached its row cap, and an import that failed halfway. Each now reports what it scanned, what the limit was and whether it was reached. A failed import also left behind the catalogue it had created on the way in, so the obvious retry of the same file was refused for a duplicate name, and that catalogue is now removed with the rest of the failed import. Two errors in the cost base endpoints stopped blaming the wrong thing: an installation without access to GitHub always takes the offline branch for building vectors and that branch failed while the message blamed the embedding model, and an installation without the optional vector store met an unexplained error instead of the plain message its sibling endpoint already gives.

Validation messages quote money in the currency of the thing being validated. An amount could reach the reader as a bare 1234567.89, or as 1E+3 once a decimal had normalised itself. Three messages that carried a bare number now go through the same formatter the rest of the module already used: a negative unit rate on a bill position, an escrow balance that has drifted, and an instalment total that does not reach the contract value. Each takes its currency from the record in hand rather than from a default, and where no currency is in scope the number is rendered on its own. A quantity is still shown bare.

The documented memory floor for a self-hosted core is now 3 GB on a dedicated server rather than 2 GB, and the install guide carries the arithmetic rather than only the conclusion. The application with every module loaded sits near 800 MB before it serves a request, PostgreSQL beside it wants another 200 to 400 MB, and the one-off import of a country cost pack peaks at roughly 1.2 GB on top of that. The sum does not fit in two gigabytes, which is what the reports of an import being killed partway through were describing. Dedicated is part of the figure and not decoration: on a shared or burstable instance the memory is only nominally yours, and the moment it matters is the one moment that cannot be retried cheaply. Two sentences in the same guide still described the deploy target as SQLite, which has not been supported since 6.6.0, and are gone.

Single sign on returned the visitor to the static site's 404 instead of completing. The callback is declared at /auth/oidc/callback in the application and the edge proxy's route matcher carried no auth segment at all, so the request never reached the application; the auth and funding segments are both in the live matcher now. Two database fixes land beside it. A downgrade that drops a unique index PostgreSQL has built a constraint on now drops the constraint first instead of failing, and three migrations that declared a native UUID column on PostgreSQL now declare the string type the models actually read, so a table one of them creates no longer disagrees with the code that reads it.

Building the frontend from source needs more memory than it did. The type check of the whole interface outgrew the 9 GB heap it was given and failed on the Linux and Windows build machines, so the build now runs with a 12 GB ceiling, and the architecture map loads its 7 MB module map as a file instead of compiling it into the type check, which took about 230 MB off the peak. Building the frontend or the container image from source now wants a machine or Docker host with about 14 GB of memory. The published container image, the Python package and the desktop installers are built for you and are not affected.

The container image builds again. The wheel's list of force-included country packs grew to forty while the Docker build context went on copying twenty-seven, and a forced include with no file behind it does not warn, it stops the install. The image therefore failed on the first missing pack while the wheel and the desktop bundle carried all forty and looked healthy. All three now name the same forty packs, and the four held back stay held back for the reasons written beside the list. Separately, an installation that reached the funding tables through the boot repair rather than through the migration chain kept one column nullable after its rows had already been filled in, which left the health endpoint reporting a schema mismatch for the rest of that installation's life. That is fixed from both ends now, and it had to be, because an ordinary installation never runs the migration chain: it moves its schema at start and then records itself as up to date, so a fix written only into the migration reaches almost nobody. Every start now fills the column and tightens it, and the model carries the default the start path needs, so a newly installed database stops arriving in that shape at all.

An approval handed to a stand-in can be decided by them. Reassigning a step moved the right to decide, told the stand-in the approval was theirs and closed the original approver out with a refusal, and then the part that decides whether a step has cleared went on asking about the original approver. So the stand-in approved, got an acknowledgement and the step did not move, while the only person who could have moved it was no longer allowed to touch it. The approval sat waiting with nothing wrong on the screen and no error anywhere. Delegations behaved the same way. A step that needs several approvals can no longer be reassigned at all: handing such a step to one stand-in used to clear it on that single approval, which is the quorum the route author asked for being spent by one person, and the refusal now says how many approvals the step wants. Escalation stopped climbing to someone who is already holding the step through a delegation, which used to spend a rung of the ladder and delay the first real authority above by a full sweep. The person named as holding a step is now worked out in one place, so the name the interface shows and the person a reminder reaches cannot disagree.

The architecture map API answers with the map. Every one of its endpoints read keys the manifest does not contain and fell through a default into an empty result, so a request for any real module answered as though the module did not exist, the connection list came back empty against the eight hundred and eighty one connections in the file, and the statistics reported none. They now read the keys the file actually carries. The page itself was never affected, because it reads the whole file through a different endpoint. One concept is removed rather than repaired: a module layer has no source anywhere in the manifest, so the layer filter, the layer group in search results and the two layer counts in the statistics are gone. This is a breaking change to a published interface and it is deliberate. Those fields could only ever answer empty, and the statistics did something worse than that, bucketing all one hundred and ninety five modules under an invented layer called unknown, which is the sort of plausible answer that keeps a fault invisible.

Five more lists say how long they are. The four registers hanging off a funding application, its draws, its accounts, its dated obligations and the costs allocated to it, each answer with a page and the count behind it instead of everything at once, and the application detail view still carries all four whole because a summary that showed the first fifty of something would be worse than no summary. The fifth is the procurement rollup of what has been ordered against each bill position, and that one had never answered at all: three faults stacked on top of each other meant every request failed before reaching the data, and the client that called it expected fields the server would never have sent. It works now, sums money as money rather than through a floating point conversion, keeps an order raised against a line that is not on the bill instead of dropping it, and reports how many positions have commitments so a buyer can tell a page from the whole picture.

A module enabled while the application is running shows up in the API documentation on every supported FastAPI version. The cached document was rebuilt only when a counter that recent FastAPI releases keep changed, and an installation upgraded without its dependencies could be on a release without that counter, so it kept serving the document built before the module was there.

The architecture map is drawn from the tree it describes again. The manifest behind it had not been regenerated since June, so it described 125 backend modules and 112 frontend features where the product now holds 195 and 188, and drew 535 connections against 881. Nothing regenerated it because nothing had ever been asked to, and no check went red in those three months, because a file in that state is still valid and still renders. A check now compares the committed manifest against a fresh scan of the tree on every push. The generator it compares against had to be made repeatable first: it resolved an ambiguous feature name by walking an unordered set, so four features were handed a different module on each run from identical inputs, and the map simply recorded whichever answer that run happened to produce.

Full Changelog: v17.7.1...v17.8.0