View our other brands
// Configuration object for centralized settings
const TabbedNavConfig = {
  DESKTOP_BREAKPOINT: 1201,
  DEBOUNCE_DELAY: 250,
  OPEN_FIRST_TAB: true,
  CLOSE_TAB_ON_MOUSEOUT: false
};

// Custom config for tabbed hero containers
const tabbedHeroConfig = {
  ...TabbedNavConfig,
  OPEN_FIRST_TAB: false,
  CLOSE_TAB_ON_MOUSEOUT: true
};

// Function to determine the appropriate config based on container context
function getConfigForContainer(container) {
  // Check if container has closest ancestor with [data-tabbed-hero="true"]
  const heroAncestor = container.getAttribute('data-tabbed-hero') === 'true'   ? container : container.closest('[data-tabbed-dropdown="true"]');
  
  if (heroAncestor) {
    return tabbedHeroConfig;
  }
  
  // Check for specific data-instance attributes (if you still want to support them)
  // const instance = container.getAttribute('data-instance');
  // if (instance === 'two') {
  //   // You can still have specific instance configs if needed
  //   return {
  //     ...TabbedNavConfig,
  //     OPEN_FIRST_TAB: false,
  //     CLOSE_TAB_ON_MOUSEOUT: true
  //   };
  // }
  
  // Default config
  return TabbedNavConfig;
}

class TabbedNavigation {
  constructor(container, config = TabbedNavConfig) {
    this.container = container;
    this.config = config;
    this.activeItem = null;
    this.isDesktop = window.innerWidth >= this.config.DESKTOP_BREAKPOINT;
    this.hasMouseListeners = false;
    this.megamenuParent = null;
    this.megamenuObserver = null;
    
    // Initialize bound handlers - these will be reused across reinitializations
    this.mouseEnterHandler = this.handleMouseEnter.bind(this);
    this.mouseLeaveHandler = this.handleMouseLeave.bind(this);
    this.resizeHandler = this.debounce(this.handleResize.bind(this), this.config.DEBOUNCE_DELAY);
    this.handleMegamenuToggle = this.handleMegamenuToggle.bind(this);
    
    this.init();
  }

  init() {
    // Cache DOM elements fresh each time
    this.navItems = [...this.container.querySelectorAll('.dwc-tabbed-nav-list__li')];
    this.elementsCache = new Map();
    
    this.cacheElements();
    this.setupAccessibility();
    this.setupMegamenuObserver();
    this.bindEvents();

    // Set initial state
    this.updateLayout();
    this.setBackText()

    // Show first item on page load only if desktop, not nested, and OPEN_FIRST_TAB is true
    if (this.navItems.length > 0 && this.isDesktop && this.config.OPEN_FIRST_TAB) {
      setTimeout(() => {
        const firstItem = this.navItems[0];

        // Count how many .dwc-tabbed-nav-container ancestors this.container has
        let count = 0;
        let current = this.container.parentElement;
        while (current) {
          if (current.classList?.contains('dwc-tabbed-nav-container')) {
            count++;
          }
          current = current.parentElement;
        }

        const isNested = count >= 1;

        if (!isNested && this.elementsCache.has(firstItem)) {
          this.showTab(firstItem);
        }
      }, 0);
    }
  }

  // Setup observer for megamenu parent
  setupMegamenuObserver() {
    // Find the .brx-has-megamenu parent
    this.megamenuParent = this.container.closest('.brx-has-megamenu');
    
    if (this.megamenuParent) {
      // Set initial inert state
      this.updateInertState();
      
      // Create a MutationObserver to watch for class changes
      this.megamenuObserver = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
          if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
            this.updateInertState();
          }
        });
      });
      
      // Start observing the megamenu parent for class changes
      this.megamenuObserver.observe(this.megamenuParent, {
        attributes: true,
        attributeFilter: ['class']
      });
    }
  }

  // Update inert state based on megamenu open/closed state
  updateInertState() {
    if (!this.megamenuParent) return;
    
    const isOpen = this.megamenuParent.classList.contains('open');
    
    if (isOpen) {
      this.container.removeAttribute('inert');
    } else {
      this.container.setAttribute('inert', '');
    }
  }

  // Handle megamenu toggle (for manual triggering if needed)
  handleMegamenuToggle() {
    this.updateInertState();
  }

  // Method to clean up event listeners and reset state
  cleanup() {
    // Remove all event listeners
    this.container.removeEventListener('click', this.handleClick);
    this.container.removeEventListener('keydown', this.handleKeydown);
    this.container.removeEventListener('focus', this.handleFocus);
    
    // Remove mouse listeners if they exist
    if (this.hasMouseListeners) {
      this.container.removeEventListener('mouseenter', this.mouseEnterHandler, true);
      this.container.removeEventListener('mouseleave', this.mouseLeaveHandler, true);
    }
    
    // Remove resize listener
    window.removeEventListener('resize', this.resizeHandler);
    
    // Clean up megamenu observer
    if (this.megamenuObserver) {
      this.megamenuObserver.disconnect();
      this.megamenuObserver = null;
    }
    
    // Reset state
    this.activeItem = null;
    this.hasMouseListeners = false;
    this.megamenuParent = null;
    
    // Clear caches
    if (this.elementsCache) {
      this.elementsCache.clear();
    }
    this.navItems = [];
    
    // Reset all tab states and accessibility attributes
    const allNavItems = this.container.querySelectorAll('.dwc-tabbed-nav-list__li');
    allNavItems.forEach(item => {
      const button = item.querySelector('.dwc-tabbed-nav-list__li__button') || 
                    item.querySelector('.dwc-tabbed-nav-list__li__btn-txt');
      const content = item.querySelector('.dwc-tabbed-nav-list__li__content');
      
      // Remove classes
      item.classList.remove('active');
      
      // Reset button attributes
      if (button) {
        button.removeAttribute('role');
        button.removeAttribute('aria-controls');
        button.removeAttribute('aria-expanded');
        button.removeAttribute('aria-selected');
        button.removeAttribute('tabindex');
        button.removeAttribute('id');
      }
      
      // Reset content attributes
      if (content) {
        content.removeAttribute('role');
        content.removeAttribute('aria-labelledby');
        content.removeAttribute('id');
        content.removeAttribute('aria-hidden');
        content.style.maxHeight = '';
      }
      
      // Remove data attributes
      item.removeAttribute('data-tab-index');
    });
    
    // Reset nav list role
    const navList = this.container.querySelector('.dwc-tabbed-nav-list');
    if (navList) {
      navList.removeAttribute('role');
    }
    
    // Remove inert attribute
    this.container.removeAttribute('inert');
  }

  // Public method to reinitialize the navigation
  reinitialize() {
    this.cleanup();
    this.isDesktop = window.innerWidth >= this.config.DESKTOP_BREAKPOINT; // Use config breakpoint
    this.init();
  }

  // Cache DOM elements to avoid repeated queries
  cacheElements() {
    const uniqueIdBase = `tab-${Date.now()}`;
    
    this.navItems.forEach((item, index) => {
      const button = item.querySelector('.dwc-tabbed-nav-list__li__button') || 
                    item.querySelector('.dwc-tabbed-nav-list__li__btn-txt');
      const content = item.querySelector('.dwc-tabbed-nav-list__li__content');
      
      this.elementsCache.set(item, {
        button,
        content,
        uniqueId: `${uniqueIdBase}-${index}`,
        index
      });
    });
  }

  setupAccessibility() {
    const navList = this.container.querySelector('.dwc-tabbed-nav-list');
    if (!navList) return;
    
    navList.setAttribute('role', 'tablist');
    
    for (const [item, cache] of this.elementsCache) {
      const { button, content, uniqueId, index } = cache;
      
      if (button) {
        button.setAttribute('role', 'tab');
        button.setAttribute('aria-controls', `${uniqueId}-panel`);
        button.setAttribute('aria-expanded', 'false');
        button.setAttribute('aria-selected', 'false');
        button.setAttribute('tabindex', '0');
        button.setAttribute('id', `${uniqueId}-tab`);
      }
      
      if (content) {
        content.setAttribute('role', 'tabpanel');
        content.setAttribute('aria-labelledby', `${uniqueId}-tab`);
        content.setAttribute('id', `${uniqueId}-panel`);
        content.setAttribute('aria-hidden', 'true');
      }
      
      item.setAttribute('data-tab-index', index);
    }
  }

  bindEvents() {
    // Store bound handlers for cleanup
    this.handleClick = this.handleClick.bind(this);
    this.handleKeydown = this.handleKeydown.bind(this);
    this.handleFocus = this.handleFocus.bind(this);
    
    // Single event delegation for better performance
    this.container.addEventListener('click', this.handleClick);
    this.container.addEventListener('keydown', this.handleKeydown);
    this.container.addEventListener('focus', this.handleFocus, true);
    
    // Passive resize listener for better performance
    window.addEventListener('resize', this.resizeHandler, { passive: true });
    
    this.updateMouseListeners();
  }

  updateMouseListeners() {
    if (this.isDesktop && !this.hasMouseListeners) {
      this.container.addEventListener('mouseenter', this.mouseEnterHandler, true);
      this.container.addEventListener('mouseleave', this.mouseLeaveHandler, true);
      this.hasMouseListeners = true;
    } else if (!this.isDesktop && this.hasMouseListeners) {
      this.container.removeEventListener('mouseenter', this.mouseEnterHandler, true);
      this.container.removeEventListener('mouseleave', this.mouseLeaveHandler, true);
      this.hasMouseListeners = false;
    }
  }

  // Helper method to get sibling nav items (nav items in the same container level)
  getSiblingNavItems(navItem) {
    // Find the direct parent container of this nav item
    const navItemContainer = navItem.closest('.dwc-tabbed-nav-list');
    if (!navItemContainer) return [];
    
    // Get all nav items that are direct children of the same container
    const siblingItems = [...navItemContainer.querySelectorAll('.dwc-tabbed-nav-list__li')];
    
    // Filter to only include items that are siblings (not the navItem itself)
    // and check if they belong to any TabbedNavigation instance
    return siblingItems.filter(item => item !== navItem);
  }

  handleFocus(e) {
    if (!this.isDesktop) return;

    const button = e.target.closest('[role="tab"]');
    if (!button) return;
    
    const navItem = button.closest('.dwc-tabbed-nav-list__li');
    if (!navItem || !this.elementsCache.has(navItem)) return;
    
    this.handleTabInteraction(navItem, 'focus');
  }

  handleClick(e) {
    const navItem = e.target.closest('.dwc-tabbed-nav-list__li');
    if (!navItem || !this.elementsCache.has(navItem)) return;
    
    const button = e.target.closest('.dwc-tabbed-nav-list__li__button, .dwc-tabbed-nav-list__li__btn-txt');
    if (!button) return;
    
    e.preventDefault();
    this.handleTabInteraction(navItem, 'click');
  }

  handleKeydown(e) {
    const button = e.target.closest('[role="tab"]');
    if (!button) return;
    
    const navItem = button.closest('.dwc-tabbed-nav-list__li');
    if (!navItem || !this.elementsCache.has(navItem)) return;
    
    const { index: currentIndex } = this.elementsCache.get(navItem);
    
    switch (e.key) {
      case 'Enter':
      case ' ':
        e.preventDefault();
        this.handleTabInteraction(navItem, 'keyboard');
        break;
      case 'ArrowRight':
      case 'ArrowDown':
        e.preventDefault();
        this.focusNextTab(currentIndex);
        break;
      case 'ArrowLeft':
      case 'ArrowUp':
        e.preventDefault();
        this.focusPrevTab(currentIndex);
        break;
      case 'Home':
        e.preventDefault();
        this.focusTab(0);
        break;
      case 'End':
        e.preventDefault();
        this.focusTab(this.navItems.length - 1);
        break;
    }
  }
  
  handleMouseEnter(e) {
    if (!this.isDesktop) return;
    
    const navItem = e.target.closest('.dwc-tabbed-nav-list__li') || 
                   (e.target.matches('.dwc-tabbed-nav-list__li') ? e.target : null);
    if (!navItem || !this.elementsCache.has(navItem)) return;
    
    this.handleTabInteraction(navItem, 'hover');
  }

  handleMouseLeave(e) {
    if (!this.isDesktop) return;
    
    // Only close tabs if CLOSE_TAB_ON_MOUSEOUT is enabled
    if (this.config.CLOSE_TAB_ON_MOUSEOUT) {
      // Check if mouse is leaving the entire container
      const rect = this.container.getBoundingClientRect();
      const { clientX, clientY } = e;
      
      // Check if mouse position is outside the container bounds
      const isOutsideContainer = (
        clientX < rect.left || 
        clientX > rect.right || 
        clientY < rect.top || 
        clientY > rect.bottom
      );
      
      if (isOutsideContainer) {
        this.hideAllTabs();
      }
    }
    // If CLOSE_TAB_ON_MOUSEOUT is false, keep content visible (original behavior)
  }

  // Unified method to handle all tab interactions with nesting support
  handleTabInteraction(navItem, interactionType) {
    if (!this.elementsCache.has(navItem)) return;
    
    if (this.isDesktop) {
      // Simply hide siblings and show the target - works for all scenarios
      this.hideSiblingTabs(navItem);
      this.showTab(navItem);
    } else {
      // Mobile accordion behavior
      if (interactionType === 'click' || interactionType === 'keyboard') {
        this.toggleTabMobile(navItem);
      }
    }
  }

  // Hide only sibling tabs (same container level)
  hideSiblingTabs(navItem) {
   // console.log('Nav Item: ', navItem)
    const siblings = this.getSiblingNavItems(navItem);
    
    siblings.forEach(sibling => {
      
         //console.log('sibling: ', sibling)
      // Only hide siblings that have the active class to avoid affecting other instances
      if (sibling.classList.contains('active')) {
        this.hideSiblingTab(sibling);
      }
    });
  }

  // Hide a sibling tab (used when we know it's a sibling from another instance)
  hideSiblingTab(navItem) {
    const button = navItem.querySelector('.dwc-tabbed-nav-list__li__button') || 
                  navItem.querySelector('.dwc-tabbed-nav-list__li__btn-txt');
    const content = navItem.querySelector('.dwc-tabbed-nav-list__li__content');
    
    if (content && button) {
      content.setAttribute('aria-hidden', 'true');
      button.setAttribute('aria-expanded', 'false');
      button.setAttribute('aria-selected', 'false');
      navItem.classList.remove('active');
      
      // Also handle mobile accordion collapse
      if (!this.isDesktop) {
        content.style.maxHeight = '0';
      }
    }
  }

  // Mobile-specific toggle behavior with proper nesting support
  toggleTabMobile(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    const isActive = navItem.classList.contains('active');
    
    if (isActive) {
      this.collapseTab(navItem);
    } else {
      // Close sibling tabs and open this one
      this.hideSiblingTabs(navItem);
      this.expandTab(navItem);
    }
  }

  toggleTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    if (this.isDesktop) {
      this.handleTabInteraction(navItem, 'click');
    } else {
      this.toggleTabMobile(navItem);
    }
  }

  showTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    const { button, content } = this.elementsCache.get(navItem);
    
    if (content && button) {
      content.setAttribute('aria-hidden', 'false');
      button.setAttribute('aria-expanded', 'true');
      button.setAttribute('aria-selected', 'true');
      navItem.classList.add('active');
    }
    
    this.setActiveTab(navItem);
  }

  hideTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    const { button, content } = this.elementsCache.get(navItem);
    
    if (content && button) {
      content.setAttribute('aria-hidden', 'true');
      button.setAttribute('aria-expanded', 'false');
      button.setAttribute('aria-selected', 'false');
      navItem.classList.remove('active');
    }
    
    // Only clear activeItem if it belongs to THIS navigation instance
    // and it's the exact item being hidden
    if (this.activeItem === navItem && this.navItems.includes(navItem)) {
      this.activeItem = null;
    }
  }

  expandTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    // Close sibling tabs
    this.hideSiblingTabs(navItem);
    
    this.showTab(navItem);
    
    // Smooth height animation
    const { content } = this.elementsCache.get(navItem);
    if (content) {
      const height = content.scrollHeight;
      content.style.maxHeight = `${height}px`;
    }
  }

  collapseTab(navItem) {
    if (!this.elementsCache.has(navItem)) return;
    
    this.hideTab(navItem);
    
    const { content } = this.elementsCache.get(navItem);
    if (content) {
      content.style.maxHeight = '0';
    }
  }

  hideAllTabs() {
    this.navItems.forEach(item => this.hideTab(item));
  }

  setActiveTab(navItem) {
    // Only update aria-selected for tabs in THIS navigation instance
    this.navItems.forEach(item => {
      const cache = this.elementsCache.get(item);
      if (cache?.button) {
        cache.button.setAttribute('aria-selected', item === navItem ? 'true' : 'false');
      }
    });
    
    // Only set activeItem if this navItem belongs to THIS navigation instance
    if (this.navItems.includes(navItem)) {
      this.activeItem = navItem;
    }
  }

  focusTab(index) {
    if (index >= 0 && index < this.navItems.length) {
      const cache = this.elementsCache.get(this.navItems[index]);
      if (cache?.button) {
        cache.button.focus();
      }
    }
  }

  focusNextTab(currentIndex) {
    const nextIndex = (currentIndex + 1) % this.navItems.length;
    this.focusTab(nextIndex);
  }

  focusPrevTab(currentIndex) {
    const prevIndex = currentIndex === 0 ? this.navItems.length - 1 : currentIndex - 1;
    this.focusTab(prevIndex);
  }

  handleResize() {
    const wasDesktop = this.isDesktop;
    this.isDesktop = window.innerWidth >= this.config.DESKTOP_BREAKPOINT; // Use config breakpoint
    
    if (wasDesktop !== this.isDesktop) {
      this.updateMouseListeners();
      this.updateLayout();
    }
  }

  updateLayout() {
    const currentlyActive = this.activeItem;

    // Reset all states
    this.hideAllTabs();

    // Clear inline styles
    this.navItems.forEach(item => {
      const cache = this.elementsCache.get(item);
      if (cache?.content) {
        cache.content.style.maxHeight = '';
      }
    });

    if (this.isDesktop) {
      // Determine if this tab instance is nested
      let count = 0;
      let current = this.container.parentElement;
      while (current) {
        if (current.classList?.contains('dwc-tabbed-nav-container')) {
          count++;
        }
        current = current.parentElement;
      }
      const isNested = count >= 1;

      if (!isNested && this.config.OPEN_FIRST_TAB) {
        if (currentlyActive) {
          this.showTab(currentlyActive);
        } else if (this.navItems.length > 0) {
          this.showTab(this.navItems[0]);
        }
      }
    }
  }

  setBackText(){
    let tabElement = document.querySelector('.dwc-tabbed-nav-container');
    let tabBackText = tabElement.getAttribute('data-back-text')
    document.querySelectorAll('.dwc-tabbed-nav-list__li__button').forEach(tabBtn => {
        const textContent = tabBtn.textContent.trim();
        const tabIcon = tabBtn.querySelector('.dwc-tabbed-nav-list__li__arrow-icon');
        if (tabIcon) {
            tabIcon.setAttribute('data-text', textContent);
            tabIcon.setAttribute('data-back-text', tabBackText);
        }
    });
  }

  // Simple debounce for resize
  debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }

  // Public method to destroy the instance completely
  destroy() {
    this.cleanup();
    // Additional cleanup can be added here if needed
  }

  // Public method to manually update inert state (if needed)
  updateInert() {
    this.updateInertState();
  }
}

// Simplified initialization on DOM ready
document.addEventListener('DOMContentLoaded', function() {
  const tabbedNavContainers = document.querySelectorAll('.dwc-tabbed-nav-container');
  
  tabbedNavContainers.forEach(container => {
    // Skip if this container is nested inside another dwc-tabbed-nav-container
    const parentNavContainer = container.parentElement.closest('.dwc-tabbed-nav-container');
    if (parentNavContainer) {
      return; // Skip nested containers
    }
    
    // Get the appropriate config for this container
    const config = getConfigForContainer(container);
    
    // Initialize with the determined config
    new TabbedNavigation(container, config);
  });
});

// For dynamic content - Updated version
window.initTabbedNav = function(container, customConfig = null) {
  if (container && container.classList.contains('dwc-tabbed-nav-container')) {
    // Check if this is a nested container
    const parentNavContainer = container.parentElement.closest('.dwc-tabbed-nav-container');
    if (parentNavContainer) {
      return null; // Don't initialize nested containers
    }
    
    // Use custom config if provided, otherwise determine config automatically
    const config = customConfig || getConfigForContainer(container);
    return new TabbedNavigation(container, config);
  }
  return null;
};

/*
// TO REINITIALIZE USE THIS
// Create an instance of TabbedNavigation
const tabbedNav = new TabbedNavigation(document.querySelector('.dwc-tabbed-nav-container'));

// Call reinitialize on the instance
tabbedNav.reinitialize();

// TO USE WITH CUSTOM CONFIG
const customConfig = {
  DESKTOP_BREAKPOINT: 768,
  DEBOUNCE_DELAY: 300,
  OPEN_FIRST_TAB: false,  // This will prevent first tab from opening automatically
  CLOSE_TAB_ON_MOUSEOUT: true  // This will close tabs when mouse leaves container
};
const tabbedNavCustom = new TabbedNavigation(document.querySelector('.dwc-tabbed-nav-container'), customConfig);

// TO UPDATE ON URL CHANGE
function onUrlChange() {
    setTimeout(function () {
const tabbedNav = new TabbedNavigation(document.querySelector('.dwc-tabbed-nav-container'));
tabbedNav.reinitialize();

    }, 1500); // Delay execution by 2 seconds
}

// Listen for popstate event (history navigation)
window.addEventListener('popstate', onUrlChange);

// Listen for hashchange event (URL hash change)
window.addEventListener('hashchange', onUrlChange);

// Handle pushState and replaceState
(function (history) {
    const pushState = history.pushState;
    const replaceState = history.replaceState;

    history.pushState = function (state) {
        if (typeof history.onpushstate == "function") {
            history.onpushstate({ state: state });
        }
        const result = pushState.apply(history, arguments);
        onUrlChange(); // Call the function when pushState is used
        return result;
    };

    history.replaceState = function (state) {
        if (typeof history.onreplacestate == "function") {
            history.onreplacestate({ state: state });
        }
        const result = replaceState.apply(history, arguments);
        onUrlChange(); // Call the function when replaceState is used
        return result;
    };
})(window.history);

*/

console.log('%c<Jamie Sheehan Web Design Mega Menu Pro Tabbed Navigation v1.0>', 'color: #b388eb');

European Refineries Are Running Out of Time to Fix Their Oil Accounting 

July 2, 2026

In this post...

All Articles

European refineries are not operating in the same environment they were three years ago. More than three months into the Strait of Hormuz disruption, global oil supply has tightened significantly. Inventory buffers across multiple countries are eroding, with some approaching critical levels [S&P Global Ratings, June 2026]. 

When supply is this tight, every barrel matters more, and so does knowing exactly where it is, what it costs, and how it is accounted for. That is precisely the data most refineries struggle to trust. 

The demand for accurate, structured, auditable operational data is arriving from two directions at once: operationally, as disruption makes inventory accuracy more commercially critical; and from a tightening set of EU regulations that increasingly expect the same. European refineries face a more demanding regulatory, economic, and technical environment than almost any other region, and the data systems behind their operations have not always kept pace. 

The Problem Most Refineries Are Still Living With

Oil accounting is where operational complexity and commercial accountability meet. Every barrel received, processed, transferred, and dispatched has to be captured accurately, reconciled against physical measurements, and available when required.

Many European refineries have been operating for more than half a century, navigating some of the most demanding compliance frameworks in the world. The challenge is not capability — it is what becomes possible when data that sits across siloed systems is brought into one place. In a tightening margin environment, the cost of fragmented, hard-to-integrate data is no longer just an operational inconvenience. It shows commercially and in the increasing demands of the regulatory frameworks.

Why Now – The Shift Making This Urgent

The regulatory ask has not changed in its existence but in what it now requires. Integration into centralised electronic systems, EMCS, NCTS, and others, has replaced periodic reporting. Data must be available at the point of the movement, not assembled afterward.

The operational environment is making the same demand from a different direction. Supply routes are shifting, schedules are under pressure, and decisions are being made faster with less margin for error. A stock position that cannot be trusted, or a mass balance that takes days to close, is now a commercial risk. Both pressures are live simultaneously.

The Rules Driving the Move

A series of EU updates affect how refineries manage and report operational data. Separate frameworks, different speeds, but broadly aligned in what they demand from the data behind them.

EMCS 4.0 — Excise movements must now be declared electronically before or during movement, not reconstructed afterward. It is live across more than 200,000 registered operators [European Commission, February 2024]. The record has to exist at the point of the movement, not be pulled from emails days later.

Compulsory Stock Obligations – Member states must hold oil stocks equivalent to at least 90 days of average daily net imports or 61 days of average daily inland consumption, whichever is greater [Council Directive 2009/119/EC]. A position assembled from spreadsheets across multiple sites does not meet that. Some refineries lean on solutions like OAS to track inventory across every terminal, depot, and storage location in one place, so the position is always current, not reconstructed for a deadline.

How OAS Supports Refineries on Operational and Regulatory Fronts

The two pressures share one root, and so does the fix.

The data already exists. Every movement, every transfer, every custody change happens and leaves a record, somewhere. The problem is where. Spreadsheets that only one person maintains. Email chains that do not survive a handover. Meter readings logged hours after the fact, if at all. By the time the figures are assembled, the moment to act on them has passed, and when a regulatory deadline arrives, the same chase starts again from scratch.

OAS captures that data at the point it is created, to API and EI standards, in one place. The mass balance is not something you close at month-end. It is the current output of operations you can read today. And because the operational record already exists in structured form, the regulatory reporting draws straight from it, the same data, the same record, already there when the deadline arrives.

Across Europe, OAS users communicate with EMCS to file the required reports. In the UK, HMRC lists OAS as a commercial EMCS software supplier and accepts forms and returns filed through it. For compulsory stock obligations, OAS produces reports that evidence the position. More than 30 refineries across Europe, North America, and Asia-Pacific have already run on it.

OAS users typically achieve full return on investment within twelve months — based on customer-reported outcomes.

The Longer You Wait, The More It Costs

What this article describes is not a forecast. It is the environment European refineries are operating in today. Supply is tighter; regulatory frameworks are stricter, and outdated, fragmented oil accounting systems can no longer keep pace with either. Disconnected systems have a ceiling. Every refinery and terminal will eventually move to a single system of record — today or tomorrow. The only variable is how much it costs to wait. The direction is clear. The only question is when.

If you are struggling to keep up with the current regulatory demands from a fragmented oil accounting environment, speak to the Maron team to see how OAS handles it.

Get the Guide

Speak to the Team

Unable to read published form data

Additional Benefits

  • Accelerated financial results for real-time decision making
  • Automated hydrocarbon pricing based on quality inspection results for accountants
  • Incorporating historical oil losses per location into transactions to reduce/eliminate oil loss claims
  • Identify differences between test method nominated versus method performed by inspection companies
  • Communicate real-time test results as they are available for prompt trading opportunities
  • Inspection cost savings from use of Navarik Inspection Price Estimator
  • All data centralised with the ability to extract key information for data analytics