Compare commits

...

5 Commits

6 changed files with 94 additions and 18 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ subprojects {
testImplementation(platform("org.junit:junit-bom:5.10.0")) testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.junit.jupiter:junit-jupiter")
implementation("de.srsoftware:configuration.api:1.0.2") implementation("de.srsoftware:configuration.api:1.0.2")
implementation("de.srsoftware:tools.jdbc:2.0.4") implementation("de.srsoftware:tools.jdbc:2.0.7")
implementation("de.srsoftware:tools.http:6.0.5") implementation("de.srsoftware:tools.http:6.0.5")
implementation("de.srsoftware:tools.mime:1.1.3") implementation("de.srsoftware:tools.mime:1.1.3")
implementation("de.srsoftware:tools.logging:1.3.2") implementation("de.srsoftware:tools.logging:1.3.2")
+38 -14
View File
@@ -1,7 +1,7 @@
<script> <script>
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import { useTinyRouter } from 'svelte-tiny-router'; import { useTinyRouter } from 'svelte-tiny-router';
import { api, target } from '../../urls.svelte.js'; import { api, post, target } from '../../urls.svelte.js';
import { error, yikes } from '../../warn.svelte'; import { error, yikes } from '../../warn.svelte';
import { t } from '../../translations.svelte.js'; import { t } from '../../translations.svelte.js';
import { display } from '../../time.svelte'; import { display } from '../../time.svelte';
@@ -18,6 +18,7 @@
let notes = $state(null); let notes = $state(null);
let pages = $state(null); let pages = $state(null);
let projects = $state(null); let projects = $state(null);
let stock = $state(null);
let tasks = $state(null); let tasks = $state(null);
let times = $state(null); let times = $state(null);
@@ -38,19 +39,16 @@
window.history.replaceState(history.state, '', url); window.history.replaceState(history.state, '', url);
const data = { key : key, fulltext : fulltext }; const data = { key : key, fulltext : fulltext };
const options = {
credentials:'include', post(api('bookmark/search'),data).then(handleBookmarks);
method: 'POST', post(api('company/search '),data).then(handleCompanies);
body: JSON.stringify(data) post(api('document/search'),data).then(handleDocuments);
}; post(api('notes/search' ),data).then(handleNotes);
fetch(api('bookmark/search'),options).then(handleBookmarks); post(api('project/search' ),data).then(handleProjects);
fetch(api('company/search'),options).then(handleCompanies); post(api('task/search' ),data).then(handleTasks);
fetch(api('document/search'),options).then(handleDocuments); post(api('stock/search' ),data).then(handleStock);
fetch(api('notes/search'),options).then(handleNotes); post(api('time/search' ),data).then(handleTimes);
fetch(api('project/search'),options).then(handleProjects); post(api('wiki/search' ),data).then(handleWikiPages);
fetch(api('task/search'),options).then(handleTasks);
fetch(api('time/search'),options).then(handleTimes);
fetch(api('wiki/search'),options).then(handleWikiPages);
} }
function onclick(e){ function onclick(e){
@@ -107,6 +105,15 @@
} }
} }
async function handleStock(resp){
if (resp.ok){
const res = await resp.json();
stock = Object.keys(res).length ? res : null;
} else {
error(resp);
}
}
async function handleTasks(resp){ async function handleTasks(resp){
if (resp.ok){ if (resp.ok){
const res = await resp.json(); const res = await resp.json();
@@ -212,6 +219,23 @@
</ul> </ul>
</fieldset> </fieldset>
{/if} {/if}
{#if stock}
<fieldset>
<legend>
{t('stock')}
</legend>
<ul>
{#each Object.values(stock) as item}
<li>
<a href="/stock/{item.owner.type}/{item.owner.id}/item/{item.owner_number}" {onclick} >
{item.name} [{t('code')}: <span class="code">{item.code}</span>]
</a>
{@html item.description.rendered}
</li>
{/each}
</ul>
</fieldset>
{/if}
{#if notes} {#if notes}
<fieldset> <fieldset>
<legend> <legend>
@@ -11,7 +11,9 @@ import static de.srsoftware.umbrella.core.ModuleRegistry.companyService;
import static de.srsoftware.umbrella.core.ModuleRegistry.translator; import static de.srsoftware.umbrella.core.ModuleRegistry.translator;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException;
import static de.srsoftware.umbrella.stock.Constants.TABLE_ITEMS; import static de.srsoftware.umbrella.stock.Constants.TABLE_ITEMS;
import static java.lang.System.Logger.Level.DEBUG;
import de.srsoftware.tools.ColorLogger;
import de.srsoftware.tools.Tuple; import de.srsoftware.tools.Tuple;
import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.ModuleRegistry;
import de.srsoftware.umbrella.core.model.*; import de.srsoftware.umbrella.core.model.*;
@@ -33,6 +35,7 @@ public class ItemDb {
try { try {
var companyLocations = new HashMap<Long,Location>(); var companyLocations = new HashMap<Long,Location>();
var companyInfo = new HashMap<Long, Tuple<Company,String>>(); // map from companyId → (company, language) var companyInfo = new HashMap<Long, Tuple<Company,String>>(); // map from companyId → (company, language)
LOG.log(DEBUG,"Reading items:\nid: code / name / unit / price / tax rate");
var rs = select(ALL).from(TABLE_ITEMS).exec(db); var rs = select(ALL).from(TABLE_ITEMS).exec(db);
while (rs.next()){ while (rs.next()){
var id = rs.getLong(ID); var id = rs.getLong(ID);
@@ -44,13 +47,17 @@ public class ItemDb {
var unitPrice = rs.getLong(UNIT_PRICE); var unitPrice = rs.getLong(UNIT_PRICE);
var tax = rs.getLong(TAX); var tax = rs.getLong(TAX);
var tuple = companyInfo.get(companyId); var tuple = companyInfo.get(companyId);
LOG.log(DEBUG," - read item {0}: {1} / {2} / {3} / {4} / {5} %",id,companyId,code,name,unit,unitPrice,tax);
String lang = null; String lang = null;
Company company; Company company;
if (tuple == null){ if (tuple == null){
LOG.log(DEBUG, " loading company {0}:",companyId);
company = companyService().get(companyId); company = companyService().get(companyId);
LOG.log(DEBUG, " → {0}",company.name());
for (var member : companyService().getMembers(companyId)){ for (var member : companyService().getMembers(companyId)){
lang = member.language(); lang = member.language();
if (lang != null){ if (lang != null){
LOG.log(DEBUG, " → language = {0}",lang);
tuple = Tuple.of(company,lang); tuple = Tuple.of(company,lang);
companyInfo.put(companyId,tuple); companyInfo.put(companyId,tuple);
break; break;
@@ -59,12 +66,15 @@ public class ItemDb {
} else { } else {
company = tuple.a; company = tuple.a;
lang = tuple.b; lang = tuple.b;
LOG.log(DEBUG, " using company: {0} ({1})",company.name(),lang);
} }
var location = companyLocations.get(companyId); var location = companyLocations.get(companyId);
if (location == null) { if (location == null) {
location = stockDb.save(new DbLocation(0,company,null,"virtual items",null)); location = stockDb.save(new DbLocation(0,company,null,"virtual items",null));
companyLocations.put(companyId,location); companyLocations.put(companyId,location);
} }
LOG.log(DEBUG, " using location: {0}",location.resolve().name());
var stockItem = new Item(0,company,0,location,code,name,description); var stockItem = new Item(0,company,0,location,code,name,description);
var props = stockItem.properties(); var props = stockItem.properties();
var keyUnitPrice = translator().translate(lang,UNIT_PRICE); var keyUnitPrice = translator().translate(lang,UNIT_PRICE);
@@ -75,12 +85,12 @@ public class ItemDb {
props.add(new Property(0,keyUnit,unit,null)); props.add(new Property(0,keyUnit,unit,null));
props.add(new Property(0,keyTax,tax,"%")); props.add(new Property(0,keyTax,tax,"%"));
props.add(new Property(0,keyLegacyId,id,null)); props.add(new Property(0,keyLegacyId,id,null));
LOG.log(DEBUG," saving item {0}:",stockItem);
stockDb.save(stockItem); stockDb.save(stockItem);
} }
rs.close(); rs.close();
} catch (SQLException e) { } catch (SQLException e) {
throw databaseException("Failed to migrate items from itemDB to stockDB!"); throw databaseException("Failed to migrate items from itemDB to stockDB!");
} }
LOG.log(System.Logger.Level.WARNING,"migrateTo({0}) not implemented", stockDb);
} }
} }
@@ -3,8 +3,8 @@ package de.srsoftware.umbrella.stock;
import static de.srsoftware.tools.Optionals.is0; import static de.srsoftware.tools.Optionals.is0;
import static de.srsoftware.tools.Optionals.nullIfEmpty; import static de.srsoftware.tools.Optionals.nullIfEmpty;
import static de.srsoftware.tools.jdbc.Condition.equal; import static de.srsoftware.tools.jdbc.Condition.*;
import static de.srsoftware.tools.jdbc.Condition.isNull; import static de.srsoftware.tools.jdbc.Condition.like;
import static de.srsoftware.tools.jdbc.Query.*; import static de.srsoftware.tools.jdbc.Query.*;
import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL;
import static de.srsoftware.umbrella.core.Constants.*; import static de.srsoftware.umbrella.core.Constants.*;
@@ -17,6 +17,7 @@ import static java.text.MessageFormat.format;
import de.srsoftware.tools.jdbc.Query; import de.srsoftware.tools.jdbc.Query;
import de.srsoftware.umbrella.core.BaseDb; import de.srsoftware.umbrella.core.BaseDb;
import de.srsoftware.umbrella.core.api.Owner; import de.srsoftware.umbrella.core.api.Owner;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException;
import de.srsoftware.umbrella.core.model.*; import de.srsoftware.umbrella.core.model.*;
import de.srsoftware.umbrella.core.model.Location; import de.srsoftware.umbrella.core.model.Location;
import java.sql.Connection; import java.sql.Connection;
@@ -189,6 +190,30 @@ public class SqliteDb extends BaseDb implements StockDb {
} }
} }
@Override
public Map<Long, Item> find(Collection<Owner> owners, Collection<String> keys, boolean fulltext) {
try {
var items = new HashMap<Long,Item>();
var ownerCodes = owners.stream().map(Owner::dbCode).toArray();
var query = select(ALL).from(TABLE_ITEMS).where(OWNER, in(ownerCodes));
if (fulltext) {
query.leftJoin(ID,TABLE_ITEM_PROPERTIES,ITEM_ID);
for (var key : keys) query.where(format("CONCAT({0},\" \",{1},\" \",{2})",NAME,DESCRIPTION,VALUE),like("%"+key+"%"));
} else {
for (var key : keys) query.where(NAME,like("%"+key+"%"));
}
var rs = query.exec(db);
while (rs.next()){
var item = Item.of(rs);
items.put(item.id(),item);
}
rs.close();
return items;
} catch (SQLException e) {
throw new UmbrellaException("Failed to load items from database");
}
}
@Override @Override
public Collection<DbLocation> listChildLocations(long parentId) { public Collection<DbLocation> listChildLocations(long parentId) {
try { try {
@@ -6,10 +6,12 @@ import de.srsoftware.umbrella.core.model.*;
import de.srsoftware.umbrella.core.model.Location; import de.srsoftware.umbrella.core.model.Location;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Set;
public interface StockDb { public interface StockDb {
Property addNewProperty(long itemId, String name, Object value, String unit); Property addNewProperty(long itemId, String name, Object value, String unit);
Location delete(DbLocation location); Location delete(DbLocation location);
Map<Long, Item> find(Collection<Owner> owners, Collection<String> keys, boolean fulltext);
Collection<DbLocation> listChildLocations(long parentId); Collection<DbLocation> listChildLocations(long parentId);
Collection<DbLocation> listCompanyLocations(Company company); Collection<DbLocation> listCompanyLocations(Company company);
Collection<Item> listItemsAt(Location location); Collection<Item> listItemsAt(Location location);
@@ -24,5 +26,6 @@ public interface StockDb {
Map<String,Object> pathToLocation(Location location); Map<String,Object> pathToLocation(Location location);
DbLocation save(DbLocation location); DbLocation save(DbLocation location);
Item save(Item item); Item save(Item item);
Property setProperty(long itemId, long existingPropId, Object value); Property setProperty(long itemId, long existingPropId, Object value);
} }
@@ -9,6 +9,8 @@ import static de.srsoftware.umbrella.core.Field.ITEM;
import static de.srsoftware.umbrella.core.ModuleRegistry.companyService; import static de.srsoftware.umbrella.core.ModuleRegistry.companyService;
import static de.srsoftware.umbrella.core.ModuleRegistry.userService; import static de.srsoftware.umbrella.core.ModuleRegistry.userService;
import static de.srsoftware.umbrella.core.Paths.LIST; import static de.srsoftware.umbrella.core.Paths.LIST;
import static de.srsoftware.umbrella.core.Paths.SEARCH;
import static de.srsoftware.umbrella.core.Util.mapValues;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*;
import static de.srsoftware.umbrella.stock.Constants.*; import static de.srsoftware.umbrella.stock.Constants.*;
import static java.lang.System.Logger.Level.WARNING; import static java.lang.System.Logger.Level.WARNING;
@@ -174,6 +176,7 @@ public class StockModule extends BaseHandler implements StockService {
case LIST -> postItemList(user.get(), path, ex); case LIST -> postItemList(user.get(), path, ex);
case LOCATION -> postLocation(user.get(),ex); case LOCATION -> postLocation(user.get(),ex);
case PROPERTY -> postProperty(user.get(),ex); case PROPERTY -> postProperty(user.get(),ex);
case SEARCH -> postSearch(user.get(),ex);
case null, default -> super.doPost(path,ex); case null, default -> super.doPost(path,ex);
}; };
} catch (UmbrellaException e){ } catch (UmbrellaException e){
@@ -389,6 +392,17 @@ public class StockModule extends BaseHandler implements StockService {
return sendContent(ex,property); return sendContent(ex,property);
} }
private boolean postSearch(UmbrellaUser user, HttpExchange ex) throws IOException {
var json = json(ex);
if (!(json.has(KEY) && json.get(KEY) instanceof String key)) throw missingFieldException(KEY);
var keys = Arrays.asList(key.split(" "));
var fulltext = json.has(FULLTEXT) && json.get(FULLTEXT) instanceof Boolean val && val;
Set<Owner> owners = new HashSet<>(companyService().listCompaniesOf(user).values());
owners.add(user);
var items = stockDb.find(owners,keys,fulltext);
return sendContent(ex,mapValues(items));
}
@Override @Override
public Collection<Object> redefineMe(long company_id) { public Collection<Object> redefineMe(long company_id) {
// TODO // TODO